Happy Dussehra! May good always win. Happy Diwali! Wishing you a bright year ahead. Merry Christmas! Warm wishes from Vizag. Happy New Year! Here’s to a great year ahead.

Archive · Docker

Docker Volumes and Networks

Keep data safe with volumes and bind mounts, connect containers with user-defined networks and DNS, and publish ports without exposing more than you intend.

3 min read

  • Docker Engine 19.03.12
  • PostgreSQL 12
  • overlay2
  • Ubuntu 20.04 LTS

Containers are disposable, but data and connectivity are not. Two ideas solve both: volumes keep data outside the container’s lifecycle, and user-defined networks let containers find each other by name without exposing anything to the outside world.

Versions in use on this date. Docker Engine 19.03.12 (released 1 July 2020) on Ubuntu 20.04 LTS, with PostgreSQL 12 as the example database.

Storage and network layout

flowchart TB
  subgraph HOST[Docker host]
    subgraph NET[User-defined network appnet - internal DNS]
      APP[api container<br/>port 3000] -->|db:5432| DB[postgres container]
    end
    VOL[(Named volume pgdata<br/>/var/lib/docker/volumes)]
    BM[Bind mount<br/>/srv/config on host]
    DB --- VOL
    APP --- BM
  end
  USER[Users] -->|published port 443 via proxy| PX[Reverse proxy container]
  PX --> APP
  USER -.->|no published port| DB

Volumes and bind mounts

Type Created with Lives Use for
Named volume -v pgdata:/var/lib/postgresql/data Managed by Docker Databases and application state
Bind mount -v /srv/config:/etc/app:ro A path you choose Config files, development source code
tmpfs --tmpfs /tmp Memory only Scratch space, secrets that must not touch disk
docker volume create pgdata
docker run -d --name db -e POSTGRES_PASSWORD=change-me -v pgdata:/var/lib/postgresql/data postgres:12
docker volume ls; docker volume inspect pgdata
docker rm -f db                                  # the volume survives
docker run -d --name db2 -e POSTGRES_PASSWORD=change-me -v pgdata:/var/lib/postgresql/data postgres:12   # data is back

Mount configuration read-only (:ro) where possible. Bind mounts depend on host paths and ownership: a container running as UID 999 cannot write to a directory owned by root unless you adjust permissions.

Backup and restore a volume

docker run --rm -v pgdata:/data -v $(pwd):/backup alpine tar czf /backup/pgdata-$(date +%F).tar.gz -C /data .
docker run --rm -v pgdata:/data -v $(pwd):/backup alpine tar xzf /backup/pgdata-2020-08-03.tar.gz -C /data

For databases, prefer a logical backup (docker exec db pg_dump -U postgres app > app.sql), since a file copy of a running database may be inconsistent.

Networks

docker network ls
docker network create appnet
docker run -d --name db --network appnet -e POSTGRES_PASSWORD=change-me -v pgdata:/var/lib/postgresql/data postgres:12
docker run -d --name api --network appnet -p 127.0.0.1:3000:3000 -e DATABASE_URL=postgres://postgres:change-me@db:5432/postgres myapi:1.0.0
docker exec api getent hosts db                 # resolves via Docker's embedded DNS
docker network inspect appnet
Driver Behaviour
bridge (default) A private network on one host. The default bridge has no name resolution; user-defined bridges do
host Container shares the host network stack (fast, no isolation)
none No networking
overlay Spans multiple hosts (Swarm)

Containers on the same user-defined network reach each other by container name on the container’s own port; no publishing is needed. Only the entry point (the reverse proxy or the API) needs a published port. The database in the diagram has none, so nothing outside the host can reach it.

Publishing ports safely

-p 8080:80                     # all host interfaces (and bypasses ufw rules!)
-p 127.0.0.1:8080:80           # only the host itself, for a reverse proxy
-p 192.168.1.10:8080:80        # one specific interface

Docker manages iptables itself, so a published port can be reachable even if the host firewall seems to block it. Restrict it with the bind address, or add rules to the DOCKER-USER chain. Check with ss -tlnp and an external nmap.

Troubleshooting

  • Container cannot resolve the other container’s name: both must be on the same user-defined network.
  • permission denied writing a bind mount: match UID/GID (--user $(id -u):$(id -g) or chown the host path).
  • Data “disappeared”: the container was started without the volume, or a different volume name was used. docker inspect -f '{{json .Mounts}}' db shows what is mounted.
  • Unused volumes fill the disk: docker system df -v; remove carefully with docker volume prune (it deletes volumes not attached to any container).
  • Port conflicts: another process owns the host port; use ss -tlnp | grep 5432.