How to Install Docker Compose on AlmaLinux 9

Install Docker Compose on AlmaLinux 9 with Docker Engine, the Compose plugin, a test stack, verification commands, and common VPS fixes in minutes.
A polished 3D server rack with glowing container blocks and a subtle orange accent on a midnight-blue background.

Install Docker Compose on AlmaLinux 9 with Docker Engine and the maintained Compose CLI plugin. In roughly ten minutes, you will start a two-service test project, check its health, and keep its web port private.

Prerequisites

  • A 64-bit AlmaLinux 9 VPS with at least 2 GB of RAM and 10 GB of free disk space.
  • SSH access as root or as a normal Linux account with working sudo privileges.
  • Outbound HTTPS access to Docker’s package repository and container registry.
  • A VPSLake Linux VPS if you need an AlmaLinux server with root access.
  • No domain is required for the private test project. Add a domain later when you put a real application behind a TLS reverse proxy.

AlmaLinux 9 uses DNF and firewalld. The commands below install Docker’s RHEL-compatible packages; if you are already using Podman or another container runtime, check its workloads before changing packages.

Step 1: Confirm the release and update packages

Checking the release avoids adding an Enterprise Linux 8 repository to an AlmaLinux 9 machine.

~~~bash . /etc/os-release printf ‘%s\n’ “$PRETTY_NAME” uname -m sudo dnf upgrade -y ~~~

The first two checks should identify AlmaLinux 9 and a 64-bit architecture:

~~~text AlmaLinux 9.x (Emerald Puma) x86_64 ~~~

The minor release and update count will vary. Resolve any DNS, mirror, or package-signature error before continuing.

Step 2: Add Docker’s RPM repository

The Docker repository provides the Engine, CLI, Buildx, and Compose plugin as packages that DNF can update together.

~~~bash sudo dnf install -y dnf-plugins-core ca-certificates sudo dnf config-manager –add-repo https://download.docker.com/linux/rhel/docker-ce.repo sudo dnf makecache dnf repolist –enabled | grep -i docker ~~~

You should see Docker’s stable repository in the final output. These are the repository steps from Docker’s RHEL installation guide, using AlmaLinux 9’s RHEL-compatible package interface.

Step 3: Install Docker Engine and Compose

Installing the plugin with the Engine gives the Docker CLI a compose subcommand and avoids the unsupported legacy standalone executable.

~~~bash sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo systemctl enable –now docker sudo systemctl is-active docker sudo docker version –format ‘Client {{.Client.Version}} / Server {{.Server.Version}}’ sudo docker compose version ~~~

The service should report active, followed by version strings. Exact versions change as Docker publishes updates:

~~~text active Client 29.x / Server 29.x Docker Compose version vX.Y.Z ~~~

Run Docker’s small smoke test. It downloads a public image, starts a temporary container, prints a message, and removes the container when it exits.

~~~bash sudo docker run –rm hello-world ~~~

Near the end, expect output containing:

~~~text Hello from Docker! Your installation appears to be working correctly. ~~~

Step 4: Allow your SSH account to use Docker

The Docker socket is root-owned by default, so add only the trusted account that will manage containers instead of making the socket broadly writable.

Replace YOUR_LINUX_USER with the username used for SSH:

~~~bash sudo usermod -aG docker YOUR_LINUX_USER ~~~

Log out and reconnect so the new group membership is loaded, then check it:

~~~bash id -nG docker info –format ‘Server {{.ServerVersion}}’ ~~~

The group list should include docker, and docker info should print the Engine version. The Docker group grants root-equivalent control over the host, so add only accounts you trust. If you prefer not to use the group, keep the sudo prefix on later Docker commands.

Step 5: Create a small Compose project

This project pairs Caddy, which serves a local page, with Redis, which stays on Compose’s private network. The loopback binding lets you test the result without exposing port 8090 to the internet.

~~~bash mkdir -p ~/compose-alma9/site cd ~/compose-alma9 printf ‘%s\n’ ‘AlmaLinux 9 Docker Compose is working.’ > site/index.html ~~~

Create the Caddy configuration:

~~~bash tee Caddyfile >/dev/null <<‘EOF’ :8090 { root * /srv file_server } EOF ~~~

Create compose.yaml:

~~~bash tee compose.yaml >/dev/null <<‘EOF’ services: web: image: caddy:2-alpine restart: unless-stopped ports:

  • “127.0.0.1:8090:8090” volumes:
  • ./Caddyfile:/etc/caddy/Caddyfile:ro,Z
  • ./site:/srv:ro,Z

cache: image: redis:7-alpine restart: unless-stopped command: [“redis-server”, “–save”, “”, “–appendonly”, “no”] healthcheck: test: [“CMD”, “redis-cli”, “ping”] interval: 5s timeout: 3s retries: 10 EOF ~~~

The :Z mount option assigns SELinux labels suitable for private container mounts. Redis has no published port, and the sample disables persistence because it is only an installation test; use a password, persistent storage, and a backup plan for a real cache or database. The service and volume syntax follows Docker’s Compose file reference.

