Skip to content

Docker Compose Fundamentals

Back to Docker Tutorials


Write a Basic Compose File

A docker-compose.yaml file describes a multi-service application as code. Each entry under services defines one container. Docker Compose reads this file, spins up the containers, and automatically connects them to the same shared network so they can communicate.

graph TD
    YAML["📄 docker-compose.yaml"]-->CMD("⚙️ docker compose up")
    CMD-->WEB["📦 web
(nginx:alpine
port 8080→80)"]
    CMD-->CACHE["📦 cache
(redis:alpine)"]

    style YAML fill:#f3f4f6,stroke:#9ca3af,stroke-width:2px,color:#1f2937
    style CMD fill:#fef08a,stroke:#eab308,stroke-width:2px,color:#854d0e
    style WEB fill:#dcfce7,stroke:#22c55e,stroke-width:2px,color:#14532d
    style CACHE fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e3a8a

Write your first Compose file.

cat > docker-compose.yaml << 'EOF'
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
  cache:
    image: redis:alpine
EOF

Verify the file was created.

cat docker-compose.yaml
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
  cache:
    image: redis:alpine

Start the Application Stack

docker compose up reads the Compose file and creates, starts, and connects all defined services. The -d flag runs them in detached (background) mode.

Start the stack.

docker compose up -d
[+] Running 3/3
 ✔ Network workspace_default    Created                                   0.1s 
 ✔ Container workspace-cache-1  Started                                   0.2s 
 ✔ Container workspace-web-1    Started                                   0.3s 

Notice in the output that Docker automatically created a default network called workspace_default even though we didn't define one. It also named your containers using the format [project]-[service]-[instance] (e.g., workspace-web-1). The project name defaults to the directory you are in (workspace).

You can assign specific names using the container_name property. Update your Compose file:

cat > docker-compose.yaml << 'EOF'
services:
  web:
    image: nginx:alpine
    container_name: my-custom-web
    ports:
      - "8080:80"
  cache:
    image: redis:alpine
    container_name: my-custom-cache
EOF

Apply the changes by running docker compose up -d again. Docker will detect the configuration change, recreate the containers with your custom names, and keep them on the shared network.

docker compose up -d
[+] Running 2/2
 ✔ Container my-custom-cache  Started                                     0.3s 
 ✔ Container my-custom-web    Started                                     0.4s 

List Running Services

docker compose ps lists the status of all services defined in the Compose file for the current project.

Run docker compose ps to see the state of the web and cache services.

docker compose ps
NAME                IMAGE          COMMAND                  SERVICE   CREATED          STATUS          PORTS
my-custom-cache     redis:alpine   "docker-entrypoint.s…"   cache     15 seconds ago   Up 14 seconds   6379/tcp
my-custom-web       nginx:alpine   "/docker-entrypoint.…"   web       15 seconds ago   Up 14 seconds   0.0.0.0:8080->80/tcp, :::8080->80/tcp

View Service Logs

docker compose logs aggregates the log output of all services into a single stream, prefixed by service name.

Run docker compose logs to view combined logs.

docker compose logs
cache  | 1:C 01 Nov 2023 13:10:01.000 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
cache  | 1:C 01 Nov 2023 13:10:01.000 * Redis version=7.2.3, bits=64, commit=00000000, modified=0, pid=1, just started
cache  | 1:M 01 Nov 2023 13:10:01.005 * Ready to accept connections tcp
web    | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
web    | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
web    | /docker-entrypoint.sh: Configuration complete; ready for start up

Run docker compose logs web to view logs for the web service only.

docker compose logs web
web    | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
web    | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
web    | /docker-entrypoint.sh: Configuration complete; ready for start up

Execute a Command in a Service

docker compose exec runs a command inside a running service container — equivalent to docker exec but using the service name instead of the container name.

Run docker compose exec web ls /usr/share/nginx/html to list the default Nginx web root.

docker compose exec web ls /usr/share/nginx/html
50x.html
index.html

Add a Service with a Dependency

depends_on tells Docker Compose to start a service only after its dependencies are running. This prevents startup race conditions.

graph TD
    CACHE["📦 cache
(starts first)"]-->|ready|API["📦 api
(starts second)"]
    API-->|ready|WEB["📦 web
(starts last)"]

    style CACHE fill:#dcfce7,stroke:#22c55e,stroke-width:2px,color:#14532d
    style API fill:#dcfce7,stroke:#22c55e,stroke-width:2px,color:#14532d
    style WEB fill:#dcfce7,stroke:#22c55e,stroke-width:2px,color:#14532d

Update the Compose file to add an api service that depends on cache:

cat > docker-compose.yaml << 'EOF'
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    depends_on:
      - api
  api:
    image: alpine:3.22
    command: sleep infinity
    depends_on:
      - cache
  cache:
    image: redis:alpine
EOF

Apply changes by running docker compose up -d — Compose will start cache first, then api, then web.

docker compose up -d
[+] Running 3/3
 ✔ Container workspace-cache-1  Started                                   0.0s 
 ✔ Container workspace-api-1    Started                                   0.3s 
 ✔ Container workspace-web-1    Started                                   0.5s 

Stop and Remove the Stack

docker compose down stops all services and removes their containers and the default network. Volumes are preserved unless -v is also passed.

Run docker compose down.

docker compose down
[+] Running 4/4
 ✔ Container workspace-web-1    Removed                                   1.2s 
 ✔ Container workspace-api-1    Removed                                   0.3s 
 ✔ Container workspace-cache-1  Removed                                   0.4s 
 ✔ Network workspace_default    Removed                                   0.2s 

Verify all containers are gone.

docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

🧠 Quick Quiz

#

Which command builds, creates, starts, and attaches to containers for a service defined in docker-compose.yaml?

#

How do you stop and completely remove the containers and networks created by docker compose up?

#

What does the depends_on directive do in a Compose file?


🐳

Practice Live in Your Browser

Don't just read about Docker commands—execute them in real time! Launch a fully-configured, secure Docker sandbox directly in your browser with automated task validation ready for you.

FREE LAUNCH OFFER Get all premium Docker labs for FREE until June 30th! (No Credit Card Required)

📬 DevopsPilot Weekly — Learn DevOps, Cloud & Gen AI the simple way.
👉 Subscribe here