$linuxjunkies
>

How to Install Docker on Linux

Install Docker Engine on Ubuntu, Debian, Fedora, RHEL, or Arch from the official repository, configure the docker group, run a first container, and get Compose working.

IntermediateUbuntuDebianFedoraArch9 min readUpdated June 7, 2026

Before you start

  • A 64-bit Linux host with kernel 4.0 or later (check with uname -r)
  • sudo or root access on the target machine
  • curl and gnupg installed (most distributions include these)

Docker is the de-facto standard for running containerised workloads on Linux. The packages shipped by most distribution repositories lag behind the upstream release cycle, so the recommended path is to add Docker's own repository and install from there. This guide covers the Docker Engine (not Docker Desktop), adds your user to the docker group, runs a first container, and gets Docker Compose working.

Prerequisites and a Note on Editions

Docker Engine is the open-source daemon you want on servers and workstations. Docker Desktop is a separate, GUI-centric product with its own licensing terms. Everything below installs Engine only. The steps assume a 64-bit x86 host; ARM64 (Raspberry Pi, AWS Graviton) is supported by the same repository—the architecture is detected automatically.

Remove Conflicting Packages

Older Docker packages shipped under different names. Remove them before you add the official repository, otherwise dependency conflicts will bite you later.

Debian / Ubuntu

for pkg in docker.io docker-doc docker-compose docker-compose-v2 \
           podman-docker containerd runc; do
  sudo apt-get remove -y $pkg 2>/dev/null
done

Fedora / RHEL / Rocky

sudo dnf remove -y docker docker-client docker-client-latest \
     docker-common docker-latest docker-latest-logrotate \
     docker-logrotate docker-selinux docker-engine-selinux docker-engine

Arch Linux

sudo pacman -Rns docker 2>/dev/null; true

Add the Docker Repository and Install

Debian / Ubuntu

Install the prerequisites, import Docker's GPG key, and add the repository in one pass.

sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
  | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

For Debian, replace ubuntu in the URL with debian. Then add the repository:

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
  | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io \
     docker-buildx-plugin docker-compose-plugin

Fedora / RHEL 9 / Rocky 9

sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo \
     https://download.docker.com/linux/fedora/docker-ce.repo

On RHEL or Rocky, use the CentOS repo URL instead:

sudo dnf config-manager --add-repo \
     https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io \
     docker-buildx-plugin docker-compose-plugin

Arch Linux

Arch's community repository ships a reasonably current Docker build.

sudo pacman -Syu --needed docker docker-compose

Enable and Start the Docker Daemon

Docker runs as a systemd service. Enable it so it starts on boot, then start it immediately.

sudo systemctl enable --now docker

Confirm it is running:

sudo systemctl status docker

You should see Active: active (running). Output will vary by host and Docker version.

Add Your User to the docker Group

By default, the Docker socket (/var/run/docker.sock) is owned by root and the docker group. Running every command with sudo is inconvenient and error-prone. Adding your user to the group removes that requirement.

Security note: membership in the docker group is effectively equivalent to passwordless root access on the host—any user who can talk to the Docker socket can trivially escalate privileges. Only add trusted users on machines where that trade-off is acceptable. On shared or production systems, consider rootless Docker instead.
sudo usermod -aG docker $USER

The new group membership does not take effect in your current shell. Apply it without logging out by starting a new login shell:

newgrp docker

Verify you can reach the daemon without sudo:

docker info

Run Your First Container

The canonical smoke test pulls a minimal image and runs a one-shot container that prints a message and exits.

docker run --rm hello-world

Docker pulls the image from Docker Hub, creates a container, runs it, and removes it when it exits (--rm). A successful run ends with a line similar to:

# Hello from Docker!
# This message shows that your installation appears to be working correctly.

Try something more interactive—an ephemeral Ubuntu shell that disappears cleanly when you exit:

docker run --rm -it ubuntu:24.04 bash

Install and Use Docker Compose

