Dockerfile: mapping volume to /config instead of /app/config

I have a dotnet core application already published in a folder. I navigate to this folder and create image using this Dockerfile

FROM mcr.microsft.com/dotnet/aspnet:6.0
WORKDIR /app
COPY . .
VOLUME /config
VOLUME /media
RUN ["chmod", "+x", "./init.sh"]
ENTRYPOINT ["sh", "./init.sh"]

When I create new container I have to map local folder to folder inside the container using /app/config

/mnt/cache/appdata/myapp : /app/config

I think I’m missing something in the Dockerfile. I know /app directory was created and everything was copied over to this folder. But I did not see any other application that would map local folder to workdir folder. I think it would confuse most of the users. Every other container uses mapping with /config

When I use mapping /config application will not even start

Note: image and container are created on Unraid server

Please advise. Thank you

So what’s the question?

A volume will always eclipse the original content of the path it is mapped to. If you use a host path (like the one in your mapping), it is actually a bind, and not a volume. Only real volumes that are empty allow to copy existing content from the container path back to the volume. In my opinion it is a terrible choice to rely on it.

Make sure your application actually uses the files from /config. At least your Dockerfile shows that you intend this container path to be used as target for a volume mapping.

Though, what exactly are you looking for?

Yes, application uses both folders config and media and they need to be persisted outside of the container. I appologize if I used incorrect terms. Local folder needs to be bind to config folder inside container. It only works when I use /app/config. This doesn’t seem to be a standard way of doing things. All the containers I came across use binding /localFolder : /config instead of /localFolder: /app/config

Clearly entire content of the app is inside working directory /app
How do I move the files one level up?
Do I need working directory?

I have no idea how you need to configure the application started from init.sh to actually use the /config path for the configuration. This is something you should know.

I am not sure what you mean by “move the files one level up”. The host folder already has the files, so instead of mapping it to /app/config, you just need to map it into /config and the files will be there.

The working directory sets the current directory. Without seeing your init.sh file, I can not say whether you need WORKDIR, but why would you want to get rid of it?

I have to say your condescending tone is bit off putting. You have enough information to understand what the problem is. You do not need to see init.sh content either. It is irrelevant what it does. I told you already that binding to /config does not work because this path inside container does not exist. The correct part is /app/config and binding works only with /app/config.

It was not my intention.

Honestly, I don’t.

Though, you do understand that I asked this question in regards of your “Do I need Workdir” question.

This is not how volumes work… If you map a volume or a bind in a container path it WILL be mapped there, if the path didn’t exist before, it will be created.

As we seem to not get on the same page, I leave this topic for someone else to carry on.

I think @meyay gave you the right advice, but it seems you are still confused about volumes, bind mounts and how you need to handle it in your aplpication. Consider the following facts:

  • You have an application inside the container that requires a folder to store data or configuration. Sometimes this folder path is hardcoded into the sourcecode, but if it is not, or you are who developed the code, you should be able to change it.
  • Docker supports basicalyl two ways to share files on the host with the container. “volumes” and “bind mounts”, although Docker and Docker Compose refers to both as “volume” sometimes which could be confusing. Regardless of which you are using you will have a mount point inside the container but that can be changed unless you defined the volume moint point in the Dockerfile. When you use the VOLUME instruction in the Dockerfile it will make sure you have a volume even if you didn’t define it when you created the container and you can’t remove it which can lead to situations when you recreate the container and the old data seems to be gone because Docker will generate a new “anonymous” volume.
  • There are named volumes as well which means Docker will create a volume with a static name so as long as you use the same name you will mount the same data into the container.
  • By default, the source folder (path on the host) of named volumes and anonymous volumes will be created under /var/lib/docker/volumes. These kind of volumes are managed by Docker and data from the container will be copied to the source folder the first time you start the container with the volume.
  • If you want to use a specific folder from your host where data already exists or you want to override the existing data inside the container you can use “bind mounts”. (you could also define volumes with custom source fodler but that is another story).
  • You can alway define bind mounts and even volumes when you create a container and that is the better approach. Then you decide what the souce fodler should be and what the moint point (path inside the container should be). As @meyay already explained, that moint point doesn’t have to exist. Docker will create it automatically, but you need to deal with the permissions of the files. For example if your user id on the host is 1000, but the process inside the container is running on behalf of 345 (I just picked a random number) that process may mor may not be able to read or write the files. In this case you need to know what is the user id and group id inside the container for the user that runs the process and set the permissions on the host before you mount the folder.

The init.sh that @meyay also asked about could define variables or directly access to the volume paths. If you can’t describe exactly how your application works, we ask for more details like content of configuration files. Sometimes when we think something is obsious, because it is clear in our head, some information is still missing and it is hard to help other without that.

