January 7, 2026

Welcome back to the Docker Simplified series! In the last chapter, we explored Docker Volumes and how to manage persistent data in containers. Now, let’s dive into Docker Compose, an essential tool for anyone working with multi-container applications.

In this chapter, we’ll cover everything you need to know about Docker Compose, from its key benefits and core concepts to advanced features like scaling services and networking. Whether you’re setting up a local development environment or deploying to production, Docker Compose can simplify and streamline your workflow.

So grab your favorite beverage, and let’s explore how Docker Compose can make managing containerized applications effortless! 🚀

Docker Compose is a powerful tool for defining and running multicontainer Docker applications. With Compose, you use a YAML file to configure your application’s services, networks, and volumes. Then, with a single command, you create and start all the services from your configuration.

Note: Docker Compose is now integrated into Docker CLI. The new command is docker compose instead of docker-compose. We’ll use the new command throughout this part.

Key Benefits of Docker Compose

  1. Simplicity: Define your entire application stack in a single file.
  2. Reproducibility: Easily share and version control your application configuration.
  3. Scalability: Simple commands to scale services up or down.
  4. Environment Consistency: Ensure development, staging, and production environments are identical.
  5. Workflow Improvement: Compose can be used throughout the development cycle for testing, staging, and production.

The docker-compose.yml File

The docker-compose.yml file is the core of Docker Compose. It defines all the components and configurations of your application. Here’s a basic example:

version: '3.8'
services:
 web:
 build: .
 ports:
   - "5000:5000"
 volumes:
   - .:/code
 environment:
   FLASK_ENV: development
 redis:
   image: "redis:alpine"

Let’s break down this example:

  • version: Specifies the Compose file format version.
  • services: Defines the containers that make up your app.
  • web: A service based on an image built from the Dockerfile in the current directory.
  • redis: A service using the public Redis image.

Key Concepts in Docker Compose

  1. Services: Containers that make up your application.
  2. Networks: How your services communicate with each other.
  3. Volumes: Where your services store and access data.

Basic Docker Compose Commands

  • docker compose up: Create and start containers
docker compose up -d # Run in detached mode
  • docker compose down: Stop and remove containers, networks, images, and volumes
docker compose down --volumes # Also remove volumes
  • docker compose ps: List containers
  • docker compose logs: View output from containers
docker compose logs -f web # Follow logs for the web service

Advanced Docker Compose Features

1. Environment Variables

You can use .env files or set them directly in the compose file:

version: '3.8'
services:
 web:
   image: "webapp:${TAG}"
   environment:
     - DEBUG=1

2. Extending Services

Use extends to share common configurations:

version: '3.8'
services:
 web:
   extends:
     file: common-services.yml
     service: webapp

3. Healthchecks

Ensure services are ready before starting dependent services:

version: '3.8'
services:
 web:
   image: "webapp:latest"
   healthcheck:
     test: ["CMD", "curl", "-f", "http://localhost"]
     interval: 1m30s
     timeout: 10s
     retries: 3
     start_period: 40s

Practical Examples

Example 1: WordPress with MySQL

version: '3.8'
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress

wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress

volumes:
db_data: {}

Let’s break this down in detail:

1. Version: version: ‘3.8’ specifies the version of the Compose file format. Version 3.8 is compatible with Docker Engine 19.03.0+.

2. Services: We define two services: db and wordpress.

2.1 db service:

  • image: mysql:5.7: Uses the official MySQL 5.7 image.
  • volumes: Creates a named volume db_data and mounts it to /var/lib/mysql in the container. This ensures that the database data persists even if the container is removed.
  • restart: always: Ensures that the container always restarts if it stops.
  • environment: Sets up the MySQL environment variables:
  • MYSQL_ROOT_PASSWORD: Sets the root password for MySQL.
  • MYSQL_DATABASE: Creates a database named “wordpress”.
  • MYSQL_USER and MYSQL_PASSWORD: Creates a new user with the specified password.

2.2 wordpress service:

  • depends_on: Ensures that the db service is started before the wordpress service.
  • image: wordpress:latest: Uses the latest official WordPress image.
  • ports: Maps port 8000 on the host to port 80 in the container, where WordPress runs.
  • restart: always: Ensures the container always restarts if it stops.
  • environment: Sets up WordPress environment variables:
  • WORDPRESS_DB_HOST: Specifies the database host. Note the use of db:3306, where db is the service name of our MySQL container.
  • WORDPRESS_DB_USER, WORDPRESS_DB_PASSWORD, WORDPRESS_DB_NAME: These match the MySQL settings we defined in the db service.

3. Volumes: db_data: {}: This creates a named volume that Docker manages. It’s used to persist the MySQL data.

