How to Install Docker on Rocky Linux 8 (Step-by-Step)

Install Docker on Rocky Linux 8 with the official RPM repository, Compose v2, SELinux-safe mounts, non-root access, and a verified container.
A polished 3D server rack with glowing container blocks and a restrained green accent on a midnight-blue background.

Install Docker on Rocky Linux 8 from Docker’s official RPM repository, then run a real container to verify the daemon and networking. The setup takes about ten minutes on a fresh VPS and keeps the test service private to the server.

Prerequisites

  • A 64-bit Rocky Linux 8 VPS updated to the current 8.10 minor release, with at least 2 GB of RAM and 10 GB of free disk space.
  • SSH access as root, or a regular Linux account with working sudo privileges.
  • Outbound HTTPS access to Docker’s package repository and Docker Hub.
  • No domain is needed for installation or the local test. You need one later if a public web application will use HTTPS.
  • A VPSLake Linux VPS if you need a Rocky Linux server with root access for containers.

These commands use sudo for a regular account. If you are logged in as root, omit sudo. Rocky Linux 8 uses DNF and firewalld, not Debian’s APT and UFW.

Step 1: Check the release and update Rocky Linux

Confirming the release before adding a repository prevents DNF from selecting packages for another Enterprise Linux generation.

~~~bash grep -E ‘^(PRETTY_NAME|VERSION_ID|ID_LIKE)=’ /etc/os-release uname -m sudo dnf upgrade -y ~~~

On a fully updated host, the release should identify Rocky Linux 8.10 and the architecture will commonly be x86_64:

~~~text PRETTY_NAME=”Rocky Linux 8.10 (Green Obsidian)” VERSION_ID=”8.10″ ID_LIKE=”rhel fedora” x86_64 ~~~

If DNF reports a mirror, DNS, or GPG-signature problem, resolve it before installing Docker. Rocky Linux 8 remains in security maintenance, but staying on the latest 8.x minor release matters.

Step 2: Remove packages that conflict with Docker Engine

Rocky Linux can have Podman, runc, or distribution Docker packages installed already. Removing unused conflicting packages lets Docker install its matching containerd runtime.

On a new VPS, run:

~~~bash sudo dnf remove -y docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine \ podman \ runc ~~~

DNF may say that some packages are not installed; that is expected. On an existing server, check which services use Podman or runc before removing them. This command does not erase Docker data under /var/lib/docker/.

Step 3: Add Docker’s RHEL-compatible repository

Rocky Linux provides the RHEL-compatible package interfaces Docker targets, so use Docker’s RHEL repository instead of the old CentOS repository URL.

Install the DNF repository plugin and certificate tools, add the signed repository, and refresh metadata:

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

Check that DNF can see Rocky Linux 8-compatible Engine builds before installing:

~~~bash dnf list docker-ce –showduplicates | sort -r | head -n 6 ~~~

The list should include docker-ce-stable entries with an el8 suffix. Docker documents the repository and package method in its RHEL installation guide.

Step 4: Install Docker Engine and Compose v2

Installing the daemon, runtime, Buildx, and Compose plugin together keeps the Docker command-line tools aligned.

~~~bash sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo systemctl enable –now docker ~~~

Check both boot startup and the client-to-daemon connection:

~~~bash sudo systemctl is-active docker sudo systemctl is-enabled docker sudo docker version –format ‘Client {{.Client.Version}} / Server {{.Server.Version}}’ sudo docker compose version ~~~

Expected output has an active, enabled service and version strings. Docker’s patch numbers change as new releases arrive:

~~~text active enabled Client 29.x.y / Server 29.x.y Docker Compose version v2.x.y ~~~

Step 5: Run the built-in installation test

The hello-world image checks image download, daemon communication, container creation, and process startup in one short command.

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

Near the end, look for:

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

The container exits after printing its message. --rm removes the stopped container record, although the downloaded image remains in the local cache.

Step 6: Give one account non-root Docker access

Docker’s Unix socket is root-owned by default. The docker group avoids repeated sudo, but every member has effectively administrative control of the VPS.

Run this as the regular account that should operate containers, replacing the placeholder with that account’s Linux username:

~~~bash sudo groupadd –force docker sudo usermod -aG docker YOUR_LINUX_USER ~~~

Log out of SSH completely and reconnect so the group membership is loaded. Then test the socket without sudo:

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

The group list should contain docker, and the second command should print the Engine version. newgrp docker can refresh the current shell, but a new SSH session is less error-prone.

Step 7: Test a container without opening a public port

This disposable NGINX container checks Docker’s port publishing while binding only to loopback, so it needs no DNS record or firewalld rule.