Step 6: Render and start the services

Rendering the file before startup catches YAML and interpolation errors without creating containers.

~~~bash docker compose config –quiet docker compose pull docker compose up -d docker compose ps ~~~

The first command is silent when the file is valid. After the images download, the table should show a running web service and a healthy cache:

~~~text NAME SERVICE STATUS PORTS compose-alma9-web-1 web Up 127.0.0.1:8090->8090/tcp compose-alma9-cache-1 cache Up (healthy) ~~~

Container names may differ if you choose another directory or set a Compose project name.

Verify Docker Compose on AlmaLinux 9

Use one HTTP request, one Redis health check, and a short log view to prove that the plugin can control the project and that both services are responding.

~~~bash curl –fail –silent http://127.0.0.1:8090 docker compose exec -T cache redis-cli ping docker compose logs –tail=20 web cache ~~~

The first two commands should return:

~~~text AlmaLinux 9 Docker Compose is working. PONG ~~~

Because Caddy listens only on loopback, a remote browser cannot reach it directly. To view it from your workstation, create an SSH tunnel with ssh -L 8090:127.0.0.1:8090 USER@SERVER_IP, then open http://127.0.0.1:8090 locally.

Troubleshooting

docker: ‘compose’ is not a docker command

The CLI plugin is missing, or Docker packages came from a different repository. Refresh the metadata and install the plugin from Docker’s repository:

~~~bash sudo dnf makecache sudo dnf install -y docker-compose-plugin docker compose version ~~~

Use docker compose with a space. The hyphenated docker-compose command is the older standalone tool and is not needed here.

No match for argument: docker-ce

DNF cannot see the Docker repository or could not refresh its metadata. Confirm the repository and rebuild the cache:

~~~bash sudo dnf repolist –enabled | grep -i docker sudo dnf makecache dnf list –showduplicates docker-ce ~~~

If the first command returns nothing, repeat Step 2. A listed package with an el9 suffix confirms that DNF is reading the AlmaLinux 9-compatible package stream.

permission denied while trying to connect to the Docker daemon socket

Your current SSH session has not loaded the docker group, or usermod was run for another account. Reconnect, run id -nG, and retry. sudo docker compose … is a temporary workaround while you correct the group membership.

Caddy returns 403 Forbidden or cannot read /srv

SELinux is blocking an unlabeled bind mount. Confirm the Compose file has :Z on both bind mounts, then recreate the web container so Docker applies the label:

~~~bash docker compose up -d –force-recreate web docker compose logs –tail=30 web ~~~

Keep SELinux enabled; do not solve a mount-label problem by disabling it.

Hardening

  • Keep test services bound to 127.0.0.1. If you later publish a web application through a TLS reverse proxy, allow only the required HTTP and HTTPS services in firewalld:

~~~bash sudo firewall-cmd –permanent –add-service=http sudo firewall-cmd –permanent –add-service=https sudo firewall-cmd –reload ~~~

  • Do not publish Redis or the Docker API to the public network. Use Compose’s private network for service-to-service traffic.
  • Keep the docker group limited to trusted administrators, store secrets outside a public repository, and pin production images to reviewed tags or digests.
  • Back up application data before using docker compose down -v; the -v option removes named volumes.

FAQ

Can I install the Compose plugin without Docker Engine?

You can install the package, but Compose needs the Docker CLI and a reachable Docker Engine to start containers. Installing the Engine and plugin together avoids a partial setup and makes docker compose version immediately useful.

Is docker-compose different from docker compose?

Yes. docker compose is the current Docker CLI plugin and is distributed by Docker’s RPM repository. docker-compose is the legacy standalone command, so new AlmaLinux 9 projects should use the space-separated form.

Does the sample require a firewalld rule for port 8090?

No. The sample maps port 8090 to 127.0.0.1, so it is reachable only from the server itself or through an SSH tunnel. If you bind to all interfaces, use a reverse proxy with TLS and add the corresponding firewalld rule instead of exposing an unprotected test service.

Will the Compose services restart after a reboot?

Yes, Docker is enabled as a system service and both sample services use restart: unless-stopped. They will return after a normal reboot unless you intentionally stopped them or removed the project.

Samrat Ghosh
Written by

Samrat Ghosh

Founder & Infrastructure Engineer

Samrat Ghosh is the founder of VPSLake, where he builds and runs the remote desktop and VPS hosting infrastructure the company is built on. He writes hands-on guides about RDP, Windows Server, VPS management and secure remote access - the practical documentation he wishes had…

Previous Article

How to Upload a Minecraft World to Your Server

Next Article

MT4 EA Not Working on VPS? 9 Fixes

Write a Comment

Leave a Comment

Your email address will not be published. Required fields are marked *

Subscribe to our Newsletter

Subscribe to our email newsletter to get the latest posts delivered right to your email.
Pure inspiration, zero spam ✨