Docker Engine packages an application and all its dependencies into an image and runs it as an isolated container on any Linux server. In this tutorial you will install Docker Engine, the Docker Compose plugin and Buildx from Docker's official repository on Ubuntu 24.04, with the differences you need for Debian 12 and Rocky Linux 9. By the end, Docker will run as a system service with log rotation enabled, and you will have published a test container on a port.
Prerequisites
To follow this guide you need:
- A 64-bit server (x86_64 or arm64) running Ubuntu 24.04 LTS, Debian 12 or Rocky Linux 9, for example a CubePath VPS.
- A non-root user with
sudoprivileges. - At least 1 GB of RAM and about 10 GB of free disk space for images and containers.
Steps 1 to 4 are for Ubuntu 24.04. If you use Debian 12 or Rocky Linux 9, follow the matching section further down and then continue with Step 5.
Step 1 - Removing conflicting packages
Ubuntu ships its own Docker-related packages (docker.io, docker-compose-v2, podman-docker) that conflict with the official ones. Remove them if they are installed; apt will simply report the ones that are not:
sudo apt remove docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc
Existing images and volumes in /var/lib/docker/ are not deleted by this command.
Step 2 - Adding Docker's official repository
Install the tools needed to download the repository key:
sudo apt update
sudo apt install ca-certificates curl
Download Docker's GPG key into /etc/apt/keyrings, the standard directory for third-party repository keys:
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
Create the repository file in deb822 format. The release codename (noble on Ubuntu 24.04) is read from /etc/os-release:
sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF
Refresh the package index and confirm that docker-ce now comes from Docker's repository:
sudo apt update
apt-cache policy docker-ce
docker-ce:
Installed: (none)
Candidate: 5:28.4.0-1~ubuntu.24.04~noble
Version table:
5:28.4.0-1~ubuntu.24.04~noble 500
500 https://download.docker.com/linux/ubuntu noble/stable amd64 Packages
Your version number will differ; what matters is that the source is download.docker.com.
Step 3 - Installing Docker Engine
Install the engine, the CLI, containerd and the Buildx and Compose plugins:
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
On Ubuntu and Debian the service is enabled and started automatically. Check it:
sudo systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; preset: enabled)
Active: active (running) since Fri 2026-09-25 10:12:41 UTC; 20s ago
Press q to exit.
Step 4 - Testing the installation
Run the hello-world image, which pulls a tiny image, starts a container and prints a message:
sudo docker run hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
Also check that the Compose plugin is available. It is now invoked as docker compose (with a space), not as the old standalone docker-compose binary:
docker compose version
Docker Compose version v2.39.2
Installing on Debian 12
On Debian the process is the same as on Ubuntu except for the repository URL and codename. Remove conflicting packages:
sudo apt remove docker.io docker-doc docker-compose podman-docker containerd runc
Add the key and point the repository at linux/debian:
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF
Install the packages as in Step 3:
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Then run sudo docker run hello-world as in Step 4.
Installing on Rocky Linux 9
Some Rocky Linux 9 images come with Podman and Buildah, whose dependencies conflict with containerd.io. Remove them if you do not use them:
sudo dnf remove podman buildah runc
Add Docker's official RHEL repository, which is the one that applies to Rocky Linux:
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
Install the packages. dnf asks you to accept the GPG key; check that the fingerprint matches 060A 61C5 1B55 8A7F 742B 77AA C52F EB6B 621E 9F35:
sudo dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Unlike on Ubuntu, the service does not start on its own on the RHEL family. Enable and start it:
sudo systemctl enable --now docker
Check that it is active with sudo systemctl status docker and run sudo docker run hello-world as in Step 4.
Step 5 - Running Docker without sudo
By default the Docker socket (/var/run/docker.sock) is owned by root and the docker group. Adding your user to that group lets you run docker without sudo:
sudo usermod -aG docker $USER
WarningMembership in the
dockergroup is equivalent to root access on the server, because any member can start a container that mounts the host's/. Only add users you trust with root.
The group change applies to new sessions. Log out of SSH, log back in and check that it works without sudo:
docker run --rm hello-world
If you see Hello from Docker! again, the setup is correct.
Step 6 - Configuring log rotation
The default logging driver (json-file) stores everything a container writes to stdout and stderr under /var/lib/docker/containers/ with no size limit. A chatty container can fill the disk in days. Set a limit in the daemon configuration:
sudo nano /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
With this, each container keeps at most three 10 MB log files. Restart Docker to apply the change:
sudo systemctl restart docker
Verify that the daemon picked up the configuration:
docker info --format '{{.LoggingDriver}}'
json-file
If Docker fails to start after the restart, the cause is almost always a syntax error in daemon.json; read the message with sudo journalctl -u docker -n 20. The limits apply only to containers created after the change.
Step 7 - Publishing a test container
To confirm networking works, start Nginx and publish its port 80 on port 8080 of the server:
docker run -d --name test-nginx -p 8080:80 nginx
Check that it responds:
curl -I http://localhost:8080
HTTP/1.1 200 OK
Server: nginx/1.29.1
Content-Type: text/html
ImportantDocker writes its own iptables rules, and ports published with
-pare reachable from the internet even if UFW or firewalld does not allow them. If a service should only be reachable from the server itself, publish it on the loopback interface:-p 127.0.0.1:8080:80.
Remove the test container:
docker rm -f test-nginx
Troubleshooting
permission denied while trying to connect to the Docker daemon socket: your user is not in the docker group, or you have not opened a new session yet. Run groups and check that docker is listed; if not, repeat Step 5 and log in again.
The service does not start: read the reason with sudo journalctl -u docker -n 50 --no-pager. The most common causes are invalid JSON in daemon.json or leftover old packages (docker.io) that were not removed in Step 1.
Bind for 0.0.0.0:8080 failed: port is already allocated: another process or container is using that port. Find it with sudo ss -tlnp | grep :8080 and pick another port or stop the process.
The disk fills up: see how much space images, containers and volumes use with docker system df, and remove unused data with docker system prune. Add --volumes only if you are sure you do not need the data in unused volumes.
Updating Docker
Because Docker is installed from a repository, it is updated with the rest of the system:
sudo apt update && sudo apt upgrade
On Rocky Linux use sudo dnf upgrade. Containers with a restart policy (--restart unless-stopped) start again on their own after the upgrade.
Conclusion
You have installed Docker Engine, Buildx and Docker Compose from the official repository, configured access without sudo and log rotation, and confirmed that containers start and publish ports correctly. Remember that published ports bypass the host firewall.
As next steps, you can:
- Learn the everyday Docker commands to run, inspect and clean up containers, images, volumes and networks.
- Build your own images with a
Dockerfileand multi-stage builds. - Define multi-container applications in a
compose.yamlfile and manage them withdocker compose up -d.
