{"id":1051,"date":"2026-11-10T13:00:00","date_gmt":"2026-11-10T13:00:00","guid":{"rendered":"https:\/\/vpslake.com\/blog\/?p=1051"},"modified":"2026-11-10T13:00:00","modified_gmt":"2026-11-10T13:00:00","slug":"install-docker-rocky-linux-10","status":"publish","type":"post","link":"https:\/\/vpslake.com\/blog\/2026\/11\/10\/install-docker-rocky-linux-10\/","title":{"rendered":"How to Install Docker on Rocky Linux 10 (Step-by-Step)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Install Docker on Rocky Linux 10 from Docker\u2019s 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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>A 64-bit Rocky Linux 10 VPS, normally <code>x86_64<\/code> or <code>aarch64<\/code>, with at least 2 GB of RAM and 10 GB of free disk space.<\/li>\n\n\n<li>SSH access as <code>root<\/code>, or a regular account with working <code>sudo<\/code> privileges.<\/li>\n\n\n<li>Outbound HTTPS access to Docker\u2019s package repository and Docker Hub.<\/li>\n\n\n<li>No domain is required for installation or the local smoke test. You need a domain later if a public web application will use HTTPS.<\/li>\n\n\n<li>A <a href=\"https:\/\/vpslake.com\/buy-vps\">VPSLake Linux VPS<\/a> if you need a Rocky Linux server with root access for containers.<\/li>\n\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The commands use <code>sudo<\/code> for a normal account. If you are logged in as <code>root<\/code>, remove <code>sudo<\/code>. Rocky Linux uses DNF and firewalld, so do not substitute Debian\u2019s APT or Ubuntu\u2019s UFW commands.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: Confirm Rocky Linux 10 and update the VPS<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Checking the release and CPU architecture first prevents you from installing packages for the wrong Enterprise Linux generation.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>grep -E '^(PRETTY_NAME|VERSION_ID|ID_LIKE)=' \/etc\/os-release\nuname -m\nsudo dnf upgrade -y\nsudo dnf install -y curl ca-certificates<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The release should identify Rocky Linux 10, and a common VPS architecture is <code>x86_64<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PRETTY_NAME=\"Rocky Linux 10.x (Linux-...)\"\nVERSION_ID=\"10.x\"\nID_LIKE=\"rhel fedora\"\nx86_64<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The minor version and upgrade count will vary. Resolve any DNS, mirror, or GPG-signature error before adding Docker\u2019s repository.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Remove packages that can conflict with Docker Engine<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Rocky Linux may already contain Podman or a standalone runtime; removing those conflicting packages lets Docker install its matching containerd and networking components.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On a new VPS, run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo dnf remove -y docker \\\n  docker-client \\\n  docker-client-latest \\\n  docker-common \\\n  docker-latest \\\n  docker-latest-logrotate \\\n  docker-logrotate \\\n  docker-engine \\\n  podman \\\n  runc<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">DNF may report that some names are not installed, which is harmless. On an existing host, inspect workloads before removing Podman or <code>runc<\/code>; uninstalling these packages does not automatically erase Docker data under <code>\/var\/lib\/docker\/<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3: Add Docker\u2019s RHEL-compatible RPM repository<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Docker\u2019s 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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Install the repository-management plugin and add the repository:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo dnf install -y dnf-plugins-core\nsudo dnf config-manager --add-repo https:\/\/download.docker.com\/linux\/rhel\/docker-ce.repo\nsudo dnf makecache<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Check that DNF can see Docker Engine builds before installing them:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>dnf list docker-ce --showduplicates | sort -r | head -n 6<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The list should contain entries from <code>docker-ce-stable<\/code>, with an <code>el10<\/code> suffix on a Rocky Linux 10 system. Docker documents this repository procedure in its <a href=\"https:\/\/docs.docker.com\/engine\/install\/rhel\/\" target=\"_blank\" rel=\"noopener\">RHEL installation guide<\/a>; Rocky\u2019s compatible RPM tooling is why the method works here.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4: Install Docker Engine and start it at boot<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Installing the daemon and plugins together keeps the client, runtime, image builder, and Compose integration aligned.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\nsudo systemctl enable --now docker<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Confirm that systemd started the daemon and that the CLI can reach it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl is-active docker\nsudo systemctl is-enabled docker\nsudo docker version --format 'Client {{.Client.Version}} \/ Server {{.Server.Version}}'\nsudo docker compose version<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Expected output has an active, enabled service and version strings. Docker publishes updates independently of Rocky\u2019s minor releases, so the numbers will change:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>active\nenabled\nClient 29.x.y \/ Server 29.x.y\nDocker Compose version v2.x.y<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 5: Run Docker\u2019s built-in installation test<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>hello-world<\/code> image exercises the complete path from the CLI to the daemon, registry, image cache, container creation, and process startup.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo docker run --rm hello-world<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Near the end, look for the success message:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Hello from Docker!\nYour installation appears to be working correctly.<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The test container exits after printing its message. <code>--rm<\/code> removes the stopped container record, while the small image remains cached for future use.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 6: Give a dedicated account non-root Docker access<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Docker\u2019s Unix socket is root-owned by default; adding one trusted administrator to the <code>docker<\/code> group avoids repeated <code>sudo<\/code> while preserving the normal daemon setup.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Run this as the account that should manage containers, replacing the placeholder with its login name:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo groupadd --force docker\nsudo usermod -aG docker YOUR_ADMIN_USER<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Log out of SSH completely and reconnect as that user so the new group is loaded. Then test the socket without <code>sudo<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>id -nG\ndocker info --format 'Server {{.ServerVersion}}'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The group list should include <code>docker<\/code>, and the second command should print the Engine version. <code>newgrp docker<\/code> can load the group in the current shell, but a fresh SSH session is easier to reason about.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Treat the <code>docker<\/code> group as root-equivalent access: a member can start a privileged container and effectively control the VPS.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 7: Test container networking without opening a public port<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A disposable NGINX container verifies port publishing while binding only to the VPS\u2019s loopback address, so no DNS record or inbound firewall rule is needed.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker run -d --name rocky10-docker-check --publish 127.0.0.1:18082:80 nginx:alpine\ndocker ps --filter name=rocky10-docker-check --format 'table {{.Names}}\\t{{.Status}}\\t{{.Ports}}'\ncurl --head --max-time 10 http:\/\/127.0.0.1:18082<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You should see the container running and an HTTP success response:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>NAMES                STATUS        PORTS\nrocky10-docker-check Up ...        127.0.0.1:18082-&gt;80\/tcp\nHTTP\/1.1 200 OK<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Clean up the test after confirming the response:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker stop rocky10-docker-check\ndocker rm rocky10-docker-check<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Verify Docker on Rocky Linux 10<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Run this final check as the Docker administrator to confirm boot startup, daemon communication, Compose, and the storage driver:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>systemctl is-active docker\nsystemctl is-enabled docker\ndocker version --format 'Client {{.Client.Version}} \/ Server {{.Server.Version}}'\ndocker compose version\ndocker info --format 'Containers {{.Containers}} | Images {{.Images}} | Storage {{.Driver}}'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Successful output includes <code>active<\/code>, <code>enabled<\/code>, client\/server versions, a Compose v2 version, and usually <code>overlay2<\/code> as the storage driver:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>active\nenabled\nClient 29.x.y \/ Server 29.x.y\nDocker Compose version v2.x.y\nContainers 0 | Images 2 | Storage overlay2<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For the way published container ports interact with host packet filtering, read Docker\u2019s <a href=\"https:\/\/docs.docker.com\/engine\/network\/packet-filtering-firewalls\/\" target=\"_blank\" rel=\"noopener\">firewall and packet-filtering guidance<\/a>.<\/p>\n\n\n\n<aside class=\"vl-element vl-cta-banner\" aria-label=\"Run Docker on Rocky 10\">\n\t<div class=\"vl-cta-banner__content\">\n\t\t<p class=\"vl-cta-banner__eyebrow\">Rocky Linux VPS<\/p>\n\t\t<h3 class=\"vl-cta-banner__title\">Run Docker on Rocky 10<\/h3>\n\t\t<p class=\"vl-cta-banner__text\">Get a root-access Rocky Linux VPS for containers, Compose projects, and development workloads.<\/p>\n\t<\/div>\n\t<a class=\"vl-cta-banner__button\" href=\"\/buy-vps\" aria-label=\"View Linux VPS plans\">View Linux VPS plans<\/a>\n<\/aside>\n\n\n\n<h2 class=\"wp-block-heading\">Troubleshooting<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><code>No match for argument: docker-ce<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">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:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>grep -E '^(PRETTY_NAME|VERSION_ID)=' \/etc\/os-release\nsudo dnf repolist --enabled | grep -i docker\nsudo dnf makecache\ndnf list docker-ce --showduplicates<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You should see the Docker stable repository and an <code>el10<\/code> package candidate. If the repository is absent, repeat the <code>dnf config-manager --add-repo<\/code> command from Step 3 instead of downloading an unrelated CentOS file.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>package ... conflicts with ...<\/code> during installation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Podman, <code>runc<\/code>, or a distribution Docker package is still installed and owns a file required by Docker\u2019s 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.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>permission denied while trying to connect to the Docker daemon socket<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The current SSH process was created before the user joined the <code>docker<\/code> group, or <code>usermod<\/code> was run for a different account. Reconnect, run <code>id -nG<\/code>, and retry; <code>sudo docker info<\/code> is only a temporary diagnostic while you correct group membership.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>curl: (7) Failed to connect to 127.0.0.1 port 18082<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">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:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker ps -a --filter name=rocky10-docker-check\ndocker logs rocky10-docker-check\nsudo ss -ltnp | grep ':18082'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If the port is occupied, remove the failed test and repeat it with <code>--publish 127.0.0.1:28082:80<\/code>, then request <code>http:\/\/127.0.0.1:28082<\/code>. A loopback-only binding does not require a public firewalld rule.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Hardening<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>Keep Docker\u2019s 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.<\/li>\n\n\n<li>Keep internal services on <code>127.0.0.1<\/code> until 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:<\/li>\n\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>  sudo firewall-cmd --permanent --add-service=http\n  sudo firewall-cmd --permanent --add-service=https\n  sudo firewall-cmd --reload<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>Review Docker\u2019s packet-filtering behavior before assuming a normal firewalld rule fully describes the reachability of a published container port.<\/li>\n\n\n<li>Monitor <code>\/var\/lib\/docker<\/code> and container logs. Image layers and the default JSON logging driver can consume disk space even when few containers are running.<\/li>\n\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">FAQ<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Does Docker work on Rocky Linux 10?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why does this guide use Docker\u2019s RHEL repository?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Docker\u2019s 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 <code>el10<\/code> RPMs without using an old CentOS repository path.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Do I have to remove Podman first?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For a clean Docker Engine installation, remove packages that conflict with Docker\u2019s 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.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Should I open port 18082 in firewalld?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. The test binds to <code>127.0.0.1<\/code>, 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\u2019s firewall guidance.<\/p>\n\n\n\n<script type=\"application\/ld+json\">{\"@context\": \"https:\/\/schema.org\", \"@type\": \"FAQPage\", \"mainEntity\": [{\"@type\": \"Question\", \"name\": \"Does Docker work on Rocky Linux 10?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"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.\"}}, {\"@type\": \"Question\", \"name\": \"Why does this guide use Docker\u2019s RHEL repository?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"Docker\u2019s 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.\"}}, {\"@type\": \"Question\", \"name\": \"Do I have to remove Podman first?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"For a clean Docker Engine installation, remove packages that conflict with Docker\u2019s 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.\"}}, {\"@type\": \"Question\", \"name\": \"Should I open port 18082 in firewalld?\", \"acceptedAnswer\": {\"@type\": \"Answer\", \"text\": \"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\u2019s firewall guidance.\"}}]}<\/script>\n\n","protected":false},"excerpt":{"rendered":"Install Docker on Rocky Linux 10 with Docker Engine, Compose v2, non-root access, a local test, and firewalld guidance for a secure Rocky VPS.","protected":false},"author":1,"featured_media":1052,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"csco_singular_sidebar":"","csco_page_header_type":"","csco_page_load_nextpost":"","footnotes":""},"categories":[5,9],"tags":[119,118,120,99],"class_list":["post-1051","post","type-post","status-publish","format-standard","has-post-thumbnail","category-guides","category-vps-hosting","tag-containers","tag-docker","tag-docker-compose","tag-rocky-linux-10","cs-entry"],"_links":{"self":[{"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/posts\/1051","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/comments?post=1051"}],"version-history":[{"count":1,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/posts\/1051\/revisions"}],"predecessor-version":[{"id":3970,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/posts\/1051\/revisions\/3970"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/media\/1052"}],"wp:attachment":[{"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/media?parent=1051"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/categories?post=1051"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vpslake.com\/blog\/wp-json\/wp\/v2\/tags?post=1051"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}