Docker Compose Guide Part 3: Commands and Operations

The third part of our Docker Compose series explores essential commands and daily operations for managing multi-container applications effectively.

Docker Compose Guide Part 3: Commands and Operations

Table of Contents

Docker Compose Guide Part 3: Commands and Operations

Welcome to the third installment of our Docker Compose series! In Part 1, we covered the fundamentals of Docker Compose. In Part 2, we explored the anatomy of docker-compose.yml files.

Now, let’s dive into the practical side - the essential Docker Compose commands and operations that will help you manage your containerized applications efficiently on a day-to-day basis.

Docker Compose Command Basics

Before we explore specific commands, let’s understand the general structure of Docker Compose commands:

docker-compose [OPTIONS] COMMAND [ARGS]

The most common options include:

  • -f, --file FILE: Specify an alternate compose file (default: docker-compose.yml)
  • -p, --project-name NAME: Specify an alternate project name (default: directory name)
  • --profile PROFILE: Specify a profile to enable

Let’s dive into the most useful commands for daily operations:

Managing Application Lifecycle

Starting Services

The most basic command to start your application is:

docker-compose up

This command creates and starts all services defined in your docker-compose.yml file. Some useful flags include:

  • -d, --detach: Run containers in the background
  • --build: Build images before starting containers
  • --no-deps: Don’t start linked services
  • --force-recreate: Recreate containers even if their configuration hasn’t changed

Example: Start your application in detached mode with rebuilt images:

docker-compose up -d --build

Stopping Services

To stop your application:

docker-compose down

This command stops and removes containers, networks, and the default volume. Additional flags include:

  • -v, --volumes: Remove named volumes declared in the volumes section
  • --rmi TYPE: Remove images, where TYPE can be all or local
  • --remove-orphans: Remove containers for services not defined in the compose file

Example: Stop your application and remove volumes and local images:

docker-compose down -v --rmi local

Restarting Services

To restart all services or specific ones:

# Restart all services
docker-compose restart

# Restart specific services
docker-compose restart web db

You can specify a timeout (in seconds) before killing the container:

docker-compose restart --timeout 30 web

Viewing Application Status

Checking Running Services

To see what’s currently running:

docker-compose ps

This displays all containers and their status, ports, and command. If you want to see all containers (including stopped ones), use:

docker-compose ps -a

Viewing Logs

One of the most useful commands for troubleshooting is:

docker-compose logs

This shows the logs from all services. To make it more useful:

  • -f, --follow: Follow log output
  • --tail N: Show only the last N lines
  • SERVICE: Specify service name(s) to view only those logs

Example: Follow the last 100 lines of logs from the web service:

docker-compose logs -f --tail=100 web

This is incredibly useful for debugging issues in real-time.

Checking Resource Usage

To monitor resource usage (CPU, memory) of your containers:

docker-compose top

Managing Individual Services

Building Services

To build or rebuild services without starting them:

docker-compose build

Useful flags include:

  • --no-cache: Don’t use cache when building the image
  • --pull: Always attempt to pull a newer version of the image
  • SERVICE: Specify service name(s) to build

Example: Rebuild the API service without using cache:

docker-compose build --no-cache api

Starting Specific Services

You can start specific services instead of the entire stack:

docker-compose up -d db redis

This is useful when you only need certain components of your application.

Stopping Specific Services

Similarly, you can stop specific services:

docker-compose stop web

This stops the containers without removing them.

Removing Services

To remove stopped service containers:

docker-compose rm

Use the -f flag to force removal of running containers.

Running Commands in Containers

One-off Commands

To run a one-off command in a service container:

docker-compose run --rm SERVICE COMMAND

The --rm flag removes the container after the command completes.

Examples:

# Run database migrations
docker-compose run --rm backend python manage.py migrate

# Open a shell in the frontend container
docker-compose run --rm frontend bash

# Run tests
docker-compose run --rm backend npm test

Executing Commands in Running Containers

To execute a command in an already running container:

docker-compose exec SERVICE COMMAND

Examples:

# Check the environment variables in a running container
docker-compose exec web env

# Get an interactive shell
docker-compose exec db psql -U postgres

# View container files
docker-compose exec app ls -la /app

The difference between run and exec is important:

  • run creates a new container to run the command
  • exec runs the command in an already running container

Scaling Services

To run multiple instances of a service:

docker-compose up -d --scale SERVICE=NUM

Example: Start 3 instances of the worker service:

docker-compose up -d --scale worker=3

Note that to scale services, you need to:

  1. Either use different host ports for each container
  2. Or let Docker Compose assign random host ports (by specifying only the container port)

For example:

services:
  web:
    image: nginx
    ports:
      - "80" # Docker will assign random host ports

Configuration and Environment Management

Validating Compose Files

Before deploying changes, it’s good practice to validate your compose file:

docker-compose config

This also shows you the resolved configuration with all variables interpolated.

To check for errors only:

docker-compose config --quiet

Environment File

Docker Compose automatically reads variables from a .env file in the same directory. Use this for storing environment-specific settings:

# .env example
POSTGRES_PASSWORD=secret123
APP_ENV=development
EXTERNAL_PORT=8080

Then reference these in your compose file:

services:
  web:
    ports:
      - "${EXTERNAL_PORT}:80"
    environment:
      - APP_ENV=${APP_ENV}

Using Multiple Compose Files

For different environments (development, staging, production), you can extend a base configuration using multiple compose files:

docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

