Docker Compose lets you define and run multi-container applications from a single YAML file, eliminating the need to string together long docker run commands. On RHEL 8, you can install it either as a plugin that integrates directly with the docker CLI (the modern approach) or as a standalone binary for environments that require a specific version. This tutorial covers both methods, walks through writing a real-world docker-compose.yml for WordPress and MySQL, and shows you how to use an .env file to keep secrets out of version control.

Prerequisites

  • Docker CE installed and running on RHEL 8 (see How to Install Docker Engine on RHEL 8)
  • Root or sudo access, or membership in the docker group
  • curl available for the standalone binary method
  • Outbound internet access to reach GitHub releases or Docker’s package servers

Step 1 — Install Docker Compose as a Plugin

The recommended method for RHEL 8 is the official Docker Compose plugin, which ships in the same Docker CE repository you added when installing Docker Engine.

sudo dnf install -y docker-compose-plugin
docker compose version

The plugin integrates as a subcommand: docker compose (with a space) rather than docker-compose (with a hyphen). Confirm the output shows version 2.x or later before continuing.

Step 2 — (Alternative) Install the Standalone Binary

If you need a specific version or are on a system where the plugin is unavailable, download the standalone binary directly from GitHub releases. Replace v2.27.0 with the latest release tag from https://github.com/docker/compose/releases.

curl -SL https://github.com/docker/compose/releases/download/v2.27.0/docker-compose-linux-x86_64 
  -o /tmp/docker-compose
sudo chmod +x /tmp/docker-compose
sudo mv /tmp/docker-compose /usr/local/bin/docker-compose
docker-compose version

This binary is invoked as docker-compose with a hyphen and is independent of the Docker CLI plugin system.

Step 3 — Create an Environment File for Secrets

Store database credentials in a .env file rather than hard-coding them in the Compose YAML. Compose automatically reads this file from the same directory.

mkdir -p ~/wordpress-stack && cd ~/wordpress-stack
cat > .env << 'EOF'
MYSQL_ROOT_PASSWORD=StrongRootPass1!
MYSQL_DATABASE=wordpress
MYSQL_USER=wpuser
MYSQL_PASSWORD=StrongWpPass1!
EOF
chmod 600 .env

Setting chmod 600 ensures only the owner can read the credentials. Never commit this file to a public repository.

Step 4 — Write the docker-compose.yml

Create a docker-compose.yml that launches WordPress connected to a MySQL database, using named volumes so data persists across restarts.

cat > docker-compose.yml << 'EOF'
services:
  db:
    image: mysql:8.0
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql

  wordpress:
    image: wordpress:latest
    restart: always
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
      WORDPRESS_DB_USER: ${MYSQL_USER}
      WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
    depends_on:
      - db

volumes:
  db_data:
EOF

Step 5 — Start and Manage the Stack

Start both containers in detached mode, then use the standard Compose subcommands to inspect, follow logs, and stop the stack.

docker compose up -d
docker compose ps
docker compose logs -f wordpress
docker compose down

docker compose down stops and removes the containers but leaves the named volume db_data intact. Add --volumes to the down command only when you intentionally want to delete the database data.

Conclusion

You now have Docker Compose installed on RHEL 8 and a working multi-service stack definition that separates credentials into an environment file. The plugin method is preferred for day-to-day use because it stays in sync with Docker Engine updates via dnf, while the standalone binary is useful when you need strict version pinning in CI pipelines. As your stacks grow in complexity, consider splitting service definitions into override files (docker-compose.override.yml) to manage environment-specific differences without duplicating configuration.

Next steps: How to Use Docker Volumes and Bind Mounts on RHEL 8, How to Configure Docker Networking on RHEL 8, and How to Manage Docker Images and Containers on RHEL 8.