Docker Compose Installation and Configuration Guide for Ubuntu
Overview
Docker Compose is an official tool for defining and running multi-container Docker applications. With Compose, you use a YAML configuration file to define your application's services, networks, and volumes. Then, using a single command, you create and start all the services from your configuration.
This comprehensive guide walks you through installing and configuring Docker Compose on Ubuntu, from prerequisites to advanced configuration options.
Prerequisites
Before installing Docker Compose, ensure you have:
- Ubuntu System: Ubuntu 20.04, 22.04, 24.04, or newer
- Docker Engine: Docker must be installed and running
- Terminal Access: Root or sudo privileges
- System Requirements: At least 2GB RAM and 20GB disk space
- Internet Connection: For downloading packages and images
Important Note
Docker Compose V2 is the current version and uses the docker compose command (without hyphen). The older V1 version (docker-compose with hyphen) is now deprecated but still supported for backward compatibility.
Installation Methods
There are three primary methods to install Docker Compose on Ubuntu:
- Using Docker Repository (Recommended)
- Manual Binary Installation
- Docker Desktop (includes Docker Compose)
Method 1: Installation via Docker Repository (Recommended)
This is the most convenient and recommended method as it provides automatic updates through the system package manager.
Step 1: Update System Packages
First, update your package list to ensure you have access to the latest software versions:
Step 2: Install Required Dependencies
Install the necessary packages for adding third-party repositories:
sudo apt install -y \
ca-certificates \
curl \
gnupg \
lsb-release \
apt-transport-https \
software-properties-common
Package explanations:
ca-certificates: Certificate authority for verifying third-party identitiescurl: Command-line tool for data transfergnupg: GNU Privacy Guard for key managementlsb-release: Linux Standard Base version reporting utilityapt-transport-https: Enables HTTPS for apt repositoriessoftware-properties-common: Scripts for managing software repositories
Step 3: Add Docker's Official GPG Key
Create a directory for Docker's GPG key and download it:
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
Set appropriate permissions:
Step 4: Add Docker Repository
Add the Docker repository to your system's sources list:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Step 5: Update Package Index
After adding the repository, update the package index:
Step 6: Install Docker Compose Plugin
Install Docker Compose along with Docker Engine and related components:
Components installed:
docker-ce: Docker Community Edition enginedocker-ce-cli: Docker command-line interfacecontainerd.io: Container runtimedocker-compose-plugin: Docker Compose V2 plugin
Step 7: Verify Docker Installation
Check if Docker is running:
You should see output indicating that Docker is active and running.
Step 8: Verify Docker Compose Installation
Check the Docker Compose version:
Expected output:
Method 2: Manual Binary Installation
This method downloads the Docker Compose binary directly from GitHub. Use this if you need a specific version or cannot use the repository method.
Step 1: Determine Installation Location
For the current user only:
For all system users:
Step 2: Download Docker Compose Binary
For current user:
curl -SL https://github.com/docker/compose/releases/download/v2.40.0/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose
For all users:
sudo curl -SL https://github.com/docker/compose/releases/download/v2.40.0/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose
Latest Version
Replace v2.40.0 with the latest version number from the Docker Compose releases page.
Step 3: Apply Executable Permissions
For current user:
For all users:
Step 4: Verify Installation
Method 3: Legacy Installation (Standalone Binary)
Deprecated Method
This method installs the older docker-compose (with hyphen) command. Use only for backward compatibility with existing scripts.
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# Create symbolic link
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
# Verify
docker-compose --version
Post-Installation Configuration
Enable Docker Service
Ensure Docker starts automatically on system boot:
Manage Docker as Non-Root User
By default, Docker requires root privileges. To run Docker commands without sudo:
Step 1: Create Docker Group
The Docker group may already exist. Create it if needed:
Step 2: Add Your User to Docker Group
Step 3: Apply Group Changes
Log out and log back in, or run:
Step 4: Verify Non-Root Access
Test Docker without sudo:
Security Consideration
Adding users to the docker group grants root-level privileges. Only add trusted users to this group.
Test Docker Installation
Run a simple test to verify Docker is working:
If successful, you'll see a message confirming Docker is properly installed.
Understanding Docker Compose Files
Docker Compose uses YAML files to define multi-container applications. The default filename is compose.yaml (or compose.yml), though docker-compose.yaml and docker-compose.yml are also supported for backward compatibility.
Basic File Structure
version: '3.8'
services:
web:
image: nginx:latest
ports:
- "8080:80"
networks:
- app-network
volumes:
- ./html:/usr/share/nginx/html
db:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: example
POSTGRES_DB: myapp
volumes:
- db-data:/var/lib/postgresql/data
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
db-data:
Key Components Explained
Version
Specifies the Compose file format version:
Note
The version field is optional in newer versions of Docker Compose but recommended for compatibility.
Services
Each service represents a container. Services can be based on an image or built from a Dockerfile:
Using an existing image:
Building from a Dockerfile:
Ports
Map container ports to host ports:
Volumes
Define persistent storage:
Named volumes:
Bind mounts:
services:
web:
volumes:
- ./app:/app # relative path
- /host/path:/container/path # absolute path
- data-volume:/data:ro # read-only mount
Networks
Define custom networks for service communication:
Assign services to networks:
Environment Variables
Set environment variables in multiple ways:
Direct definition:
From a file:
Dependencies
Control service startup order:
Docker Compose File Examples
Example 1: Simple Web Application
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html:ro
restart: unless-stopped
Example 2: WordPress with MySQL
version: '3.8'
services:
wordpress:
image: wordpress:latest
ports:
- "8000:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress_password
WORDPRESS_DB_NAME: wordpress
volumes:
- wordpress-data:/var/www/html
depends_on:
- db
networks:
- wordpress-network
db:
image: mysql:8.0
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress_password
MYSQL_ROOT_PASSWORD: root_password
volumes:
- db-data:/var/lib/mysql
networks:
- wordpress-network
restart: unless-stopped
networks:
wordpress-network:
driver: bridge
volumes:
wordpress-data:
db-data:
Example 3: Multi-Container Application Stack
version: '3.8'
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
networks:
- app-network
depends_on:
- backend
environment:
- API_URL=http://backend:5000
backend:
build: ./backend
ports:
- "5000:5000"
networks:
- app-network
depends_on:
- database
- redis
environment:
- DATABASE_URL=postgresql://user:password@database:5432/appdb
- REDIS_URL=redis://redis:6379
database:
image: postgres:15-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: appdb
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- app-network
redis:
image: redis:alpine
networks:
- app-network
volumes:
- redis-data:/data
networks:
app-network:
driver: bridge
volumes:
postgres-data:
redis-data:
Example 4: Development Environment with Hot Reload
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- ./app:/app
- /app/node_modules
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- CHOKIDAR_USEPOLLING=true
command: npm run dev
Essential Docker Compose Commands
Starting Services
Start all services defined in compose.yaml:
Start in detached mode (background):
Build images before starting:
Recreate containers:
Stopping Services
Stop all running services:
Stop and remove containers:
Stop and remove containers, networks, volumes:
Remove all stopped containers:
Managing Services
List running services:
View logs:
Follow logs in real-time:
View logs for specific service:
Execute command in running container:
Run one-off command:
Building and Pulling
Build or rebuild services:
Build specific service:
Pull latest images:
Advanced Commands
Validate compose file:
Pause services:
Unpause services:
Restart services:
Scale services:
View resource usage:
Environment Variables Configuration
Using .env File
Create a .env file in the same directory as your compose.yaml:
Reference variables in compose.yaml:
version: '3.8'
services:
db:
image: postgres:${POSTGRES_VERSION}-alpine
environment:
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
web:
build: .
ports:
- "${APP_PORT}:5000"
environment:
NODE_ENV: ${NODE_ENV}
Variable Substitution Syntax
Basic substitution:
Default value if not set:
Required variable (fails if not set):
Using Multiple Environment Files
Specify a custom env file:
Environment Variable Precedence
From highest to lowest priority:
- Shell environment variables
- Variables from
--env-fileflag - Variables from default
.envfile - Default values in
compose.yaml
Best Practices for Environment Variables
Security Warning
Never commit .env files containing sensitive data to version control. Use .env.example with dummy values instead.
Create .env.example:
Add to .gitignore:
Working with Multiple Compose Files
You can use multiple compose files to organize configurations for different environments.
Base Configuration
docker-compose.yml:
Development Override
docker-compose.override.yml (automatically loaded):
version: '3.8'
services:
web:
volumes:
- ./app:/app
environment:
DEBUG: "true"
command: python manage.py runserver 0.0.0.0:8000
Production Configuration
docker-compose.prod.yml:
Using Specific Files
# Development (default)
docker compose up
# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up
# Testing
docker compose -f docker-compose.yml -f docker-compose.test.yml up
Advanced Configuration Topics
Resource Limits
Limit CPU and memory usage:
services:
web:
image: nginx
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
Health Checks
Define health check for services:
services:
web:
image: nginx
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Restart Policies
Control container restart behavior:
Available options:
no: Never restartalways: Always restarton-failure: Restart only on failureunless-stopped: Restart unless manually stopped
Logging Configuration
Configure logging drivers:
Security Options
Run containers with enhanced security:
services:
web:
image: nginx
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
user: "1000:1000"
Using Secrets (Docker Swarm)
Store sensitive data securely:
version: '3.8'
services:
db:
image: postgres
secrets:
- db_password
secrets:
db_password:
file: ./db_password.txt
Troubleshooting Common Issues
Issue 1: Command Not Found
Error:
Solutions:
- Verify installation:
- Check PATH configuration:
- Reinstall using repository method
Issue 2: Permission Denied
Error:
Solutions:
- Add user to docker group:
- Use sudo (temporary):
Issue 3: Port Already in Use
Error:
Solutions:
- Find process using the port:
- Stop the conflicting process or change port in compose.yaml
Issue 4: Cannot Find Configuration File
Error:
Solutions:
- Ensure you're in the correct directory:
- Specify file explicitly:
Issue 5: Volume Mount Permission Issues
Error:
Solutions:
- Check file ownership:
- Adjust permissions:
- Run container with matching user ID:
Issue 6: Service Won't Start
Debugging steps:
- View logs:
- Check service status:
- Inspect container:
- Validate compose file:
Issue 7: Network Connectivity Issues
Solutions:
- Inspect networks:
- Recreate networks:
- Verify DNS resolution:
Security Best Practices
1. Run Containers as Non-Root User
Or in Dockerfile:
2. Use Read-Only Filesystems
3. Disable Privilege Escalation
4. Limit Container Capabilities
5. Use Secrets for Sensitive Data
Never hardcode passwords in compose files. Use secrets or environment files:
services:
db:
image: postgres
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
6. Keep Images Updated
Regularly update base images:
7. Scan Images for Vulnerabilities
8. Use Network Segmentation
Isolate services with different networks:
services:
frontend:
networks:
- frontend-net
backend:
networks:
- frontend-net
- backend-net
database:
networks:
- backend-net
networks:
frontend-net:
backend-net:
Performance Optimization
1. Use BuildKit
Enable BuildKit for faster builds:
2. Optimize Image Layers
Use multi-stage builds in Dockerfiles:
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm install --production
CMD ["node", "dist/index.js"]
3. Use Volume Caching
For development, cache dependencies:
4. Parallel Service Startup
Docker Compose starts services in parallel by default. Use dependencies wisely:
Monitoring and Logging
View Real-Time Logs
# All services
docker compose logs -f
# Specific service
docker compose logs -f web
# Last 100 lines
docker compose logs --tail=100
Monitor Resource Usage
Export Logs
Updating Docker Compose
Update via Repository
Update Manual Installation
Download the latest version:
COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep 'tag_name' | cut -d\" -f4)
sudo curl -L "https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/lib/docker/cli-plugins/docker-compose
sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
Complete Practical Example
Let's create a complete full-stack application with Docker Compose.
Project Structure
my-app/
├── docker-compose.yml
├── .env
├── .env.example
├── .dockerignore
├── frontend/
│ ├── Dockerfile
│ ├── package.json
│ └── src/
├── backend/
│ ├── Dockerfile
│ ├── requirements.txt
│ └── app/
└── nginx/
├── Dockerfile
└── nginx.conf
docker-compose.yml
version: '3.8'
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: my-app-frontend
volumes:
- ./frontend/src:/app/src
networks:
- app-network
depends_on:
- backend
environment:
- REACT_APP_API_URL=http://backend:5000
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: my-app-backend
volumes:
- ./backend/app:/app
networks:
- app-network
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
environment:
- DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
- REDIS_URL=redis://redis:6379
- SECRET_KEY=${SECRET_KEY}
db:
image: postgres:15-alpine
container_name: my-app-database
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- app-network
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:alpine
container_name: my-app-redis
volumes:
- redis-data:/data
networks:
- app-network
command: redis-server --appendonly yes
nginx:
build:
context: ./nginx
dockerfile: Dockerfile
container_name: my-app-nginx
ports:
- "${NGINX_PORT:-80}:80"
- "${NGINX_SSL_PORT:-443}:443"
networks:
- app-network
depends_on:
- frontend
- backend
restart: unless-stopped
networks:
app-network:
driver: bridge
volumes:
postgres-data:
driver: local
redis-data:
driver: local
.env.example
# Database Configuration
POSTGRES_USER=myapp_user
POSTGRES_PASSWORD=change_this_password
POSTGRES_DB=myapp_db
# Application Configuration
SECRET_KEY=your_secret_key_here
NODE_ENV=development
# Ports
NGINX_PORT=80
NGINX_SSL_PORT=443
Usage Commands
# Copy environment file
cp .env.example .env
# Edit environment variables
nano .env
# Start all services
docker compose up -d
# View logs
docker compose logs -f
# Stop services
docker compose down
# Rebuild and restart
docker compose up -d --build
# Clean up everything
docker compose down --volumes --remove-orphans
Quick Reference Cheat Sheet
Common Commands
| Command | Description |
|---|---|
docker compose up | Start services |
docker compose up -d | Start in background |
docker compose down | Stop and remove containers |
docker compose ps | List running services |
docker compose logs | View logs |
docker compose logs -f | Follow logs |
docker compose exec <service> bash | Execute shell in container |
docker compose build | Build services |
docker compose pull | Pull latest images |
docker compose restart | Restart services |
docker compose stop | Stop services |
docker compose start | Start stopped services |
docker compose config | Validate compose file |
File Locations
| Location | Purpose |
|---|---|
compose.yaml | Primary compose file (preferred) |
docker-compose.yml | Legacy compose file name |
.env | Default environment variables |
.dockerignore | Exclude files from build context |
Environment Variable Syntax
| Syntax | Description |
|---|---|
${VAR} | Substitute variable |
${VAR:-default} | Use default if not set |
${VAR:?error} | Show error if not set |
${VAR-default} | Use default only if VAR doesn't exist |
Additional Resources
Official Documentation
Community Resources
Learning Materials
Conclusion
You now have a comprehensive understanding of installing and configuring Docker Compose on Ubuntu. This guide covered:
✅ Multiple installation methods (repository, manual, legacy) ✅ Post-installation configuration and user management ✅ Docker Compose file structure and syntax ✅ Essential commands and workflows ✅ Environment variable management ✅ Advanced configuration options ✅ Security best practices ✅ Troubleshooting common issues ✅ Performance optimization techniques ✅ Complete practical examples
With this knowledge, you can effectively manage multi-container applications using Docker Compose. Start with simple configurations and gradually incorporate advanced features as your requirements grow.
Remember to:
- Keep Docker and Docker Compose updated
- Follow security best practices
- Use version control for compose files (except .env)
- Document your configurations
- Monitor resource usage
- Regularly backup volumes and data
Happy containerizing!
Document Version: 1.0
Last Updated: October 2025
Compatibility: Ubuntu 20.04, 22.04, 24.04 and newer