ESC

Start typing to search across invoices, services, domains, tickets, and more...

Search... Ctrl+K
Linux Server

How to Install Docker and Docker Compose on Debian, Ubuntu, Rocky Linux and AlmaLinux

7 steps 15 min read 5 views 0
On this page

This guide shows how to install Docker and Docker Compose on a Linux server using Docker's official repository, with separate commands for Debian/Ubuntu and Rocky Linux/AlmaLinux. It also covers post-install setup, running your first container, registry mirrors and the classic pitfall of Docker publishing ports straight past your firewall. It applies to IMIDC VPS and dedicated servers running a 64-bit OS with root access.

Step 1: Remove Old Docker Packages Before Installing

Distribution packages such as docker.io or podman-docker conflict with Docker CE. On a fresh system the commands simply report that nothing is installed. Existing images and volumes in /var/lib/docker are not deleted by removing these packages.

# Debian / Ubuntu: remove distro packages that conflict with Docker CE
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do apt-get remove -y $pkg; done

# Rocky Linux / AlmaLinux
dnf remove -y docker docker-client docker-client-latest docker-common docker-latest \
  docker-latest-logrotate docker-logrotate docker-engine podman runc

Step 2: Install Docker on Debian/Ubuntu from the Official Repository

The official repo ships newer releases than the distro archives and includes the docker compose plugin (Compose V2). The commands read /etc/os-release to detect Debian or Ubuntu and the release codename, so you can paste them as-is.

apt-get update
apt-get install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
. /etc/os-release          # sets $ID (debian/ubuntu) and $VERSION_CODENAME
curl -fsSL https://download.docker.com/linux/$ID/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/$ID $VERSION_CODENAME stable" \
  > /etc/apt/sources.list.d/docker.list
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Step 3: Install Docker CE on Rocky Linux/AlmaLinux

On RHEL-compatible systems use Docker's rhel repository, which works for Rocky Linux and AlmaLinux 8 and 9. During the first install dnf asks you to accept Docker's GPG key; check the fingerprint and answer y.

dnf install -y dnf-plugins-core
dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Step 4: Start Docker and Complete Post-Install Setup

Enable the service at boot and verify it with hello-world. To let a regular user run docker without sudo, add them to the docker group, but remember that membership grants root-equivalent rights, so only do this for trusted accounts.

systemctl enable --now docker
docker version
docker compose version
docker run --rm hello-world

# Optional: allow a normal user to run docker (this is equivalent to root access)
usermod -aG docker deploy
# log out and log in again as "deploy", then test:
docker ps

Step 5: Run Your First Container with Docker Compose

Compose V2 is invoked as docker compose (with a space) and the preferred file name is compose.yaml. The example below starts an Nginx container bound to 127.0.0.1 only; you can later publish it through a host Nginx reverse proxy with HTTPS (see our Nginx reverse proxy tutorial).

mkdir -p /opt/web && cd /opt/web
cat > compose.yaml <<'EOF'
services:
  web:
    image: nginx:stable
    ports:
      - "127.0.0.1:8080:80"
    volumes:
      - ./html:/usr/share/nginx/html:ro
    restart: unless-stopped
EOF
mkdir -p html && echo "Hello from Docker" > html/index.html
docker compose up -d
docker compose ps
curl http://127.0.0.1:8080
docker compose logs -f web      # Ctrl+C to stop following

Step 6: Configure a Docker Registry Mirror and Log Limits

IMIDC servers in Hong Kong, Japan, Korea and other regions normally reach Docker Hub directly, so a mirror is rarely needed. If pulls are slow, add a mirror you trust in daemon.json (registry-mirrors applies to Docker Hub only). It is also wise to cap container log size so logs cannot fill the disk. Anonymous pulls are rate limited, so run docker login if you pull often.

mkdir -p /etc/docker
cat > /etc/docker/daemon.json <<'EOF'
{
  "registry-mirrors": ["https://mirror.example.com"],
  "log-driver": "json-file",
  "log-opts": { "max-size": "20m", "max-file": "3" }
}
EOF
systemctl restart docker
docker info | grep -A2 "Registry Mirrors"

# Avoid anonymous Docker Hub pull limits
docker login
daemon.json must be valid JSON; a single trailing comma stops Docker from starting. Check journalctl -u docker -n 50 if the restart fails.

Step 7: Docker and the Firewall (iptables, ufw, firewalld)

Docker writes its own iptables/nftables rules to publish ports, and those rules are evaluated before ufw's. As a result a port published with -p 8080:80 is reachable from the internet even if ufw never allowed 8080. The safest pattern is to bind to 127.0.0.1 and front the app with Nginx; if a port must be public, filter sources in the DOCKER-USER chain.

# 1) Recommended: publish only on localhost and expose the app through Nginx
#    compose.yaml ->  ports: - "127.0.0.1:8080:80"

# 2) Restrict a published port in the DOCKER-USER chain
#    (only 198.51.100.20 may reach container port 8080 via eth0)
iptables -I DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 8080 --ctdir ORIGINAL \
  ! -s 198.51.100.20 -j DROP
iptables -L DOCKER-USER -n --line-numbers

# 3) firewalld (Rocky/AlmaLinux): Docker adds its own "docker" zone
firewall-cmd --get-active-zones
firewall-cmd --zone=docker --list-all

# After reloading or restarting the firewall, restart Docker to rebuild its rules
systemctl restart docker
Never expose database containers (MySQL, Redis, MongoDB) with -p 3306:3306 to the internet. It is one of the most common causes of breaches and ransom attacks.

FAQ

"permission denied while trying to connect to the Docker daemon socket"?

Your user is not in the docker group, or you have not logged in again since adding it. Run id to check, reconnect over SSH, or simply use root or sudo.

What is the difference between docker-compose and docker compose?

docker-compose (with a hyphen) is the retired V1 standalone tool. docker compose is the V2 plugin installed with docker-compose-plugin. The syntax is largely compatible, so use V2 for all new projects.

Containers lost network access after a firewall reload?

Reloading firewalld or flushing iptables removes the NAT rules Docker created. Run systemctl restart docker and Docker recreates them.

Still stuck after following these steps? Open a support ticket and the IMIDC 24/7 technical team will help. Include the server IP, OS version, the commands you ran and the full error output so we can pinpoint the issue faster.

Was this answer helpful?

Related Tutorials