Docker Compose
In this lesson you start three services together: Nginx as a web server, a Node.js program, and a PostgreSQL database. You describe them once in a file called compose.yaml, and Docker Compose starts, lists, reads and stops all of them with one command. Compose also creates a network for the services, so Nginx reaches the Node.js program by its name.
Compose is part of Docker. Use the current command, docker compose with a space. The older docker-compose command is no longer used in this course. Every key used in the file is described in the Compose file reference.
Step 1: Create the project folder and its files
Create a folder named docker-course. Inside it, create the files below. Each file is plain text, and each one is shown with its exact name and location. Use any plain-text editor, and do not add formatting. The Windows Notepad editor and macOS TextEdit both work if you choose plain text.
Your folder will look like this when you finish:
docker-course/
├── compose.yaml
├── nginx.conf
└── app/
└── server.js
compose.yaml
This file lists the three services and the storage they use.
services:
web:
image: nginx:stable
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- app
app:
image: node:24
working_dir: /app
command: node server.js
volumes:
- ./app:/app:ro
db:
image: postgres:17
environment:
POSTGRES_PASSWORD: learner-only-change-me
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Here is what each key does:
services:starts the list of containers. Each name under it,web,appanddb, is the service name. Other services use that name to reach it.image:is the image and tag to start, the same idea as in the earlier lessons. This file usesnginx:stable,node:24andpostgres:17.ports:publishes a port."8080:80"connects port 8080 on your computer to port 80 inside thewebcontainer. The quotes keep the value as text.volumes:attaches storage../nginx.conf:/etc/nginx/conf.d/default.conf:rocopies the path of your file into the container, read-only (ro)../app:/app:roattaches yourappfolder the same way. A path starting with./means “in the same folder as this file”, on every operating system.depends_on:tells Compose to startappbeforeweb.working_dir:sets the folder wherecommandruns.command:is the program the container runs. Here it runsserver.jswith Node.js.environment:sets environment variables.POSTGRES_PASSWORDsets the password for the database administrator. Use it only for this practice, and never reuse it.pgdata:/var/lib/postgresql/dataattaches a named volume calledpgdatato the folder where PostgreSQL stores its data.volumes:at the bottom declares the named volumepgdata, so Docker creates it and keeps it between runs.
nginx.conf
This file tells Nginx what to do with a visit. It replaces the welcome page you saw in the Run Nginx lesson.
server {
listen 80;
location / {
proxy_pass http://app:3000;
}
}
listen 80;makes Nginx accept visits on port 80 inside its container.location / { ... }applies to every address on the site.proxy_pass http://app:3000;sends each visit to the service namedappon port 3000. Nginx does not know an address forapp; Compose’s network answers the nameappwith the address of the Node.js container.
app/server.js
This is the small Node.js program that answers the visits. Create the app folder inside docker-course, and put this file in it.
const http = require('node:http');
const port = 3000;
// Listens on every network interface so the other containers can reach it.
http.createServer((request, response) => {
response.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
response.end('Hello from Node.js running in a container\n');
}).listen(port, () => {
console.log(`Listening on port ${port}`);
});
Node.js is not installed on your computer for this lesson. The node:24 image provides it inside the app container.
Step 2: Start all the services
Open your terminal in the docker-course folder. Use cd followed by the folder path, for example cd Documents/docker-course. Then type this command and press Enter:
docker compose up -d
Compose creates a network, downloads any missing images, and starts the three containers. Lines such as Container docker-course-web-1 Started show each step. The name of each container is the project name, which comes from the folder name, followed by the service name and a number.
Step 3: Check that everything is running
Type this command and press Enter:
docker compose ps
You should see three rows, one for each service, with running or Up in the status column. The web row shows 0.0.0.0:8080->80/tcp, which means port 8080 is published.
Open a browser and go to http://localhost:8080. You should see:
Hello from Node.js running in a container
The browser asked Nginx. Nginx passed the visit to the app service by its name, and the Node.js program answered through Nginx.
Now check the database. Type this command and press Enter:
docker compose exec db psql -U postgres -c "SELECT 1 AS ok;"
It prints a table with one row, ok and 1. The database accepts queries from inside its own container, and no other program on your computer can reach it, because the db service publishes no port.
Step 4: Read the logs
Type this command and press Enter:
docker compose logs
Compose prints the output of all three services, each line marked with its service name. Look for app-1 | Listening on port 3000 and for lines from web-1 that show GET / answered with 200. To see the logs of one service, add its name at the end, for example docker compose logs app.
Step 5: Stop the services, keeping the data
Type this command and press Enter:
docker compose down
Compose stops and removes the three containers and the network. The named volume pgdata is kept, so the database data survives.
Step 6: Remove the services and the data
Compose removes the volume only when you ask it to. Type this command and press Enter:
docker compose down -v
Warning: -v deletes the named volume pgdata, so every table and row in the database is deleted for good. Use it only when you no longer need the data. After this command, docker compose ps lists no services, and docker volume ls no longer lists docker-course_pgdata.
What you learned
- One file describes several services, and
docker composestarts, shows, reads, stops and removes them together. - Compose creates a network for the services, so each service reaches the others by its service name.
downremoves containers and networks and keeps volumes.down -valso removes volumes and their data.
What comes next
Go on to Manage containers with Portainer, where a web page shows the containers, networks and volumes you have made so far.