If Part 5 was about building images, Part 6 asks a new question: how do you define and run related containers together? Compose makes that easier with one Compose file, usually named compose.yaml, where you describe services, networks, and volumes. This article introduces the basics with an nginx static site plus MySQL example.
How this post flows
- Build a mental model of the problem Compose solves
- Define the smallest web + DB Compose example
- Practice the must-know commands (
up,down,logs,ps) - Read volume, environment, and network declarations with confidence
- Capture the instincts beginners need first
Reading card
- Estimated time: 16 minutes
- Prereqs: you have already built an image with a Dockerfile
- After reading: you can write a Compose file and launch two containers together.
Mental model: one file for one small app
Before reading the YAML, lock in three words first.
services: the app components you want to run (web, db, ...)
networks: the communication paths between those components
volumes: the persistent storage that stays even if containers are recreated
Once those three pieces are in a Compose file, docker compose up can create and start the whole app, and docker compose down can stop and remove it. That is the main friction Compose removes for beginners.
Web + DB Compose example
This example is intentionally small. A static nginx site does not usually need MySQL, but putting a web service and a database service in one file makes the Compose structure easy to read for the first time.
Here is a minimal compose.yaml.
services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./public:/usr/share/nginx/html:ro
networks:
- app-net
db:
image: mysql:8.4
environment:
- MYSQL_ROOT_PASSWORD=your-root-password
- MYSQL_DATABASE=school
volumes:
- db-data:/var/lib/mysql
networks:
- app-net
volumes:
db-data:
networks:
app-net:
driver: bridge
In a simple project, the explicit app-net block is optional because Compose can create a default network for you. It is shown here so you can see where network settings live.
This one file already answers the core questions:
- The
webservice mounts static files read-only and maps host port 8080 to container port 80. - The
dbservice uses environment variables for first-run initialization and stores its data in thedb-datavolume. - Both services share the
app-netnetwork, so containers on that network can reach MySQL atdb:3306.
The two volume styles are worth separating clearly:
./public:/usr/share/nginx/html:rois a bind mount, so the container reads files from your local folder.db-data:/var/lib/mysqlis a named volume, so Docker manages that storage and keeps it across container replacement.
Compose commands to try in the terminal
- Save the file and run
docker compose up -dto start both services in the background. On current Docker Desktop releases,docker composereplaces the legacydocker-composebinary. - Use
docker compose psto view container names, ports, and status. - Run
docker compose logs -f webto stream only the web server logs. - When you need to stop everything, run
docker compose downto remove containers and networks. - Named volumes declared under the
volumesblock survivedown, sodb-datareattaches on the nextup. Passdocker compose down -vonly when you truly want to remove this project's volumes and wipe its data.
Mini Lab: Change the
webserviceportsentry to9090:80, then rundocker compose up -dagain. You now have to visithttp://localhost:9090. Switching the port in your browser makes port mapping feel concrete. If a project usesbuild:, that is when--buildbecomes useful.
Checklist for reading volumes, environment variables, and networks
- volumes: Named volumes such as
db-datakeep data across restarts. Bind mounts like./publicmirror local folder changes immediately. - environment: Define configuration such as database names or application modes. For local development,
.envorenv_filecan keep values out of the main YAML, but they are not secure secret storage for production. - networks: In simple cases Compose creates a default network automatically. You can also declare multiple networks to let
webtalk to an external reverse proxy while keepingdbon an internal-only network.
One critical reminder: service names like db only resolve inside the same Compose network. From your Mac terminal, you can only connect through published host ports. In this example, web is reachable at http://localhost:8080, but MySQL is not reachable from the host unless you also publish 3306:3306.
When you read a Compose file for the first time, look for these three blocks. Most other options are details layered on top of this structure.
Instincts beginners should build first
Compose has one essential separation of concerns:
- Services: what to run
- Networks: who talks to whom
- Volumes: what data should survive container replacement
Once that framework is solid, you can scan any project’s docker-compose.yml quickly. Part 7 explains why dev and prod containers differ inside the same file, using a Node app as the practical comparison.
💬 댓글
이 글에 대한 의견을 남겨주세요