· Satrajit Sengupta · Containers · 5 min read
Installing Docker on Linux — Ubuntu and CentOS / RHEL complete guide
Docker installation varies more than it should across Linux distributions. Here's the complete path for both Ubuntu and CentOS/RHEL, including the offline method for air-gapped production servers.
Docker installation is not quite as universal as “run one command” — the steps differ meaningfully between Debian-based systems (Ubuntu) and RHEL-based systems (CentOS, RHEL, Fedora), and production environments with restricted internet access need a completely different approach.
This post covers both major Linux families and both installation scenarios: online (repository-based) and offline (manual package installation for air-gapped servers).
Before you install
Check which Linux distribution and version you’re running:
# Ubuntu
lsb_release -a
# CentOS / RHEL
cat /etc/centos-release
hostnamectlVerify your kernel version — Docker requires 3.10 or higher:
uname -rDocker supports the following architectures: x86_64, amd64, arm, and arm64.
Ubuntu
Supported versions
Docker CE officially supports:
- Ubuntu 22.04 LTS (Jammy)
- Ubuntu 20.04 LTS (Focal)
- Ubuntu 18.04 LTS (Bionic)
Ubuntu uses overlay2 as the default storage driver. It also supports aufs and btrfs.
Step 1: Remove older Docker packages
Ubuntu’s default repositories include an older docker package that conflicts with Docker CE. Remove it first:
sudo apt-get remove docker docker-engine docker.io containerd runcEarlier versions were distributed as docker, docker.io, or docker-engine — none of these are Docker CE.
Method A: Online installation (repository)
This is the standard method for internet-connected servers and the easiest upgrade path.
# Update package index
sudo apt-get update
# Install HTTPS transport dependencies
sudo apt-get install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
# Add Docker's official GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# Verify the fingerprint — the last 8 characters should be: 0EBF CD88
sudo apt-key fingerprint 0EBFCD88
# Add the stable Docker repository
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
# Update again and install
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.ioInstalling a specific version (important for production — pin your Docker version):
# List available versions
sudo apt-cache madison docker-ce
# Install a specific version
sudo apt-get install -y \
docker-ce=5:20.10.12~3-0~ubuntu-focal \
docker-ce-cli=5:20.10.12~3-0~ubuntu-focal \
containerd.ioVerify the installation:
sudo docker run hello-worldMethod B: Offline installation (air-gapped servers)
Many production environments have no direct internet access — database servers in private subnets, secure compliance environments, or servers behind strict egress rules. In these cases you download the .deb packages on a machine that has internet access and transfer them manually.
On a machine with internet access, download the packages from:
https://download.docker.com/linux/ubuntu/dists/<distro>/pool/stable/amd64/For Ubuntu 20.04 (Focal), you need three files:
containerd.io_<version>_amd64.debdocker-ce-cli_<version>~ubuntu-focal_amd64.debdocker-ce_<version>~ubuntu-focal_amd64.deb
Transfer to the target server (example using a bastion host for a server in a private subnet):
# From your local machine to the bastion host
scp -i your-key.pem *.deb ubuntu@<bastion-public-ip>:/tmp/
# From the bastion host to the private server
scp -i your-key.pem /tmp/*.deb ubuntu@<private-server-ip>:/home/ubuntu/On the target server, install with dpkg:
sudo dpkg -i *.deb
docker -vThis method works for any software installation on air-gapped servers — not just Docker.
CentOS / RHEL
The instructions below apply to CentOS 7, CentOS 8 Stream, and RHEL (CentOS is built from RHEL source, so the process is identical). Note which package manager applies to your version: CentOS 7 uses yum; CentOS 8 and later use dnf.
Step 1: Remove older Docker packages
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engineThis removes older pre-CE Docker packages. Docker CE (if previously installed) has different package names and won’t be affected.
Step 2: Install on CentOS 7
The centos-extras repository must be enabled (it is by default on a fresh install):
sudo yum install -y yum-utils
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
sudo yum update -y && sudo yum install -y docker-ce docker-ce-cli containerd.ioIf you hit a runc conflict (this can happen on some CentOS 7 configurations):
sudo yum remove -y runc
sudo yum install -y docker-ce docker-ce-cli containerd.ioStart and enable Docker:
sudo systemctl start docker
sudo systemctl enable docker
sudo systemctl status dockerStep 3: Install on CentOS 8 / Stream / RHEL 8+
CentOS 8 ships with podman and buildah as default container tools. They conflict with Docker CE and must be removed first:
sudo dnf remove -y podman buildahThen add the Docker repository and install (use dnf instead of yum):
sudo dnf install -y yum-utils
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
sudo systemctl start docker
sudo systemctl enable dockerVerify:
sudo docker run hello-worldPost-install: Running Docker without sudo
By default, Docker requires root privileges. The correct fix is to add your user to the docker group — not to run everything as root.
sudo usermod -aG docker $USER
# Apply immediately without logging out (Ubuntu/CentOS both)
newgrp docker
# Verify
docker run hello-worldCentOS / RHEL: Firewall considerations
CentOS and RHEL run firewalld by default. Docker manages iptables rules directly and usually adds the right rules when it starts. If your containers can’t reach the network or each other, inspect the firewall state:
sudo firewall-cmd --list-allIf the docker zone is missing or the docker0 interface isn’t in a trusted zone:
sudo firewall-cmd --permanent --zone=trusted --add-interface=docker0
sudo firewall-cmd --reload
sudo systemctl restart dockerCentOS / RHEL: SELinux
CentOS and RHEL run SELinux in enforcing mode by default. This can block Docker volume mounts. The right fix is to use the :z or :Z label options — not to disable SELinux.
# :z — shared label; the volume can be accessed by multiple containers
docker run -v /host/path:/container/path:z my-image
# :Z — private label; only this container can access the volume
docker run -v /host/path:/container/path:Z my-imageDisabling SELinux is the most common “fix” suggested on Stack Overflow and the most dangerous one in a production system. Use the volume label flags instead.
Keeping Docker up to date
Ubuntu:
sudo apt-get update && sudo apt-get upgrade docker-ce docker-ce-cli containerd.ioCentOS 7:
sudo yum update docker-ce docker-ce-cli containerd.ioCentOS 8+:
sudo dnf update docker-ce docker-ce-cli containerd.ioSubscribe to Docker release notes to know when security updates land. In production, stay within one minor version of the current release.
Post 3 of the Docker series on The Digital Drift. Previous: Docker basics · Next: Docker basic commands (coming soon)