Stackvora.tech

Docker Compose Basics

A foundational guide to Docker Compose, covering its architecture, `docker-compose.yml` structure, common commands, and best practices for multi-container application development.

Docker Compose simplifies the orchestration of multi-container Docker applications. Instead of managing individual containers via docker run commands, Compose allows you to define your entire application stack in a single YAML file, streamlining deployment, scaling, and management. This guide covers the essential aspects of Docker Compose, from its core concepts to practical examples and troubleshooting.

Docker Compose is typically installed alongside Docker Desktop or as a standalone Python package. As of Docker Desktop 3.0+, it's often included as the docker compose (without a hyphen) plugin, superseding the older docker-compose (with a hyphen) command. This article will primarily use the newer docker compose syntax.

Understanding the docker-compose.yml Structure

The heart of any Compose project is the docker-compose.yml file. This YAML file describes the services that make up your application, their configurations, networks, and volumes. A typical structure looks like this:

version: '3.8'
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    depends_on:
      - db
    networks:
      - app_net
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example
      POSTGRES_DB: mydatabase
    volumes:
      - dbdata:/var/lib/postgresql/data
    networks:
      - app_net
volumes:
  dbdata:
networks:
  app_net:
    driver: bridge

Top-Level Keys

  • version: Specifies the Compose file format version. Using '3.8' or higher is recommended for access to the latest features.
  • services: Defines the individual containers that constitute your application. Each service entry corresponds to one container configuration.
  • volumes: Declares named volumes that can be shared between services and persist data beyond container lifecycles.
  • networks: Defines custom networks for inter-container communication, offering better isolation and naming.

Service-Specific Directives

Each service within the services section can have numerous configuration options:

  • image: The Docker image to use for the service (e.g., nginx:alpine, my-custom-app:1.0).
  • build: Specifies a path to a Dockerfile if you need to build a custom image (e.g., build: ./app). Can also take an object with context and dockerfile keys.
  • ports: Maps host ports to container ports (e.g., "8080:80" maps host port 8080 to container port 80).
  • environment: Sets environment variables within the container (e.g., POSTGRES_PASSWORD: example). Can also reference an .env file or host environment variables.
  • volumes: Mounts host paths or named volumes into the container (e.g., dbdata:/var/lib/postgresql/data).
  • networks: Attaches the service to one or more defined networks.
  • depends_on: Expresses dependency between services, ensuring they start in a specific order. Note: This only ensures startup order, not service readiness.
  • restart: Defines the restart policy (e.g., no, always, on-failure, unless-stopped).
  • command: Overrides the default command specified by the image.
  • entrypoint: Overrides the default entrypoint specified by the image.
  • env_file: Specifies one or more files to load environment variables from.

Common Docker Compose Commands

The docker compose command is your primary interface for managing your application stack.

docker compose up

This command builds, (re)creates, starts, and attaches to containers for your services.

  • docker compose up -d: Starts services in detached mode (in the background). This is the most common way to deploy.
  • docker compose up --build: Forces Compose to rebuild images for services that have a build instruction, even if they haven't changed.
  • docker compose up --force-recreate: Recreates all containers, even if their configuration hasn't changed. Useful for ensuring a fresh start.
  • docker compose up [SERVICE_NAME]: Starts only a specific service and its dependencies.

docker compose down

Stops and removes containers, networks, and volumes created by up.

  • docker compose down: Stops and removes containers and default networks.
  • docker compose down -v: Also removes named volumes declared in the volumes section of docker-compose.yml. Use with caution as this deletes persistent data!
  • docker compose down --rmi all: Removes images used by any service, in addition to containers, networks, and volumes.

docker compose ps

Lists all running services defined in the docker-compose.yml.

$ docker compose ps
NAME                IMAGE               COMMAND                  SERVICE             CREATED             STATUS              PORTS
myapp-db-1          postgres:16         "docker-entrypoint.s..." db                  5 seconds ago       running             5432/tcp
myapp-web-1         nginx:alpine        "/docker-entrypoint.s..." web                 5 seconds ago       running             0.0.0.0:8080->80/tcp

docker compose logs

Displays log output from services.

  • docker compose logs: Shows logs from all services.
  • docker compose logs -f: Follows log output in real-time (like tail -f).
  • docker compose logs web: Shows logs only for the web service.
  • docker compose logs --tail 100: Displays the last 100 lines of logs.

docker compose exec

Executes an arbitrary command inside a running service container.

$ docker compose exec db psql -U postgres mydatabase
Password for user postgres:
psql (16.2 (Debian 16.2-1.pgdg120+1))
Type "help" for help.

mydatabase=#

docker compose build

Builds or rebuilds services defined with a build instruction.

docker compose build web

Networks and Volumes

Proper management of networks and volumes is crucial for robust Compose applications.

Networks

By default, Compose creates a single default bridge network for your application. All services are attached to this network and can reach each other by their service names. For more complex setups, defining custom networks offers better control and isolation.

For example, to create a custom bridge network named app_net:

networks:
  app_net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/24

Then, attach services to it:

services:
  web:
    networks:
      - app_net

Volumes

Volumes are the preferred mechanism for persisting data generated by Docker containers. Named volumes are managed by Docker and are typically created in /var/lib/docker/volumes/ on Linux hosts. Bind mounts, which map a host directory directly into a container, are also supported but less portable.

volumes:
  dbdata:
    driver: local # default
    driver_opts:
      type: 'none'
      o: 'bind'
      device: '/path/to/host/data/for/db'

The above example shows how to configure a named volume to use a specific host path as a bind mount for more direct control, though this is less common than letting Docker manage the named volume completely.

Common Pitfalls and Troubleshooting

  • Port Conflicts: If docker compose up fails with an error like Error starting userland proxy: listen tcp 0.0.0.0:8080: bind: address already in use, it means a process on your host machine (or another container) is already using the specified host port. Change the host port in your ports mapping (e.g., "8081:80") or stop the conflicting process.
  • Service Readiness vs. Startup Order: depends_on only ensures that dependent services start in order, not that they are fully ready to accept connections (e.g., a database might be starting but not yet listening on its port). For production, consider using health checks (Compose v3.4+) or application-level retry logic.
  • Data Persistence: Forgetting -v with docker compose down means your named volumes (and thus your data) will persist. This is usually desired for databases. However, if you want a clean slate, remember to add -v.
  • YAML Indentation: YAML is whitespace-sensitive. Incorrect indentation will lead to parsing errors. Use a linter or a good editor with YAML support.
  • Environment Variables: Ensure environment variables are correctly loaded. If using an .env file, it must be in the same directory as docker-compose.yml by default, or specified with --env-file. Remember that variables defined in environment block within the service take precedence over env_file.
  • Image Pull Errors: If an image fails to pull (e.g., repository does not exist or may require 'docker login'), verify the image name and tag, and ensure you are logged into any necessary private registries.

Back to the knowledge base · Ask the AI assistant