Based on what you shared, I would recommend removing the VOLUME instructions from the image and define the volume path when you start the container. You haven’t shared how you created the container (docker command or docker compose), but you can do it with both. Then you can change the mount point to be /app/config if that is what your application requires and if you can’t reconfigure your application to use a different path.

If it is still not clear to you what we mean, please show us step by step what commands you use so we can give you more concrete answers.

Thank you both for clarifying some things.

Here is the init.sh file. All relevant files and folder structure is stored inside /init/config folder. When application starts it confirms that everything is in the right place in the /config folder. If something is missing, it is copied from /init/config folder to /config. The script also starts the application WebApp.dll

#! /bin/bash


DIRECTORY_BACKUP=./config/backup
DIRECTORY_BACKUP_FILES=./config/backup/backup_files
DIRECTORY_BACKUP_LOGOS=./config/backup/backup_logos
DIRECTORY_DB=./config/database
DIRECTORY_FEEDS=./config/feeds
DIRECTORY_FFMPEG=./config/ffmpeg
DIRECTORY_LOGOS=./config/logos
DIRECTORY_TEMP=./config/temp
DIRECTORY_MEDIA=./media


if [ ! -d $DIRECTORY_BACKUP ]
 then
	mkdir $DIRECTORY_BACKUP
fi

if [ ! -d $DIRECTORY_BACKUP_FILES ]
 then
	mkdir $DIRECTORY_BACKUP_FILES
fi

if [ ! -d $DIRECTORY_BACKUP_LOGOS ]
 then
	mkdir $DIRECTORY_BACKUP_LOGOS
fi


if [ ! -d $DIRECTORY_DB ]
 then
	mkdir $DIRECTORY_DB
fi

if [ ! -d $DIRECTORY_FEEDS ]
 then
	mkdir $DIRECTORY_FEEDS
fi

if [ ! -d $DIRECTORY_FFMPEG ]
 then
	mkdir $DIRECTORY_FFMPEG
fi


if [ ! -d $DIRECTORY_LOGOS ]
 then
	mkdir $DIRECTORY_LOGOS
fi

if [ ! -d $DIRECTORY_TEMP ]
 then
	mkdir $DIRECTORY_TEMP
fi

if [ ! -d $DIRECTORY_MEDIA ]
 then
	mkdir $DIRECTORY_MEDIA
fi


SOURCE_FFMPEG=./init/config/ffmpeg/ffmpeg
TARGET_FFMPEG=./config/ffmpeg/ffmpeg

SOURCE_PROBE=./init/config/ffmpeg/ffprobe
TARGET_PROBE=./config/ffmpeg/ffprobe

if [ ! -f $TARGET_FFMPEG ]
 then

		echo "copying ffmpeg"
		cp $SOURCE_FFMPEG $TARGET_FFMPEG
	

fi



if [ ! -f $TARGET_PROBE ]
 then

		echo "copying ffprobe"
		cp $SOURCE_PROBE $TARGET_PROBE
		

fi

SOURCE_SETTINGS=./init/config/settings.xml
TARGET_SETTINGS=./config/settings.xml

if [ ! -f $TARGET_SETTINGS ]
 then

		echo "copying settings"
		cp $SOURCE_SETTINGS $TARGET_SETTINGS
	

fi


dotnet WebApp.dll

exit

Docker image build command

docker build -t myapp-image:latest .

Docker build container

docker run -it --name myapp -p 8386:80 -v /mnt/cache/appdata/myapp:/app/config -v /mnt/user/cache_data/media:/app/media -e PUID=99 -e PGID=100 myapp-image:latest

Since the workdir is /app in the Dockerfile and your init script refers to ./config which is actually /app/config in this case and you mounted a host folder to /app/config, your data should be okay. Your volume definition in the Dockerfile is not pointing to the right folder which will create additional anonymous volumes. If you want to avoid that, you can replace the current definition with the followings:

VOLUME /app/config
VOLUME /app/media

or just remove the two volume definition (I would do that, since it is not really useful)

Referring to a part of your first post

IT doesn’t matter where you mount a folder as long as the mount point is actually pointing to the used path in the container. Every container can use completely different folders, but if you want to use /config instead of /app/config (And I assume media instead of /app/media) I can see two options:

  1. Change your init script (you see it was necessary to see it :slight_smile: ). You have relative paths in the init script. In containers I usually use absolute paths so I can be sure where things are exactly. Replace all occurances of ./config with /config (notice the missing dot) and ./media with /media. Rebuild the image.
  2. You could also keep the original init script and replace the /app/config and /app/media folders in the container with a symlink. Just add the following lines to the init script right after the variables before creating directories.
    mkdir -p /app
    mkdir -p /config
    mkdir -p /media
    ln -s /config /app/config
    ln -s /media /app/media
    

Either way you need to use the following command to run the container:

