Docker is the foundation of almost every modern self-hosted stack, every CI/CD pipeline, and a growing share of production deployments. Installing it on Ubuntu 22.04 looks deceptively simple — a one-line install script will technically work — but the right way involves the official repository, a non-root user setup, and a few daemon settings that prevent surprises later. This guide walks through it properly. Should take you about ten minutes.
Prerequisites: Ubuntu 22.04 VPS with KVM virtualization (Docker breaks weirdly on OpenVZ — see why), a non-root user with sudo access, and SSH access. OliveVPS plans ship Ubuntu 22.04 with KVM by default.
Steps in this guide
Step 1: Update your system
Always start with a fully patched system. SSH into your VPS and run:
sudo apt update
sudo apt upgrade -y
If the kernel updated, reboot before continuing:
sudo reboot
Wait 30 seconds, SSH back in, and continue.
Step 2: Add the official Docker repository
Don't use the docker.io package from Ubuntu's default repository — it's almost always outdated and missing Docker Compose v2. Use Docker's official repository instead.
Install the prerequisite packages:
sudo apt install -y ca-certificates curl gnupg lsb-release
Add Docker's official GPG key:
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
Add the repository to apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
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 update
Step 3: Install Docker Engine and Compose
One command gets you everything:
sudo apt install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
The five packages, in order: the Docker daemon, the CLI, the container runtime, BuildKit (modern image building), and Docker Compose v2 as a CLI plugin. Compose v2 is invoked as docker compose (with a space), not docker-compose (the legacy v1).
Start and enable the Docker daemon to run on boot:
sudo systemctl enable --now docker
Step 4: Add your user to the docker group
By default, Docker requires sudo for every command. Adding your user to the docker group means you can run docker without sudo.
sudo usermod -aG docker $USER
Security note: Membership in the docker group is effectively root access. Anyone in the group can mount the host filesystem into a container and modify anything. Only add users who should have full administrative control of the box.
The group change doesn't apply to your current session. Either log out and back in, or activate it now:
newgrp docker
Step 5: Verify the installation
Run the canonical hello-world test:
docker run --rm hello-world
You should see Docker pull the hello-world image and print a friendly success message. If you got "Hello from Docker!" you're done with the install. The --rm flag deletes the container after it exits, which keeps things tidy.
Check your version:
docker --version
docker compose version
Both should report current versions (Docker Engine 27.x or later, Compose v2.x).
Step 6: Configure the Docker daemon properly
The default Docker daemon config is fine for laptops and not great for production VPS use. Two changes worth making:
Limit log file size
By default, Docker logs container stdout/stderr forever, in a single JSON file per container. A chatty container can fill your VPS disk in days. Cap the log size.
Edit (or create) /etc/docker/daemon.json:
sudo nano /etc/docker/daemon.json
Paste:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"default-address-pools": [
{ "base": "172.17.0.0/16", "size": 24 }
]
}
This caps logs at 10MB per file and keeps 3 files (30MB total per container). The default-address-pools override prevents Docker from grabbing IP ranges that conflict with VPN subnets — a common gotcha on VPSes also running WireGuard.
Restart Docker:
sudo systemctl restart docker
Enable BuildKit by default (already on in modern Docker)
Recent Docker versions enable BuildKit automatically. If you want to confirm:
docker info | grep -i buildx
Step 7: Run your first container
Let's run something more useful than hello-world. An Nginx container serving a default page:
docker run -d \
--name test-nginx \
-p 8080:80 \
--restart unless-stopped \
nginx:alpine
Breakdown of the flags:
-d: detached mode (run in background)--name test-nginx: give the container a friendly name-p 8080:80: map host port 8080 to container port 80--restart unless-stopped: auto-restart on host reboot or container crashnginx:alpine: the image (using Alpine Linux for the smaller size)
Visit http://your-vps-ip:8080 in your browser. You should see the Nginx welcome page. Congratulations — you have a running container.
Stop and remove it when you're done:
docker stop test-nginx
docker rm test-nginx
Need a VPS for Docker workloads?
NVMe storage, KVM virtualization (Docker actually works), dedicated cores, and 20 regions worldwide. Starting at $3.99/mo.
See VPS Plans →Common issues and fixes
"Got permission denied while trying to connect to the Docker daemon socket"
You forgot to log out and back in after adding yourself to the docker group. Either do that, or run newgrp docker in your current session.
"Cannot connect to the Docker daemon"
Either Docker isn't running (sudo systemctl start docker), or there's a startup failure. Check logs:
sudo journalctl -u docker --since "1 hour ago"
The most common culprit on a fresh VPS is a malformed /etc/docker/daemon.json — a stray comma or unquoted string. JSON is strict.
"Network already exists" when running compose
You're trying to start a stack twice. Either docker compose down first, or use docker compose up -d which is idempotent.
Containers can't reach the internet
Usually a kernel module issue. On a real KVM VPS, this works out of the box. On OpenVZ "VPS" instances, this is one of the many things that breaks because the kernel isn't yours. Verify with systemd-detect-virt — if it says openvz, your problem isn't fixable without changing hosts.
"No space left on device"
Docker's image and container storage builds up over time. Clean it:
docker system prune -a --volumes
Note the --volumes flag deletes any volumes not currently in use — make sure you're not actively using one before running it.
FAQ
What's the difference between Docker Engine and Docker Desktop?
Docker Engine is the headless server-side daemon — what you install on a Linux VPS. Docker Desktop is a Windows/Mac application that bundles the engine inside a Linux VM plus a GUI. On servers, you only ever want the Engine.
Should I use docker-compose or docker compose?
Use docker compose (with a space). That's Compose v2, integrated into Docker as a plugin. The hyphenated docker-compose is v1, written in Python, and end-of-lifed years ago. Modern installs only ship v2.
Is rootless Docker worth it?
For shared/multi-user systems, yes. For a single-user VPS, the security benefit is small (anyone with shell access can already do anything). Rootless Docker is more complicated to set up and has known performance and feature limitations. We don't recommend it as a default for personal VPS use.
How much RAM does Docker itself use?
The Docker daemon itself is small — about 50-150MB resident. Containers each add their own memory footprint. On a 1GB VPS, you can comfortably run 3-5 small containers. On a 2GB VPS, plenty of room for typical self-hosted stacks.
Can I install Docker on Ubuntu 24.04 the same way?
Yes. The instructions above work identically on 24.04 — the package and repository names are the same. Older versions (20.04, 18.04) also work but you should be migrating off those.