The second file will override values from the first. This allows for a DRY (Don’t Repeat Yourself) approach:

  • docker-compose.yml: Base configuration
  • docker-compose.override.yml: Development overrides (loaded automatically)
  • docker-compose.prod.yml: Production overrides

Advanced Operations

Watching Network Traffic

To check which ports are being used by your services:

docker-compose port SERVICE PRIVATE_PORT

Example:

docker-compose port web 80

This outputs the host port mapped to port 80 of the web service.

Force-recreating Containers

If you make changes to your environment variables or other configurations:

docker-compose up -d --force-recreate

This ensures fresh containers with the latest configuration.

Cleaning Up

To remove all stopped containers, unused networks, and dangling images:

docker system prune

Add -a and --volumes to also remove unused images and volumes (use with caution).

Real-world Examples

Let’s look at some common practical use cases for Docker Compose commands:

Developer Workflow Example

A typical development workflow might look like this:

# Start the development environment
docker-compose up -d

# View logs of a specific service
docker-compose logs -f app

# Run tests in the test container
docker-compose run --rm test npm run test

# Make code changes locally...

# Rebuild and restart the affected service
docker-compose up -d --build app

# When done, shut down the environment
docker-compose down

Database Operations Example

Working with databases is a common task:

# Start just the database
docker-compose up -d db

# Run a database migration
docker-compose run --rm app ./manage.py migrate

# Connect to the database with a client
docker-compose exec db psql -U postgres

# Create a database backup
docker-compose exec db pg_dump -U postgres > backup.sql

# Restore from backup
cat backup.sql | docker-compose exec -T db psql -U postgres

The -T flag in the last command disables pseudo-TTY allocation, which is necessary when piping input to the container.

Debugging Example

When things go wrong, these commands help diagnose issues:

# Check container status
docker-compose ps

# See container logs
docker-compose logs -f --tail=100

# Check resource usage
docker-compose top

# Get a shell in the problematic container
docker-compose exec app bash

# Inspect network settings
docker network inspect docker-compose_default

Best Practices for Working with Docker Compose

Based on the commands we’ve explored, here are some best practices:

1. Use the --build flag when changes are made

Always use --build when you’ve made changes to your Dockerfile or source code:

docker-compose up -d --build

2. Use profiles for optional services

If you have services that aren’t always needed (like monitoring tools), use profiles:

services:
  app:
    image: myapp

  monitoring:
    image: prometheus
    profiles:
      - monitoring

Then start only when needed:

docker-compose --profile monitoring up -d

3. Use .env files for environment-specific variables

Keep secrets and environment-specific configuration in .env files, which should not be committed to version control.

4. Use healthchecks for service dependencies

Ensure services wait for dependencies properly:

services:
  app:
    depends_on:
      db:
        condition: service_healthy

  db:
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

5. Clean up regularly

Prevent resource exhaustion by cleaning up unused resources:

docker-compose down -v
docker system prune

6. Use version control for your compose files

Track changes to your compose files in git, making it easier to roll back changes if needed.

Troubleshooting Common Issues

Containers exit immediately

If your containers exit immediately after starting:

  1. Check logs: docker-compose logs SERVICE
  2. Make sure your service has a running process (e.g., containers need a foreground process)
  3. Verify the command in your Dockerfile or compose file

Port conflicts

If you see errors like “port is already allocated”:

  1. Check what’s using the port: lsof -i :PORT
  2. Either stop the conflicting service or use a different port in your compose file

Network issues between containers

If containers can’t communicate:

  1. Verify they’re on the same network: docker network inspect NETWORK
  2. Try pinging between containers: docker-compose exec service1 ping service2
  3. Check service names are used correctly as hostnames

Volume permission issues

If you see permission denied errors with volumes:

  1. Check ownership and permissions of the host directory
  2. Consider running the container process with the same UID/GID as the host user
  3. For development, you might need to make host directories world-writable (use with caution)

Frequently Asked Questions

How do I update a single service without affecting others?

Use:

docker-compose up -d --no-deps --build SERVICE

The --no-deps flag ensures dependent services aren’t also restarted.

Can I run Docker Compose in production?

While Docker Compose can be used in production for simple setups, tools like Docker Swarm or Kubernetes are generally recommended for production deployments. That said, many companies successfully use Compose in production for smaller applications.

How can I view variables that will be used in my compose file?

Use:

docker-compose config

This shows the interpolated compose file with all variables resolved.

How can I limit resources for my services?

Use resource constraints in your compose file:

services:
  app:
    image: myapp
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: 512M
        reservations:
          cpus: "0.25"
          memory: 256M

Docker Compose in CI/CD Pipelines

Docker Compose is also valuable in Continuous Integration and Continuous Deployment pipelines:

# Example GitLab CI/CD pipeline using Docker Compose
stages:
  - test
  - build

test:
  stage: test
  script:
    - docker-compose -f docker-compose.test.yml up --build --exit-code-from tests
    - docker-compose -f docker-compose.test.yml down

build:
  stage: build
  script:
    - docker-compose build
    - docker-compose push

The --exit-code-from SERVICE flag makes the up command return the exit code from a specific service, which is perfect for test runners.

Next Steps

In this article, we’ve explored the essential Docker Compose commands for managing your containerized applications, from basic lifecycle management to advanced operations and troubleshooting.

In Part 4, our final installment, we’ll dive into advanced Docker Compose topics, including using Docker Compose with Swarm mode, creating production-ready configurations, implementing CI/CD pipelines, and optimizing your Docker Compose workflows.


Continue to Part 4: Advanced Topics and Production Deployment or go back to Part 2: Anatomy of docker-compose.yml Files

Table of Contents