How to Install Docker on Ubuntu 22.04 (Step-by-Step)

Install Docker on Ubuntu 22.04 with Docker Engine, Compose v2, non-root access, firewall guidance, a local test, and fixes for common VPS errors.
A polished 3D server rack with glowing container blocks and a subtle orange accent on a midnight-blue background.

This guide shows you how to install docker on ubuntu 22.04 using Docker’s maintained APT repository. In roughly ten minutes, you will have Docker Engine, Compose v2, non-root CLI access, and a private web-container test ready on your VPS.

Prerequisites

  • A 64-bit Ubuntu Server 22.04 LTS VPS, preferably with at least 2 GB of RAM and 10 GB of free disk space.
  • SSH access as root, or a regular account that can run commands with sudo.
  • Outbound HTTPS access to Docker’s package repository and Docker Hub.
  • UFW may be enabled, but no domain is required for this installation or its loopback-only test. You need a domain later for a public application with HTTPS.
  • A VPSLake Linux VPS if you need an Ubuntu server with root access for containers.

The commands below assume a normal SSH account and therefore use sudo. If you are already root, remove sudo. Ubuntu 22.04 uses APT and UFW; do not substitute DNF, firewalld, or instructions for another Ubuntu release.

Step 1: Confirm Jammy and update the VPS

Checking the release before adding a repository prevents packages for a different Ubuntu codename from entering the system.

grep -E '^(PRETTY_NAME|VERSION_ID|VERSION_CODENAME)=' /etc/os-release
dpkg --print-architecture
sudo apt update
sudo apt upgrade -y

The output should identify Ubuntu 22.04 and the jammy codename. Docker supports several 64-bit Ubuntu architectures; amd64 is common on VPS plans:

PRETTY_NAME="Ubuntu 22.04.5 LTS"
VERSION_ID="22.04"
VERSION_CODENAME=jammy
amd64

Resolve any DNS, mirror, or interrupted-package error before continuing. A clean package index makes the repository and dependency checks easier to diagnose.

Step 2: Remove packages that conflict with Docker Engine

Ubuntu’s docker.io and older Compose packages are distribution builds, while Docker’s official packages provide their own Engine, containerd, Buildx, and Compose components.

On a new VPS, remove possible conflicts:

sudo apt remove -y docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc

APT may say that some packages were not installed. That is normal. If this is an existing Docker host, inspect its containers and images first: removing packages does not automatically erase /var/lib/docker/, but replacing a working runtime can still interrupt services.

Step 3: Add Docker’s signed Jammy repository

The official repository gives APT a maintained source for Docker Engine and lets normal package updates handle future releases.

Install the certificate and download tools, store Docker’s signing key with readable permissions, and create an APT source specifically for Jammy:

sudo apt install -y ca-certificates curl
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
sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: jammy
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update

Check that APT can see a candidate from Docker’s repository before installing it:

apt-cache policy docker-ce

The source should mention download.docker.com and jammy/stable, with a current version listed as the candidate:

Candidate: 5:29.x.y-1~ubuntu.22.04~jammy
 500 https://download.docker.com/linux/ubuntu jammy/stable amd64 Packages

For the repository layout and supported Ubuntu releases, see Docker’s official Ubuntu installation guide.

Step 4: Install Docker Engine and Compose v2

Installing the daemon and its companion plugins together keeps the runtime, CLI, image builder, and Compose integration compatible.

sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker

Check both systemd state and the installed command-line tools:

sudo systemctl is-active docker
sudo systemctl is-enabled docker
sudo docker version --format 'Client {{.Client.Version}} / Server {{.Server.Version}}'
sudo docker compose version

Successful output looks like this; patch versions change as Docker publishes updates:

active
enabled
Client 29.x.y / Server 29.x.y
Docker Compose version v2.x.y

Step 5: Run a disposable Docker smoke test

The official hello-world image checks the full path from the CLI to the daemon, registry, image cache, container creation, and process startup.

sudo docker run --rm hello-world

Near the end, look for the confirmation below:

Hello from Docker!
Your installation appears to be working correctly.

The container exits after printing its message. --rm removes the stopped container record, although the small image remains cached for the next command.

Step 6: Enable Docker for one non-root account

Docker’s Unix socket is root-owned by default. Adding a trusted administrator to the docker group avoids repeated sudo, but it also grants practical root-level control of the VPS.

Run this as the regular account that should manage containers:

sudo groupadd --force docker
sudo usermod -aG docker "$USER"

Log out of SSH completely and reconnect so the new group membership is loaded. Then verify 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. newgrp docker can reload the group in the current shell, but a new SSH session is less error-prone.

Step 7: Check container networking without opening a public port

A local NGINX container proves that Docker can publish a port while binding it only to loopback, so neither a DNS record nor an inbound firewall rule is needed.

