Docker mounted file is not updating? Your text editor probably updated the file inode.

Jonas Bunevičius
4 min readJun 10, 2020

--

Photo by NeONBRAND on Unsplash

Docker platform can be used for various reasons. In other words, it is enough flexible to use in development and production environments. In most cases, a Docker container holds all the required data to run an application. However, practice shows that it often is handy to have some files directly on the host, not in a container.

The reasons for that can be very varied. For example, during active development, it is handy to access often changing files directly from the host. This approach helps to avoid common container re-build. By the way, most modern applications are communicating with external services and can have various configurable options. In most cases, these files can’t be embedded into a Docker image due to security reasons.

Luckily Docker allows easily adjust container content with custom bind mounts. The following feature is useful and is in use by many Docker users. However, it has one tricky part which can cause a lot of confusion, especially for Linux users.

If you ever had a situation when mounted file inside Docker container and on the host is different (not in sync)? If yes, this is mean that you already have been caught by expected but not yet documented Docker bind-mount limitation.

The short explanation to this would be that Docker container single file bind mount strictly depends on the inode number. Docker doesn’t care about file inode changes on the host when a container is already up and running. This is means that it is a possibility that container points to the old file inode. However, the same file after an update can be under a new inode on the host.

A mentioned situation can be demonstrated with a simple one-line file. So let’s create a file which has some text and mount it into the new Docker container:

echo 'Very important text' > ~/file.txtdocker run -d --rm --name mountTest --mount type=bind,source=/root/file.txt,target=/file.txt alpine sleep 5000

Here want to point out that it doesn’t matter which bind mount flag will be used. Both flags give the same result. In other words, I just follow the Docker documentation recommendation.

Tip: New users should use the --mount syntax. Experienced users may be more familiar with the -v or --volume syntax, but are encouraged to use --mount, because research has shown it to be easier to use.

When the container is running it is time to verify that everything is in place. The expected result is such that file inside the container and on the host point to the same inode.

[root@server ~]# cat file.txt 
Very important text
[root@server ~]# ls -i file.txt
16845572 file.txt
[root@server ~]# docker exec mountTest cat file.txt
Very important text
[root@server ~]# docker exec mountTest ls -i file.txt
16845572 file.txt

As can be seen, previously created file points to the inode number 16845572. File content is also the same inside the container and on the host. Now let see what happen, if file content will be updated with stream editor sed:

[root@server ~]# sed -i.bak 's/./\U&/g' file.txt[root@server ~]# cat file.txt
VERY IMPORTANT TEXT
[root@server ~]# ls -i file.txt
17754028 file.txt
[root@server ~]# docker exec mountTest cat file.txt
Very important text
[root@server ~]# docker exec mountTest ls -i file.txt
16845572 file.txt

Stream editor did its job exactly like was requested. However, now the previously mounted file is not in sync between host and container. It happened because of sed behavior which is quite common in Linux. Behind the scene, sed created a temporary file, sent output to that file, and finally renamed that file to the original name.

That is the main reason why the file inode number changed to 17754028 on the host. As mentioned, Docker doesn’t care about these changes. The container still points to the old inode number 16845572 on the filesystem and determines the following unexpected result.

[root@server ~]# ls -i file.txt* 
17754028 file.txt 16845572 file.txt.bak
[root@server ~]# echo 'File update using echo' > file.txt.bak[root@server ~]# docker exec mountTest cat file.txt
File update using echo

When updating the file with sed, the backup extension also has been supplied. That permitted to save the original file. As can be seen, it is under the old inode number and no matter that file name changed. It is because the container points to the inode on the filesystem, not to file name. If we update this file with a command which behaves differently than sed, Docker container mounted file content also be updated.

Tools can behave differently. For example, I mostly use the “Visual Studio Code” together with the “Remote Development” extension. However, sometimes much easier and faster to update config files just using a text editor like vi. The first one directly updates a file but the second one works very similar like sed.

So what to do? In my opinion, there are at least three options:

  • As often as possible use not a single file but directory bind mount
  • Restart Docker container after any mounted single file modification
  • Use editors only without swap files or disable this feature

The first option works always without exceptions. Meanwhile, the rest are constantly required to follow instructions. If it is one person project, misunderstandings can be avoided. In other cases, it is definitely recommended to use a directory, not single file bind mounts.

--

--