docker run -it --name myapp -p 8386:80 -v /mnt/cache/appdata/myapp:/config -v /mnt/user/cache_data/media:/media -e PUID=99 -e PGID=100 myapp-image:latest

By the way you can make sour init script much shorter if you replace these kind of lines:

if [ ! -d $DIRECTORY_BACKUP ]
 then
	mkdir $DIRECTORY_BACKUP
fi

with this

mkdir -p $DIRECTORY_BACKUP

It won’t do anything if the folder already exists but it will create it if it doesn’t exist.

It works!. Almost. Thank you for you for your help. There is one problem. Database is persisted in config local folder but media files created in this folder are inside the container instead of in local media folder. When container and image is removed all media files are gone. I’m using

/mnt/user/media:/media

I’m not sure why would it work for /config but not for /media

docker run
  -d
  --name='rradio-2'
  --net='bridge'
  -e TZ="America/New_York"
  -e HOST_OS="Unraid"
  -e HOST_HOSTNAME="RADIO"
  -e HOST_CONTAINERNAME="rradio-2"
  -e 'PUID'='99'
  -e 'PGID'='100'
  -l net.unraid.docker.managed=dockerman
  -p '8384:80/tcp'
  -v '/mnt/user/media-2/':'/media':'rw'
  -v '/mnt/user/appdata/rradio-2/':'/config':'rw' 'mpcdigitize/rradio-2' 

Dockerfile

FROM mcr.microsoft.com/dotnet/aspnet:6.0
WORKDIR /app
COPY . .
RUN ["chmod", "+x", "./init.sh"]
ENTRYPOINT ["sh","./init.sh"]

init script

#! /bin/bash


DIRECTORY_BACKUP=/config/backup
DIRECTORY_BACKUP_FILES=/config/backup/backup_files
DIRECTORY_BACKUP_LOGOS=/config/backup/backup_logos
DIRECTORY_DB=/config/database
DIRECTORY_FEEDS=/config/feeds
DIRECTORY_FFMPEG=/config/ffmpeg
DIRECTORY_LOGOS=/config/logos
DIRECTORY_TEMP=/config/temp


mkdir -p /app
mkdir -p /config
mkdir -p /media
ln -s /config /app/config
ln -s /media /app/media 
 
mkdir -p $DIRECTORY_BACKUP
mkdir -p $DIRECTORY_BACKUP_FILES
mkdir -p $DIRECTORY_BACKUP_LOGOS
mkdir -p $DIRECTORY_DB
mkdir -p $DIRECTORY_FEEDS
mkdir -p $DIRECTORY_FFMPEG
mkdir -p $DIRECTORY_LOGOS
mkdir -p $DIRECTORY_TEMP
mkdir -p $DIRECTORY_BACKUP


SOURCE_FFMPEG=/init/config/ffmpeg/ffmpeg
TARGET_FFMPEG=/config/ffmpeg/ffmpeg

SOURCE_PROBE=/init/config/ffmpeg/ffprobe
TARGET_PROBE=/config/ffmpeg/ffprobe

if [ ! -f $TARGET_FFMPEG ]
 then

		echo "copying ffmpeg"
		cp $SOURCE_FFMPEG $TARGET_FFMPEG
	

fi



if [ ! -f $TARGET_PROBE ]
 then

		echo "copying ffprobe"
		cp $SOURCE_PROBE $TARGET_PROBE
		

fi

SOURCE_SETTINGS=/init/config/settings.xml
TARGET_SETTINGS=/config/settings.xml

if [ ! -f $TARGET_SETTINGS ]
 then

		echo "copying settings"
		cp $SOURCE_SETTINGS $TARGET_SETTINGS
	

fi



dotnet WebApp.dll

exit

Spoke too soon. I was able to figure out why /media folder is not persisted outside the container. It is because there is already media folder in the project folder. Once I removed it files are created in the local folder.

However, ffmpeg, ffprobe and settings file are not copied from /init/config folder to local /config folder.
Error no such a file or directory

Then the source probably doesn’t exist, but unfortunately I can’t debug it for you. I could help you to solve the volume mounting issue, but I don’t know your application as much as you do and it seems to me that you need to use your bash debugging skills now to find out what is missing. If you find out it is related to the mounting, we can get back to the mounting issue and help you to solve it, but I don’t see the connection now.

I changed file paths of ffmpeg & ffprobe and for some reason it works. Files are copied properly and they are not overwritten on subsequent reinstall. Instead of /init/ I switched to ./init. target folder remains the same

SOURCE_FFMPEG=./init/config/ffmpeg/ffmpeg
TARGET_FFMPEG=/config/ffmpeg/ffmpeg

SOURCE_PROBE=./init/config/ffmpeg/ffprobe
TARGET_PROBE=/config/ffmpeg/ffprobe

Thank you for your help with setting up my app.