docker run -d --name jammy-web-check --publish 127.0.0.1:18080:80 nginx:alpine
docker ps --filter name=jammy-web-check --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
curl --head --max-time 10 http://127.0.0.1:18080

You should see a running container and an HTTP success response:

NAMES            STATUS       PORTS
jammy-web-check  Up ...       127.0.0.1:18080->80/tcp
HTTP/1.1 200 OK

Remove the disposable test when you are done:

docker stop jammy-web-check
docker rm jammy-web-check

Verify Docker on Ubuntu 22.04

Run this final check after reconnecting as your Docker administrator. It confirms boot startup, daemon communication, Compose, and local storage:

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, client and server versions, a Compose v2 version, and commonly overlay2 as the storage driver:

active
enabled
Client 29.x.y / Server 29.x.y
Docker Compose version v2.x.y
Containers 0 | Images 2 | Storage overlay2

For Docker’s interaction with UFW and published ports, read the official packet-filtering and firewall guidance.

Troubleshooting

docker-ce has no installation candidate

APT has not loaded the Docker repository, or the source file uses a suite other than jammy. Inspect the release and source, then refresh the package lists:

grep -E '^(VERSION_ID|VERSION_CODENAME)=' /etc/os-release
grep -E '^(Types|URIs|Suites|Components|Architectures|Signed-By):' /etc/apt/sources.list.d/docker.sources
sudo apt update
apt-cache policy docker-ce

The source should show Suites: jammy, and the policy output should offer a candidate from download.docker.com. Recreate Step 3 if the file is missing or points to another release.

NO_PUBKEY or an APT signature error

APT cannot read the key assigned to the Docker source. Download the key again and ensure it is world-readable; do not bypass signature verification or mark the source as trusted:

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
sudo apt update

permission denied while trying to connect to the Docker daemon socket

The current SSH session started before the account joined the docker group, or the group command was run for another user. Reconnect, run id -nG, and retry; sudo docker info is only a temporary diagnostic.

curl: (7) Failed to connect to 127.0.0.1 port 18080

The NGINX image may still be downloading, the test container may have stopped, or another local process may own the port. Inspect the container and listener:

docker ps -a --filter name=jammy-web-check
docker logs jammy-web-check
sudo ss -ltnp | grep ':18080'

If the port is occupied, remove the failed test and repeat it with --publish 127.0.0.1:28080:80, then request http://127.0.0.1:28080. A loopback binding does not need a UFW rule.

Hardening

  • Keep Docker on its Unix socket. Never expose an unauthenticated Docker API on TCP port 2375; remote administration needs deliberate TLS configuration and narrow source-IP rules.
  • Treat membership in the docker group as administrative access. Use rootless Docker when you need a stronger privilege boundary.
  • Keep application ports on 127.0.0.1 until a TLS reverse proxy is ready. Docker-published ports can bypass ordinary UFW filtering, so review Docker’s DOCKER-USER chain before exposing a service. The VPSLake UFW firewall generator can help draft a starting ruleset.
  • If you publish a web application, allow only the ports you intend to use and keep SSH restricted to your administration source addresses. Add the matching UFW rules before exposing the container, then verify them with sudo ufw status numbered.
  • Monitor /var/lib/docker and container logs; image layers and the default JSON log driver can consume disk space even when few containers are running.

FAQ

Is Ubuntu 22.04 still supported by Docker?

Yes. Docker’s current Ubuntu instructions list Jammy 22.04 LTS as a supported release. Use the jammy suite in the repository definition even when the server has a newer minor point release.

Do I need Docker Desktop on an Ubuntu VPS?

No. Docker Engine is the server-side runtime intended for Linux VPS deployments, and it runs as a system service without a graphical desktop. Docker Desktop is a separate product aimed at desktop workflows.

Why does docker compose work but docker-compose does not?

This guide installs the Compose v2 plugin, whose current command is docker compose with a space. The older hyphenated docker-compose command is a separate legacy package and is not required for new installations.

Does Docker need a public firewall port?

No. The daemon uses a local Unix socket, and the verification container binds to 127.0.0.1. Open only the application ports you intentionally publish, and account for Docker’s packet-filtering behavior rather than assuming UFW alone controls every container port.

Samrat Ghosh
Written by

Samrat Ghosh

Founder & Infrastructure Engineer

Samrat Ghosh is the founder of VPSLake, where he builds and runs the remote desktop and VPS hosting infrastructure the company is built on. He writes hands-on guides about RDP, Windows Server, VPS management and secure remote access - the practical documentation he wishes had…

Previous Article

Busy Software on Cloud: Run BUSY on Your Own RDP

Next Article

How to Install Docker on Ubuntu 24.04 (Step-by-Step)

Write a Comment

Leave a Comment

Your email address will not be published. Required fields are marked *

Subscribe to our Newsletter

Subscribe to our email newsletter to get the latest posts delivered right to your email.
Pure inspiration, zero spam ✨