Install Docker on AlmaLinux 10 with Docker Engine, Compose v2, and a non-root administration setup. In about ten minutes, you will run a verified container and a local web test without exposing a new service to the public internet.
Prerequisites
- A 64-bit AlmaLinux 10 VPS with a current package index, at least 2 GB of RAM, and 10 GB of free disk space.
- SSH access as
root, or a normal Linux account with workingsudoprivileges. - Outbound HTTPS access to Docker’s package repository and Docker Hub.
- No domain is needed for this installation or the local smoke test. Use a domain later if you put a public application behind a TLS reverse proxy.
- A VPSLake Linux VPS if you need an AlmaLinux server with root access.
The commands use sudo for a normal SSH user. If you are logged in as root, remove sudo. AlmaLinux uses DNF and firewalld; do not substitute Ubuntu’s apt or ufw commands.
Step 1: Confirm AlmaLinux 10 and update the host
Checking the release first prevents you from adding a repository for a different Enterprise Linux version.
grep -E '^(PRETTY_NAME|VERSION_ID|ID_LIKE)=' /etc/os-release
uname -m
sudo dnf -y upgrade
The release should identify AlmaLinux 10 and the architecture should normally be x86_64:
PRETTY_NAME="AlmaLinux 10.x (Cerulean Lion)"
VERSION_ID="10.x"
ID_LIKE="rhel centos fedora"
x86_64
The exact minor release changes as AlmaLinux publishes updates. Resolve any DNF mirror, DNS, or signature error before installing Docker.
Step 2: Remove packages that conflict with Docker Engine
Distribution-provided Docker packages, Podman, and a separately installed runtime can conflict with Docker’s bundled containerd and networking components.
On a new VPS, remove the possible conflicting packages with DNF:
sudo dnf remove -y docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine \
podman \
runc
If DNF says a package is not installed, that is expected. Removing these packages does not automatically delete Docker data under /var/lib/docker/, but do not remove a package from a production host until you have checked which workloads depend on it.
Step 3: Add Docker’s signed RPM repository
The repository supplies Docker Engine, its CLI, containerd, Buildx, and the maintained Compose plugin as a compatible package set.
Install the repository-management tools and add Docker’s RHEL-compatible repository for AlmaLinux 10:
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
Confirm that DNF can see an Engine candidate before installing anything:
dnf list --showduplicates docker-ce | tail -n 5
You should see one or more docker-ce entries from download.docker.com. Docker publishes new patch versions regularly, so the version and release date will differ from this example.
For the repository prerequisites and package names, see Docker’s RHEL installation documentation. AlmaLinux’s compatible Enterprise Linux package interfaces let DNF use this package path.
Step 4: Install Docker Engine and Compose v2
These packages install the daemon, CLI, runtime, Buildx builder, and the docker compose subcommand in one transaction.
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
Check the service and command-line tools immediately:
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 matching client/server versions. Patch numbers will change:
active
enabled
Client 29.x.y / Server 29.x.y
Docker Compose version v2.x.y
Step 5: Run Docker’s installation test
The hello-world image proves that the CLI can reach the daemon, pull an image, create a container, and run it successfully.
sudo docker run --rm hello-world
Look for this confirmation near the end of the output:
Hello from Docker!
Your installation appears to be working correctly.
The container exits after printing the message, and --rm removes its stopped container record. The small image remains cached until you prune it.
Step 6: Allow a trusted account to run Docker
Docker’s Unix socket is root-owned by default. The docker group removes repeated sudo prompts, but membership is effectively administrative access to the host.
Run this block from the normal sudo-enabled account that should manage containers, not from a root shell:
sudo groupadd --force docker
sudo usermod -aG docker "$USER"
Log out of SSH completely and reconnect so the new group is loaded. Then check access without sudo:
id -nG
docker info --format 'Server {{.ServerVersion}}'
The group list should include docker, and the second command should print the Engine version. Until you reconnect, use newgrp docker or keep using sudo docker.
Step 7: Test a local web container
This test checks Docker networking while binding the host port to loopback, so a random internet client cannot reach the container.
docker run -d --name almalinux-docker-test --publish 127.0.0.1:8080:80 nginx:alpine
docker ps --filter name=almalinux-docker-test --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
curl --head http://127.0.0.1:8080
The container should be running and curl should receive an HTTP success response:
NAMES STATUS PORTS
almalinux-docker-test Up ... 127.0.0.1:8080->80/tcp
HTTP/1.1 200 OK
Remove the disposable test when you are finished:
docker stop almalinux-docker-test
docker rm almalinux-docker-test
Verify Docker on AlmaLinux 10
Run this final check to confirm service startup, daemon communication, Compose, and the container runtime:
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}}'
Expected results include active, enabled, Docker client/server versions, a Compose v2 version, and a storage driver such as overlay2:
active
enabled
Client 29.x.y / Server 29.x.y
Docker Compose version v2.x.y
Containers 0 | Images 2 | Storage overlay2
Docker is ready for a Compose project or application image. For firewall behavior and published ports, read Docker’s packet-filtering and firewalls guide.
Troubleshooting
No match for argument: docker-ce
DNF has not loaded Docker’s repository, or the repository was added for a different Enterprise Linux release. Inspect the repo file and refresh metadata:
sudo dnf repolist --enabled | grep -i docker
sudo dnf makecache
dnf list --showduplicates docker-ce
The enabled list should contain Docker’s repository and the package list should show an el10 build. If the repo file is missing, run the dnf config-manager --add-repo command from Step 3 again.
package podman ... conflicts with ... during installation
Podman or a standalone runc package is still installed and owns files Docker expects to manage. On a fresh host, rerun the removal block from Step 2, then retry the Docker package installation; do not remove a runtime that a current production service still needs without a migration plan.
permission denied while trying to connect to the Docker daemon socket
Your SSH session was opened before the account joined the docker group, or you ran the group command from a different account. Reconnect, run id -nG, and retry. sudo docker info is a temporary workaround, while newgrp docker reloads group membership in the current shell.
curl: (7) Failed to connect to 127.0.0.1 port 8080
The test container stopped, the image is still downloading, or another process already owns port 8080. Check the container state and logs:
docker ps -a --filter name=almalinux-docker-test
docker logs almalinux-docker-test
docker port almalinux-docker-test
If another local service owns the port, repeat the test with --publish 127.0.0.1:18080:80 and curl http://127.0.0.1:18080. Because the example binds only to loopback, you do not need to open 8080 in firewalld.
Hardening
- Keep Docker on its Unix socket. Never expose an unauthenticated Docker API on TCP port 2375; remote administration requires a deliberately configured TLS endpoint and narrow firewall rules.
- Treat the
dockergroup as root-equivalent access. Use Docker rootless mode when the workload needs a stronger privilege boundary. - Keep test and internal application ports on
127.0.0.1. If a web service must be public, publish only ports 80 and 443 and open the matching firewalld services:
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
- Keep SELinux enabled. When a container bind-mounts a host directory, use a private
:Zlabel, for example-v /srv/app:/app:Z, instead of weakening file permissions or disabling SELinux.
FAQ
Is Docker supported on AlmaLinux 10?
AlmaLinux 10 follows the Enterprise Linux 10 interfaces that Docker’s RHEL package instructions target. The repository method in this guide uses DNF and Docker’s el10 packages, while the exact package versions change as Docker publishes updates.
Should I use Podman instead of Docker on AlmaLinux?
Podman is a strong daemonless option and is commonly available in the AlmaLinux repositories. Choose Docker when your deployment, Compose files, tooling, or team workflow specifically expects the Docker Engine and CLI; do not run both engines casually with overlapping runtime packages.
Where does Docker store images and containers?
The default data root is /var/lib/docker, with containerd data under /var/lib/containerd. Keep that space on a filesystem with enough capacity, and use Docker’s own prune commands only after checking which images, stopped containers, and volumes are disposable.
How do I publish a container safely?
Bind internal services to 127.0.0.1 and put a TLS reverse proxy in front of them when they do not need direct public access. For a public service, publish only its required ports and align firewalld and Docker’s packet-filtering rules with the application design.