How to Use Docker Compose for Local Development

Explains how to use Docker Compose to define and run multi-container Docker │ applications for local development, covering services, networks, volumes, and │ common commands, along with best practices for a streamlined workflow.

Beginner

Docker Compose is a powerful tool that simplifies the process of defining and running multi-container Docker applications for local development. It allows you to configure your application's services, networks, and volumes in a single docker-compose.yml file, enabling you to spin up your entire development environment with a single command.

Why Use Docker Compose for Local Development?

  • Simplified Setup: It streamlines the process of setting up complex development environments with multiple dependencies (e.g., a web app, database, and caching service).
  • Reproducible Environments: Ensures that your local environment closely mirrors production, reducing "it works on my machine" issues.
  • Dependency Management: Easily manage different versions of services (e.g., databases, message queues) without conflicts on your host machine.
  • Onboarding: New developers can quickly get a project running with a single command.

Core Concepts

  1. docker-compose.yml (or compose.yaml): This YAML file is the heart of Docker Compose. It defines the services, networks, and volumes that make up your application.
  2. Services: Each service in your docker-compose.yml represents a container. For example, you might have a web service for your application, a db service for your database, and a redis service for caching.
  3. Networks: Docker Compose sets up a single network for your application by default, allowing services to communicate with each other using their service names as hostnames.
  4. Volumes: Used for persistent data storage (e.g., database data) and for mounting local code into your containers, enabling live reloads during development.

Basic Workflow and Commands

  1. Define Services in docker-compose.yml: Create a docker-compose.yml file in your project's root directory.

    Here's a simple example for a Python Flask web application connected to a Redis cache:

    ```yaml

    docker-compose.yml

    version: '3.8' # Specifies the Compose file format version services: web: build: . # Build from a Dockerfile in the current directory ports: - "8000:5000" # Map host port 8000 to container port 5000 volumes: - .:/code # Mount the current directory into the container's /code directory environment: FLASK_ENV: development # Set environment variables REDIS_HOST: redis # Use service name 'redis' as hostname redis: image: "redis:alpine" # Use the official Redis image ports: - "6379:6379" # Map Redis port volumes: - redis_data:/data # Persist Redis data volumes: redis_data: # Define the named volume ```

  2. Build and Run Your Application: Navigate to your project directory in the terminal and run: bash docker compose up This command will build images (if necessary) and start all services defined in your docker-compose.yml file.

    To run services in the background (detached mode), use: bash docker compose up -d

  3. Stop and Remove Services: When you're done developing, stop and remove all containers, networks, and volumes created by Compose with: bash docker compose down Use docker compose down -v to also remove named volumes (this deletes persistent data).

  4. View Logs: To see the logs from your running services: bash docker compose logs [service_name] # Example: docker compose logs web # Use -f to follow logs in real-time: # docker compose logs -f web

  5. Execute Commands in a Service: You can run commands inside a running container using exec: bash docker compose exec web bash # This opens a bash shell inside the 'web' service container.

Best Practices for Local Development

  • Volume Mounting for Live Reloads: Mount your local code directory into the container (volumes: - .:/code) so code changes are reflected instantly without rebuilding the image.
  • Environment Variables: Use .env files to manage environment-specific variables (like API keys or database credentials) and keep them out of your docker-compose.yml file and version control.
  • Health Checks: Implement health checks in your docker-compose.yml to ensure services are ready before dependent services start.
  • Lightweight Images: Use minimal base images (like Alpine variants) and multi-stage builds to keep image sizes small.

By following these steps, you can effectively leverage Docker Compose to create consistent, isolated, and easily manageable local development environments for your multi-container applications.