~~~bash docker run -d –name rocky8-docker-check –publish 127.0.0.1:18083:80 nginx:alpine docker ps –filter name=rocky8-docker-check –format ‘table {{.Names}}\t{{.Status}}\t{{.Ports}}’ curl –head –max-time 10 http://127.0.0.1:18083 ~~~

You should see a running container and an HTTP success response:

~~~text NAMES STATUS PORTS rocky8-docker-check Up … 127.0.0.1:18083->80/tcp HTTP/1.1 200 OK ~~~

Remove the test container after confirming the response:

~~~bash docker stop rocky8-docker-check docker rm rocky8-docker-check ~~~

Verify Docker on Rocky Linux 8

Run this final check as the Docker administrator to confirm service startup, daemon access, Compose, and local storage:

~~~bash systemctl is-active docker systemctl is-enabled docker docker version –format ‘Client {{.Client.Version}} / Server {{.Server.Version}}’ docker compose version docker info –format ‘Containers {{.Containers}} | Images {{.Images}} | Storage {{.Driver}}’ ~~~

Successful output includes active, enabled, Docker client/server versions, Compose v2, and commonly overlay2:

~~~text active enabled Client 29.x.y / Server 29.x.y Docker Compose version v2.x.y Containers 0 | Images 2 | Storage overlay2 ~~~

For published-port behavior and Docker’s interaction with host filtering, read the official packet-filtering and firewall guidance.

Troubleshooting

No match for argument: docker-ce

DNF has not loaded Docker’s repository, or the host is not on Rocky Linux 8. Check the release, repository list, and metadata:

~~~bash grep -E ‘^(PRETTY_NAME|VERSION_ID)=’ /etc/os-release sudo dnf repolist –enabled | grep -i docker sudo dnf makecache dnf list docker-ce –showduplicates ~~~

The enabled list should include Docker’s stable repository and the package list should show an el8 build. If the repository is absent, repeat the dnf config-manager --add-repo command from Step 3.

package ... conflicts with ... during installation

Podman, runc, or an older Docker package still owns a file required by Docker’s package set. On a fresh VPS, rerun Step 2; on a production host, identify the dependent service before removing a runtime.

permission denied while trying to connect to the Docker daemon socket

The SSH session started before the account joined the docker group, or usermod targeted the wrong account. Reconnect, run id -nG, and retry. sudo docker info is a temporary diagnostic, not the non-root fix.

curl: (7) Failed to connect or bind: address already in use

The image may still be downloading, the test container may have stopped, or another process may own port 18083. Inspect the container and listener:

~~~bash docker ps -a –filter name=rocky8-docker-check docker logs rocky8-docker-check sudo ss -ltnp | grep ‘:18083’ ~~~

If the port is occupied, remove the failed test and repeat it with --publish 127.0.0.1:28083:80, then request http://127.0.0.1:28083. A loopback binding does not need a public firewall rule.

Hardening

  • Keep Docker’s API on its Unix socket. Never expose an unauthenticated Docker API on TCP port 2375; remote administration requires deliberate TLS configuration and narrow source-IP rules.
  • Treat membership in the docker group as root-equivalent access. Use rootless Docker when you need a stronger privilege boundary.
  • Keep internal services on 127.0.0.1. For a public web service, open only HTTP and HTTPS in firewalld after confirming SSH access remains allowed:

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

  • Keep SELinux enabled. For a private bind mount, add Docker’s :Z label instead of disabling policy enforcement:

~~~bash docker run –rm -v /srv/app:/app:Z YOUR_IMAGE ~~~

Replace YOUR_IMAGE with the image you plan to run. Use :z only when the same host directory must be shared by multiple containers.

FAQ

Is Rocky Linux 8 still supported?

Rocky Linux 8 receives security maintenance through May 31, 2029. Keep the host on the latest 8.10 packages and plan a major-version migration before that maintenance window ends.

Why does this guide use the RHEL repository?

Docker’s official repository instructions target RHEL, while Rocky Linux supplies compatible Enterprise Linux package interfaces. The RHEL URL is therefore the current supported package path for this installation; the older CentOS URL in many legacy tutorials is not needed.

Can Docker and Podman run on the same Rocky server?

They can coexist in some configurations, but their runtimes and command names make a small VPS harder to reason about. Remove unused conflicting packages on a fresh host, and migrate active Podman workloads deliberately instead of uninstalling their runtime blindly.

Do I need to open port 18083 in firewalld?

No. The test publishes to 127.0.0.1, so only processes on the VPS can reach it. When you publish a real application publicly, open only its required port and use a domain with TLS for web traffic.

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

Best RDP for Ghana in 2026: Private, Cheap and Crypto-Friendly

Next Article

Monthly vs Yearly VPS Billing: Which Should You Choose?

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 ✨