moeman
moeman

Reputation: 129

Docker: Strange behaviour of bind mount

I have a compose file:

version: "3"
services:

  db:
    image: postgres:12.5
    ports:
      - "15432:5432"
    restart: always  
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: test
    volumes:
      - ./postgres-data:/var/lib/postgresql/data

  backend:
    image: backend
    depends_on:
      - db
    restart: always
    ports:
      - "6969:8000"
    volumes:
      - ./app:/app

When I run docker-compose up to start the 2 containers, I notice that:

Both service uses bind mount, and as I understand: bind mount always transfer the content from the host directory to the container. But this does not happen in the case of the db service

So where did I understand wrong here ?

Thanks in advance

Upvotes: 1

Views: 803

Answers (2)

David Maze
David Maze

Reputation: 158647

Nothing ever gets "transferred". The contents of the host directory always hide what was in the underlying image, and once the container has started, the host and container directories are "the same"; writes in one side should be visible in the other.

The Docker Hub database images in particular know to look at their data directories at startup time. The sequence is roughly like this:

  1. Docker creates a container, with the host's $PWD/postgres-data directory replacing /var/lib/postgresql/data in the container.
  2. The postgres image's entrypoint script looks at the data directory and sees it is totally empty. It then creates a new database and runs any first-time-initialization scripts. Since that directory is a bind-mounted host directory, this content is also visible on the host.
  3. The database starts up normally with whatever its data directory is.

This work is done on the mounted directory at container startup time, so it happens to be visible on the host as well. (If you docker run --rm -it --entrypoint /bin/sh postgres and look around, bypassing its entrypoint script, you'll probably find the /var/lib/postgresql/data directory is totally empty; there is nothing there to be "transferred to the host".)

This also means that your bind mount over the application container's /app directory hides whatever the Dockerfile does; if you run a build sequence it will get lost, and if the directory layouts in the host and container aren't identical, you will have non-reproducible problems. This further means you can't directly use volumes to copy file out of an image; you have to run a container that runs a cp command.

Upvotes: 5

Chris Becke
Chris Becke

Reputation: 36016

I must admit that I thought this behaviour only worked with volume mounts and not bind mounts. The documentation for the volume long syntax indicates the copy mode is only available to volume mounts.

Upvotes: 1

Related Questions