Modern Docker ships Compose as a CLI plugin (docker compose, no hyphen). If you installed the packages above it is already present. Confirm:

docker compose version

Typical output: Docker Compose version v2.27.0 (version will vary).

A Minimal Compose Example

Create a working directory and a compose.yaml file:

mkdir ~/compose-test && cd ~/compose-test
cat > compose.yaml <<'EOF'
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: example
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  db_data:
EOF
docker compose up -d

Check that both services are running:

docker compose ps

Then open http://localhost:8080 in a browser—you should see the nginx welcome page. Tear it down when you are done:

docker compose down -v

Docker's daemon configuration lives in /etc/docker/daemon.json. A few settings worth knowing about on production hosts:

sudo tee /etc/docker/daemon.json <<'EOF'
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "20m",
    "max-file": "5"
  },
  "storage-driver": "overlay2"
}
EOF
sudo systemctl restart docker

Without log rotation, long-running containers can fill your disk with JSON logs. The overlay2 storage driver is the default on modern kernels but stating it explicitly prevents surprises if defaults change.

Firewall Considerations

Docker manipulates iptables rules directly to handle port forwarding and container networking. This can interact unexpectedly with ufw, firewalld, or nftables front-ends—published ports may be reachable even when your firewall rules suggest otherwise.

  • ufw users: Docker bypasses ufw by writing to the DOCKER chain. Bind sensitive ports to 127.0.0.1 explicitly (e.g., 127.0.0.1:5432:5432) to avoid unintended exposure.
  • firewalld users: Docker 26+ has improved firewalld integration. Run sudo firewall-cmd --reload after starting Docker if rules seem inconsistent.
  • Never disable Docker's iptables integration ("iptables": false) unless you fully understand the networking consequences—container-to-container and NAT routing will break.

Troubleshooting

  • Permission denied on the socket: You ran docker in a shell that predates the usermod change. Run newgrp docker or open a fresh terminal. A full logout/login is the most reliable fix.
  • Cannot connect to the Docker daemon: The service is not running. Check sudo systemctl status docker and look at journalctl -xeu docker for errors.
  • Repository GPG errors on Debian/Ubuntu: The key file must be readable by apt. Re-run the chmod a+r step and then sudo apt-get update.
  • SELinux denials on Fedora/RHEL/Rocky: Docker and SELinux coexist in enforcing mode, but volume mounts often need the :z or :Z flag (e.g., -v /host/path:/container/path:z) to relabel files correctly.
  • Compose v1 vs v2: The legacy docker-compose binary (Python, hyphenated) is end-of-life. If a project requires it, the modern plugin is a drop-in replacement for nearly all workflows.
tested on:Ubuntu 24.04Debian 12Fedora 40Rocky 9

Frequently asked questions

What is the difference between docker-compose (v1) and docker compose (v2)?
Docker Compose v1 was a standalone Python binary invoked as docker-compose. V2 is a Go-based CLI plugin invoked as docker compose (no hyphen) and ships with Docker Engine packages. V1 is end-of-life; use V2 for all new work.
Is adding my user to the docker group safe?
It is convenient but carries real risk: any user in the docker group can mount the host filesystem into a container and gain root-equivalent access. On shared or multi-user servers, use rootless Docker or require sudo instead.
Why does Docker bypass my ufw or firewalld rules?
Docker writes directly to iptables to manage container NAT and port forwarding, which can expose published ports even when your firewall front-end says they are blocked. Bind sensitive ports to 127.0.0.1 in your Compose or run command to limit exposure.
Should I use the distro-provided docker.io package instead?
The distro packages are often significantly behind the upstream release. For anything beyond casual experimentation, the official Docker repository is preferable because you get current Engine versions, Buildx, and the Compose plugin together.
How do I update Docker after this installation?
Because Docker is now in a managed repository, updates come through your normal package manager: apt-get upgrade on Debian/Ubuntu, dnf upgrade on Fedora/RHEL, or pacman -Syu on Arch. No manual steps required.

Related guides