Install Docker on Rocky Linux 10 from Docker’s RPM repository and finish with Compose v2, non-root CLI access, and a working container test. Set aside about ten minutes for a fresh VPS; the validation service stays on loopback until you deliberately publish an application.
Prerequisites
- A 64-bit Rocky Linux 10 VPS, normally
x86_64oraarch64, with at least 2 GB of RAM and 10 GB of free disk space. - SSH access as
root, or a regular account with workingsudoprivileges. - Outbound HTTPS access to Docker’s package repository and Docker Hub.
- No domain is required for installation or the local smoke test. You need a domain 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.
The commands use sudo for a normal account. If you are logged in as root, remove sudo. Rocky Linux uses DNF and firewalld, so do not substitute Debian’s APT or Ubuntu’s UFW commands.
Step 1: Confirm Rocky Linux 10 and update the VPS
Checking the release and CPU architecture first prevents you from installing packages for the wrong Enterprise Linux generation.
grep -E '^(PRETTY_NAME|VERSION_ID|ID_LIKE)=' /etc/os-release
uname -m
sudo dnf upgrade -y
sudo dnf install -y curl ca-certificates
The release should identify Rocky Linux 10, and a common VPS architecture is x86_64:
PRETTY_NAME="Rocky Linux 10.x (Linux-...)"
VERSION_ID="10.x"
ID_LIKE="rhel fedora"
x86_64
The minor version and upgrade count will vary. Resolve any DNS, mirror, or GPG-signature error before adding Docker’s repository.
Step 2: Remove packages that can conflict with Docker Engine
Rocky Linux may already contain Podman or a standalone runtime; removing those conflicting packages lets Docker install its matching containerd and networking components.
On a new VPS, run:
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 report that some names are not installed, which is harmless. On an existing host, inspect workloads before removing Podman or runc; uninstalling these packages does not automatically erase Docker data under /var/lib/docker/.
Step 3: Add Docker’s RHEL-compatible RPM repository
Docker’s repository supplies the Engine, CLI, containerd, Buildx, and Compose plugin as a coordinated package set for the RHEL 10 package interface used by Rocky Linux 10.
Install the repository-management plugin and add the repository:
sudo dnf install -y dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
sudo dnf makecache
Check that DNF can see Docker Engine builds before installing them:
dnf list docker-ce --showduplicates | sort -r | head -n 6
The list should contain entries from docker-ce-stable, with an el10 suffix on a Rocky Linux 10 system. Docker documents this repository procedure in its RHEL installation guide; Rocky’s compatible RPM tooling is why the method works here.
Step 4: Install Docker Engine and start it at boot
Installing the daemon and plugins together keeps the client, runtime, image builder, and Compose integration aligned.
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
Confirm that systemd started the daemon and that the CLI can reach it:
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 publishes updates independently of Rocky’s minor releases, so the numbers will change:
active
enabled
Client 29.x.y / Server 29.x.y
Docker Compose version v2.x.y
Step 5: Run Docker’s built-in installation test
The hello-world image exercises the complete path from the CLI to the daemon, registry, image cache, container creation, and process startup.
sudo docker run --rm hello-world
Near the end, look for the success message:
Hello from Docker!
Your installation appears to be working correctly.
The test container exits after printing its message. --rm removes the stopped container record, while the small image remains cached for future use.
Step 6: Give a dedicated account non-root Docker access
Docker’s Unix socket is root-owned by default; adding one trusted administrator to the docker group avoids repeated sudo while preserving the normal daemon setup.
Run this as the account that should manage containers, replacing the placeholder with its login name:
sudo groupadd --force docker
sudo usermod -aG docker YOUR_ADMIN_USER
Log out of SSH completely and reconnect as that user so the new group is loaded. Then test the socket without sudo:
id -nG
docker info --format 'Server {{.ServerVersion}}'
The group list should include docker, and the second command should print the Engine version. newgrp docker can load the group in the current shell, but a fresh SSH session is easier to reason about.
Treat the docker group as root-equivalent access: a member can start a privileged container and effectively control the VPS.
Step 7: Test container networking without opening a public port
A disposable NGINX container verifies port publishing while binding only to the VPS’s loopback address, so no DNS record or inbound firewall rule is needed.
docker run -d --name rocky10-docker-check --publish 127.0.0.1:18082:80 nginx:alpine
docker ps --filter name=rocky10-docker-check --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
curl --head --max-time 10 http://127.0.0.1:18082
You should see the container running and an HTTP success response:
NAMES STATUS PORTS
rocky10-docker-check Up ... 127.0.0.1:18082->80/tcp
HTTP/1.1 200 OK
Clean up the test after confirming the response:
docker stop rocky10-docker-check
docker rm rocky10-docker-check
Verify Docker on Rocky Linux 10
Run this final check as the Docker administrator to confirm boot startup, daemon communication, Compose, and the storage driver:
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, client/server versions, a Compose v2 version, and usually overlay2 as the storage driver:
active
enabled
Client 29.x.y / Server 29.x.y
Docker Compose version v2.x.y
Containers 0 | Images 2 | Storage overlay2
For the way published container ports interact with host packet filtering, read Docker’s firewall and packet-filtering guidance.
Troubleshooting
No match for argument: docker-ce
DNF has not loaded the Docker repository, or the host is not actually Rocky Linux 10. Check the release, enabled repository list, and available package suffix:
grep -E '^(PRETTY_NAME|VERSION_ID)=' /etc/os-release
sudo dnf repolist --enabled | grep -i docker
sudo dnf makecache
dnf list docker-ce --showduplicates
You should see the Docker stable repository and an el10 package candidate. If the repository is absent, repeat the dnf config-manager --add-repo command from Step 3 instead of downloading an unrelated CentOS file.
package ... conflicts with ... during installation
Podman, runc, or a distribution Docker package is still installed and owns a file required by Docker’s package set. On a fresh VPS, rerun Step 2 and then retry Step 4; on a working production host, identify the dependent service before removing anything.
permission denied while trying to connect to the Docker daemon socket
The current SSH process was created before the user joined the docker group, or usermod was run for a different account. Reconnect, run id -nG, and retry; sudo docker info is only a temporary diagnostic while you correct group membership.
curl: (7) Failed to connect to 127.0.0.1 port 18082
The image may still be downloading, the container may have exited, or another process may own port 18082. Inspect both the container and local listener:
docker ps -a --filter name=rocky10-docker-check
docker logs rocky10-docker-check
sudo ss -ltnp | grep ':18082'
If the port is occupied, remove the failed test and repeat it with --publish 127.0.0.1:28082:80, then request http://127.0.0.1:28082. A loopback-only binding does not require a public firewalld rule.
Hardening
- Keep Docker’s API on its Unix socket. Never expose an unauthenticated Docker API on TCP port 2375; remote administration needs deliberate TLS configuration and narrow source-IP rules.
- Keep internal services on
127.0.0.1until they are behind a TLS reverse proxy. For a public web service, open only HTTP and HTTPS in firewalld after confirming your SSH access remains allowed:
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
- Review Docker’s packet-filtering behavior before assuming a normal firewalld rule fully describes the reachability of a published container port.
- Monitor
/var/lib/dockerand container logs. Image layers and the default JSON logging driver can consume disk space even when few containers are running.
FAQ
Does Docker work on Rocky Linux 10?
Docker publishes RHEL 10 packages, and Rocky Linux 10 provides the compatible Enterprise Linux package interfaces used by the repository method in this guide. The available package build and exact Engine version change as Docker releases updates.
Why does this guide use Docker’s RHEL repository?
Docker’s RHEL instructions provide the current package names and repository configuration for Enterprise Linux 10. Rocky Linux is a RHEL-compatible distribution, so DNF can resolve the matching el10 RPMs without using an old CentOS repository path.
Do I have to remove Podman first?
For a clean Docker Engine installation, remove packages that conflict with Docker’s container runtime as shown in Step 2. Podman and Docker can coexist in some setups, but mixing runtimes on a small VPS adds package and command confusion; keep an existing production service intact until you have a migration plan.
Should I open port 18082 in firewalld?
No. The test binds to 127.0.0.1, which accepts connections only from the VPS itself. When you later publish a real application publicly, open only its required ports, use a domain with TLS for web traffic, and review Docker’s firewall guidance.