Install Docker and docker compose on a VPS: Step by Step

05 Apr 2026 By Olga Naumova

Run on the server with one command (check the script contents below first):

curl -sSL https://cp.lv/scripts/ustanovit-docker-vps.sh | sudo bash

…or with wget:

wget -qO- https://cp.lv/scripts/ustanovit-docker-vps.sh | sudo bash

Script contents ustanovit-docker-vps.sh:

#!/usr/bin/env bash
set -e
export DEBIAN_FRONTEND=noninteractive

# 1. Remove old versions
sudo apt-get remove -y docker docker-engine docker.io containerd runc 2>/dev/null || true

# 2. Official Docker repository
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
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

# 3. Install Docker Engine and plugins
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker

# 4. Run without sudo (takes effect after a new SSH login)
sudo usermod -aG docker $USER

# 5. Cap container log size
sudo tee /etc/docker/daemon.json > /dev/null << 'EOF'
{
  "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" }
}
EOF
sudo systemctl restart docker

# 6. Verify
sudo docker run hello-world
docker compose version
echo "Docker is ready. Log in over SSH again to use it without sudo."

Docker packs an application and its dependencies into a container that behaves the same on any server. Below is how to install Docker Engine and the docker compose plugin from the official repository on Ubuntu 22.04/24.04. Do not use the docker.io package from the standard Ubuntu repository — it is often outdated.

Step 1. Remove old versions

sudo apt remove docker docker-engine docker.io containerd runc 2>/dev/null
sudo apt update

Step 2. Add the official Docker repository

sudo apt install ca-certificates curl gnupg -y
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

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

Step 3. Install Docker Engine and the plugins

sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin -y

Enable autostart and check that it works:

sudo systemctl enable --now docker
sudo docker run hello-world

If you see “Hello from Docker!”, the engine is running.

Step 4. Run Docker without sudo

Add your user to the docker group so you do not have to type sudo before every command:

sudo usermod -aG docker $USER
newgrp docker
docker ps

The group change takes effect after a new SSH session.

Step 5. Check docker compose

In modern Docker, compose is a subcommand rather than a separate binary:

docker compose version

Create a simple stack. The docker-compose.yml file:

services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    restart: unless-stopped
    volumes:
      - ./site:/usr/share/nginx/html:ro

Start it and check:

mkdir site && echo "<h1>It works</h1>" > site/index.html
docker compose up -d
curl http://localhost:8080

Stop and remove the stack:

docker compose down

Everyday commands

Task Command
List containers docker ps -a
Service logs docker compose logs -f web
Update images docker compose pull && docker compose up -d
Clean up junk docker system prune -a
Disk usage docker system df

Updates and security

  • Cap the log size so containers do not fill the disk. Open /etc/docker/daemon.json in an editor:
sudo nano /etc/docker/daemon.json

File contents:

{
  "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" }
}

Or create the file with a single command — copy it and paste it into the console:

sudo tee /etc/docker/daemon.json > /dev/null << 'EOF'
{
  "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" }
}
EOF

After editing, run sudo systemctl restart docker.

  • Do not publish database ports (5432, 3306) to the outside without a reason — keep them on the internal compose network.
  • Update images regularly: vulnerabilities usually live in outdated versions.

FAQ

What is the difference between docker-compose and docker compose? The old docker-compose (with a hyphen) is the standalone Python binary v1 and is no longer supported. The new docker compose (plugin v2) ships with the engine and is the recommended one.

Does Docker need swap? On a server with little RAM, swap helps survive peaks during image builds. 1–2 GB of swap is usually enough.

How do I start containers automatically after a reboot? Set restart: unless-stopped in the compose file and make sure the Docker service is enabled: systemctl is-enabled docker.

Docker filled the whole disk — how do I clean it? docker system prune -a removes unused images, containers and networks. Volumes are removed with the --volumes flag (be careful with your data).

Docker likes a fast disk and spare memory for builds. Suitable configurations are on the VPS hosting page, and for containerised projects there is Docker VPS.

Olga Naumova