To run this setup:

  1. Save the above YAML in a file named docker-compose.yml.
  2. In the same directory, run docker compose up -d.
  3. Once the containers are running, you can access WordPress by navigating to http://localhost:8000 in your web browser.

This setup provides a complete WordPress environment with a MySQL database, all configured and ready to use. The use of environment variables and volumes ensures that the setup is both flexible and persistent.

Example 2: Flask App with Redis and Nginx

version: '3.8'
services:
  flask:
    build: ./flask
    environment:
      - FLASK_ENV=development
    volumes:
      - ./flask:/code
   
  redis:
    image: "redis:alpine"
  
  nginx:
    image: "nginx:alpine"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    ports:
      - "80:80"
    depends_on:
      - flask

networks:
  frontend:
  backend:volumes:
  db-data:

Let’s break this down:

  1. Version: As before, we’re using version 3.8 of the Compose file format.
  2. Services: We define three services: flask, redis, and nginx.

2.2 flask service: build: ./flask: This tells Docker to build an image using the Dockerfile in the ./flask directory.

  • build: ./flask: This tells Docker to build an image using the Dockerfile in the ./flask directory.
  • environment: Sets FLASK_ENV=development, which enables debug mode in Flask.
  • volumes: Mounts the local ./flask directory to /code in the container. This is useful for development as it allows you to make changes to your code without rebuilding the container.

2.3 redis service:

  • image: “redis:alpine”: Uses the official Redis image based on Alpine Linux, which is lightweight.

2.4 nginx service:

  • image: “nginx:alpine”: Uses the official Nginx image based on Alpine Linux.
  • volumes: Mounts a local nginx.conf file to /etc/nginx/nginx.conf in the container. The :ro flag makes it read-only.
  • ports: Maps port 80 on the host to port 80 in the container.
  • depends_on: Ensures that the flask service is started before Nginx.

3. Networks: We define two networks: frontend and backend. This allows us to isolate our services. For example, we could put Nginx and Flask on the frontend network, and Flask and Redis on the backend network.

4. Volumes: db-data: This creates a named volume. Although it’s not used in this configuration, it’s available if we need persistent storage, perhaps for a database service we might add later.

To use this setup:

  1. You need a Flask application in a directory named flask, with a Dockerfile to build it.
  2. You need an nginx.conf file in the same directory as your dockercompose.yml.
  3. Run docker compose up -d to start the services.

This configuration sets up a Flask application server, with Redis available for caching or as a message broker, and Nginx as a reverse proxy. The Flask code is mounted as a volume, allowing for easy development. Nginx handles incoming requests and forwards them to the Flask application.

The use of Alpine-based images for Redis and Nginx helps to keep the overall image size small, which is beneficial for deployment and scaling.

This setup is particularly useful for developing and testing a Flask application in an environment that closely mimics production, with a proper web server (Nginx) in front of the application server (Flask) and a caching/messaging system (Redis) available.

Best Practices for Docker Compose

  1. Use version control for your docker-compose.yml file
  2. Keep development, staging, and production environments as similar as possible.
  3. Use build arguments and environment variables for flexibility.
  4. Leverage healthchecks to ensure service dependencies are met.
  5. Use .env files for environment-specific variables.
  6. Optimize your images to keep them small and efficient.
  7. Use docker-compose.override.yml for local development settings.

Scaling Services

Docker Compose can scale services with a single command:

docker compose up -d --scale web=3

This command would start 3 instances of the web service.

Networking in Docker Compose

By default, Compose sets up a single network for your app. Each container for a service joins the default network and is both reachable by other containers on that network, and discoverable by them at a hostname identical to the container name.

You can also specify custom networks:

version: '3.8'
services:
  web:
    networks:
      - frontend
      - backend
  db:
    networks:
      - backend
networks:
  frontend:
  backend:

Volumes in Docker Compose

Compose also lets you create named volumes that can be reused across multiple services:

version: '3.8'
services:
  db:
    image: postgres
    volumes:
      - data:/var/lib/postgresql/data
volumes:
  data:

Conclusion

In this blog, we’ve explored the power of Docker Compose, from its key benefits and essential concepts to advanced features and practical use cases. Whether you’re managing multi-container applications locally or at scale, Docker Compose simplifies your workflow and enhances productivity.

In the next part of this series, we’ll dive into Docker Security Best Practices, where we’ll discuss how to secure your containers, images, and deployments effectively.

Stay tuned, and if you found this guide helpful, don’t forget to follow and hit the 👏 button below. Happy coding! 🚀

Thank you for reading! 💚

Leave a Reply

Your email address will not be published. Required fields are marked *