Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does "volumes" override the docker image's original files with docker-compose?

Let's use this docker-compose.yml:

version: '2'
services:
   db:
     image: mysql:5.7
     volumes:
       - ./mysql:/var/lib/mysql                # <- important
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress
   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     volumes:
       - ./wp:/var/www/html                    # <- important
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress

I noticed that:

  • When doing

    mkdir wp
    docker-compose up
    # create a basic Wordpress website from the browser
    # stop the containers from the command-line with CTRL+C
    

    then, ./wp/ (initially empty) is filled with the new Wordpress files (**). This is normal.

  • Then let's do

     docker rm wordpress_1 db_1   # remove the existing containers but keep 
                                  # ./wp/ as it has been created in the previous step (**)
     docker-compose up
    

    During the re-creation of the containers, ./wp/ is not overwritten with new Wordpress files, instead the previous files from previous step (**) are kept! Why?

    How does it magically know that new Wordpress files should not be written, but that instead, the previous files should be kept?

Question: How does docker decide if /hostdir/:/containerdir/ listed in volumes: should override the files which are already present in the original docker image, or not?

like image 684
Basj Avatar asked Sep 06 '25 00:09

Basj


1 Answers

Bind mounts with that syntax always overwrite the files that are present in the image. This acts the same way as the normal Linux mount(8) command: if you mount something like a USB disk over part of your source directory, the contents of the mounted device hide what was originally in the filesystem, and all reads and writes use the mounted device instead.

When the container starts up, this means it can look to see if its data directory is empty, and if it is, install some initial data there. You cite the Docker Hub wordpress image; that has an entrypoint script that has an explicit check

if [ ! -e index.php ] && [ ! -e wp-includes/version.php ]; then
    echo >&2 "WordPress not found in $PWD - copying now..."
    # ... some code that creates sourceTarArgs and targetTarArgs ...
    tar "${sourceTarArgs[@]}" . | tar "${targetTarArgs[@]}"
    echo >&2 "Complete! WordPress has been successfully copied to $PWD"
fi

The mysql image has a similar check to see if its data directory is empty. If it is, it does its first-time initialization, including processing the /docker-entrypoint-initdb.d directory; if it isn't, it assumes there is pre-existing data there and completely skips the initialization step.

In general you should try to reserve bind mounts and similar volumes for actual data, and not copy your code there. In the case of the wordpress image, since it makes a copy of the application in the volume, it's not totally obvious what would happen if you tried to upgrade the underlying image: the volume takes precedence and the upgrade could get ignored.

Named Docker volumes (as distinct from bind mounts) will copy content from the underlying image, but only if it's a named volume and not some other kind of mount, only on Docker proper (not in Kubernetes), and only if the volume is totally empty (it's the very first time you've run the container). Avoid relying on this behavior, since it's not especially portable and ignores updates in the underlying image.

like image 186
David Maze Avatar answered Sep 07 '25 22:09

David Maze