Docker Compose

Compose runs a set of related containers as one project. Use the current Compose plugin through docker compose. The old standalone Compose v1 command is retired and should not be used for new work.

Check the plugin.

1docker compose version
2docker compose ls

Compose files

The preferred file name is compose.yaml. Current Compose files follow the Compose Specification; do not add a top-level version key to new files.

The sample below defines three services: a MySQL database, a Flask API, and an nginx-hosted frontend.

 1name: student-stack
 2services:
 3  db:
 4    image: db-app:local
 5    container_name: db
 6    ports:
 7      - "3306:3306"
 8    volumes:
 9      - type: bind
10        source: ./mysql/docker-entrypoint-initdb.d
11        target: /docker-entrypoint-initdb.d
12        consistency: consistent
13    environment:
14      MYSQL_ROOT_PASSWORD: "oneoffcoder"
15    healthcheck:
16      test: mysqladmin ping -h localhost -p$$MYSQL_ROOT_PASSWORD && test '0' -eq $$(ps aux | awk '{print $$11}' | grep -c -e '^mysql$$')
17  flask:
18    image: rest-app:local
19    container_name: rest
20    ports:
21      - "5000:5000"
22    healthcheck:
23      test: ["CMD", "curl", "-f", "http://localhost:5000"]
24      interval: 60s
25      timeout: 10s
26      retries: 3
27      start_period: 40s
28    depends_on:
29      - db
30  ng:
31    image: ui-app:local
32    container_name: ui
33    ports:
34      - "80:80"
35    healthcheck:
36      test: ["CMD", "curl", "-f", "http://localhost:80"]
37      interval: 60s
38      timeout: 10s
39      retries: 3
40      start_period: 40s
41    depends_on:
42      - flask
43      - db

The YAML file is mostly self-explanatory.

  • We create three services: db, flask and ng.

  • Each service has a specified container image: db-app:local, rest-app:local and ui-app:local.

  • The database mounts initialization scripts and receives its root password through environment configuration.

  • Health checks describe how Compose can tell whether a service is healthy.

For services that need a dependency to be healthy before startup, use long-form depends_on.

1services:
2  api:
3    image: rest-app:local
4    depends_on:
5      db:
6        condition: service_healthy

Environment files

Keep local defaults in a .env file or pass an explicit environment file. Do not commit real secrets.

1docker compose --env-file .env.local config
2docker compose --env-file .env.local up --build

Profiles

Profiles let optional services stay out of the default development loop.

1services:
2  worker:
3    image: worker-app:local
4    profiles:
5      - jobs

Run the optional service only when the profile is requested.

1docker compose --profile jobs up

Secrets and configs

Compose supports secrets and configs. For local development, a secret can be mounted from a file.

1services:
2  api:
3    image: rest-app:local
4    secrets:
5      - db_password
6
7secrets:
8  db_password:
9    file: ./secrets/db_password.txt

The application reads the mounted file rather than receiving the value through an environment variable.

Networks and volumes

Compose creates a project network by default. Add named networks when you need explicit boundaries, and use named volumes for persistent state.

 1services:
 2  db:
 3    image: db-app:local
 4    volumes:
 5      - db_data:/var/lib/mysql
 6    networks:
 7      - backend
 8
 9volumes:
10  db_data:
11
12networks:
13  backend:

Daily commands

The commands below cover the normal loop.

1docker compose -f compose.yaml config
2docker compose -f compose.yaml up --build
3docker compose -f compose.yaml ps
4docker compose -f compose.yaml logs -f
5docker compose -f compose.yaml exec flask sh
6docker compose -f compose.yaml down --volumes

Use Compose for local development, integration tests, and simple single-host deployments. For managed cloud deployments, translate the same image and configuration ideas into the target platform’s native model, such as ECS task definitions or Kubernetes manifests.

Dockerfiles

MySQL

1FROM mysql:latest

Flask

1FROM python:3
2
3WORKDIR /rest-app
4COPY ./rest-app .
5RUN pip install --no-cache-dir requests flask flask-cors mysql-connector-python SQLAlchemy
6
7CMD [ "python", "./app.py" ]

Angular

 1FROM node:lts AS node_builder
 2WORKDIR /tmp/ui-app
 3COPY ./ui-app .
 4RUN npm install -g @angular/cli@latest
 5RUN npm install
 6RUN ng build
 7
 8FROM nginx:alpine 
 9COPY --from=node_builder /tmp/ui-app/dist/ui-app /usr/share/nginx/html
10EXPOSE 80

References