Let containers communicate (inter-service, with the host, with the public internet) and persist data across restarts.
Networking is the road system; ports are house numbers. Volumes are external hard drives you can plug into any container.
-p 8080:80 host 8080 -> container 80
--network ... bridge | host | none | custom
--dns 8.8.8.8 custom resolver
-v /host:/c bind mount (host FS)
-v volname:/c named volume (docker-managed)
Docker networking has four drivers: bridge (default; private 172.17.0.0/16 network), host (no isolation - container shares host’s net ns), none (no network), and user-defined networks (Compose creates these; DNS resolution by service name). Port publishing -p 8080:80 maps host TCP 8080 to container 80; -p 127.0.0.1:8080:80 only on loopback. Bind mounts (-v /host:/c) for dev iteration; named volumes for prod, in /var/lib/docker/volumes/.
docker run -d --name web -p 127.0.0.1:8080:80 nginx
curl http://localhost:8080
binds to host loopback only - not exposed to public even though ‘running’.
docker network create mynet
docker run --network mynet --name db postgres:16
docker run --network mynet \
-e DATABASE_URL=postgres://db/app --name api myapi
creates a user-defined network so api can resolve db by name.
docker volume create pgdata
docker run -d -v pgdata:/var/lib/postgresql/data postgres:16
named volume survives container removal; live in /var/lib/docker/volumes/pgdata/_data.
Exposing databases to the public internet (-p 5432:5432 without -p 127.0.0.1). Confusing bind-mount and volume. Publishing too many ports (security risk).
user-defined networks for inter-service DNS; loopback-only port publishing for dev; named volumes for prod persistence; bind mounts for hot-reload during development.
publicly-routable port 5432 from security scanner.nmap; Shodan index.docker run -p 5432:5432 postgres without binding to 127.0.0.1.-v /empty:/var/lib/postgres/data in prod.