How to Deploy Applications with Docker Compose on RHEL 7
Docker Compose lets you define and run multi-container applications using a single YAML file. Instead of managing individual docker run commands for each service, you describe the entire application stack — web server, database, cache, message queue — in a docker-compose.yml file and bring everything up with one command. This tutorial covers installing Docker Compose on RHEL 7, writing a realistic multi-service compose file, managing environment variables and secrets with .env files, using named volumes and custom networks, and operating the stack day-to-day with common Compose commands.
Prerequisites
- RHEL 7 with Docker CE installed and running
- Root or sudo access
- Internet access (or a local mirror) to pull Docker images
- At least 1 GB free RAM beyond what the OS needs
Step 1: Install Docker Compose
Docker Compose is not packaged in the RHEL 7 base or extras repos, so install the binary directly from GitHub:
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/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# Verify
docker-compose --version
If the server has no internet access, copy the binary from another machine or install via pip:
sudo yum install -y python3-pip
sudo pip3 install docker-compose
Step 2: Create the Project Directory
Compose treats each directory as a project. Create a structured directory for the application:
mkdir -p ~/myapp/{app,nginx,data/mysql,data/redis}
cd ~/myapp
Step 3: Create the .env File for Variables
Storing configuration values in a .env file keeps them out of the compose file and makes environment-specific overrides easy. Compose automatically reads .env from the same directory:
cat > .env <<'EOF'
# Application settings
APP_PORT=8080
APP_ENV=production
# MySQL
MYSQL_ROOT_PASSWORD=R00tSecurePass!
MYSQL_DATABASE=appdb
MYSQL_USER=appuser
MYSQL_PASSWORD=AppPass2024!
# Redis
REDIS_PASSWORD=RedisPass2024!
# Domain
DOMAIN=myapp.example.com
EOF
Set restrictive permissions so other users cannot read the credentials:
chmod 600 .env
Step 4: Write the docker-compose.yml
The following compose file defines four services: an Nginx web server, a Python/Node web application, a MySQL database, and a Redis cache. Services communicate over a dedicated internal network:
cat > docker-compose.yml <<'EOF'
version: "3.8"
services:
# --- Nginx reverse proxy / web server ---
nginx:
image: nginx:1.25-alpine
container_name: myapp_nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/certs:/etc/nginx/certs:ro
- static_files:/var/www/static:ro
networks:
- frontend
depends_on:
- app
# --- Application service ---
app:
image: myapp:latest
container_name: myapp_app
restart: unless-stopped
env_file:
- .env
environment:
- DB_HOST=db
- DB_PORT=3306
- DB_NAME=${MYSQL_DATABASE}
- DB_USER=${MYSQL_USER}
- DB_PASS=${MYSQL_PASSWORD}
- CACHE_HOST=cache
- CACHE_PORT=6379
- CACHE_PASS=${REDIS_PASSWORD}
- APP_ENV=${APP_ENV}
volumes:
- ./app:/app
- static_files:/app/static
networks:
- frontend
- backend
depends_on:
- db
- cache
# --- MySQL database ---
db:
image: mysql:8.0
container_name: myapp_db
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=${MYSQL_DATABASE}
- MYSQL_USER=${MYSQL_USER}
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
volumes:
- ./data/mysql:/var/lib/mysql
networks:
- backend
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost",
"-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 5
# --- Redis cache ---
cache:
image: redis:7-alpine
container_name: myapp_cache
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- ./data/redis:/data
networks:
- backend
volumes:
static_files:
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
EOF
Key points in this file:
- depends_on ensures the database and cache containers start before the application container, though it does not wait for the service inside them to become ready — use healthchecks for that.
- env_file loads all variables from
.envinto the container environment, while environment allows per-service overrides. - internal: true on the backend network prevents containers in that network from reaching the internet directly.
- Named volumes (e.g.,
static_files) are managed by Docker and persist acrossdocker-compose down. Bind mounts (e.g.,./data/mysql) are tied to specific host paths.
Step 5: Start the Stack
# Pull all images first (optional but useful to spot pull errors early)
docker-compose pull
# Start all services in detached mode
docker-compose up -d
Check that all containers are running:
docker-compose ps
Step 6: View Logs
Compose aggregates logs from all services, colour-coded by service name:
# All services
docker-compose logs -f
# Single service
docker-compose logs -f db
# Last 100 lines from the app
docker-compose logs --tail=100 app
Step 7: Scale Services
With Compose v3 and the deploy.replicas key (used primarily with Docker Swarm), or using the --scale flag for standalone Compose:
# Run 3 replicas of the app service
docker-compose up -d --scale app=3
Note that when scaling, the app service must not use a fixed container_name, and port mappings must not be static. Remove container_name and use a range or omit host-side port mapping for services you intend to scale.
Step 8: Common Day-to-Day Commands
# Stop all services (containers stopped, not removed)
docker-compose stop
# Stop and remove containers, networks (volumes preserved)
docker-compose down
# Stop, remove containers AND named volumes (data loss!)
docker-compose down -v
# Restart a single service after config change
docker-compose restart app
# Rebuild image and recreate container
docker-compose up -d --build app
# Execute a command inside a running container
docker-compose exec db mysql -u root -p
# View resource usage
docker-compose top
Step 9: Override Files for Multiple Environments
Compose supports multiple files merged at startup. Create a docker-compose.override.yml for development tweaks that should not appear in production:
cat > docker-compose.override.yml <<'EOF'
version: "3.8"
services:
app:
environment:
- APP_ENV=development
- DEBUG=true
ports:
- "8080:8080"
EOF
Compose automatically merges this file when you run docker-compose up. For a production deployment, specify only the base file:
docker-compose -f docker-compose.yml up -d
Conclusion
Docker Compose dramatically simplifies multi-service application management on RHEL 7. With a single docker-compose.yml you capture the entire runtime topology of your application — services, networks, volumes, and environment configuration — in a version-controllable text file. Combined with .env files for secrets, healthchecks for dependency readiness, and override files for environment-specific settings, Compose provides a robust foundation for deploying containerised applications on a single RHEL 7 host, and its compose file format transfers directly to Docker Swarm or Kubernetes when you need to scale beyond one machine.