Docker packages an application with everything it needs to run. On a VPS, that means installing n8n, a PostgreSQL database, a Discord bot or a monitoring tool with one command, with no version conflicts between them, and moving them from one server to another without reinstalling anything. Docker Compose describes the whole setup in a single readable file.
1. Why Docker on a VPS
- Isolation: each application has its own libraries. Updating one does not break another.
- Reproducibility: the
compose.yamlfile describes the whole deployment. The same file works on another server. - Catalogue: most open-source software publishes an official, ready-to-use image.
The trade-off: a little more memory, and a few networking pitfalls described below. For a VPS running several services, the balance is overwhelmingly positive.
2. Install Docker Engine from the official repository
The distributions' docker.io packages are often several versions behind and do not always include Compose. Install Docker's official version. First remove any old packages:
for p in docker.io docker-doc docker-compose podman-docker containerd runc; do
apt remove -y $p 2>/dev/null
done
Then add Docker's repository and install:
apt update && apt install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
. /etc/os-release
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 update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
The $ID variable is ubuntu or debian depending on your system: the same command works on both.
3. Check the installation
docker run --rm hello-world
docker compose version
The first command downloads a test image and prints a welcome message. Note the docker compose syntax, with a space: it is the current version, built into Docker. The old docker-compose command with a hyphen is no longer maintained.
4. User and permissions
To use Docker without sudo, add your user to the docker group, then log in again:
usermod -aG docker alex
docker group is equivalent to being root on the server: a member can mount any folder of the system into a container. Only add trusted accounts.5. First project with Docker Compose
Each project lives in its own folder, with a compose.yaml file. Example: the monitoring tool Uptime Kuma, which checks that your sites respond.
mkdir -p /opt/uptime-kuma && cd /opt/uptime-kuma
cat > compose.yaml <<'EOF'
services:
kuma:
image: louislam/uptime-kuma:1
restart: unless-stopped
volumes:
- ./data:/app/data
ports:
- "127.0.0.1:3001:3001"
EOF
docker compose up -d
Three choices in this file are good practices to reuse everywhere:
- A specific image version (
:1) rather than:latest, so a major update is not installed without your decision. - A volume (
./data) for the data: a container can be deleted and recreated at any time; only the volume persists. - A port bound to
127.0.0.1: the application is only reachable from the server, and will be published cleanly over HTTPS by Nginx.
"3001:3001" is reachable from the Internet, even if UFW does not allow it. Bind everything that is not meant to be public to 127.0.0.1. Details in configuring UFW.6. Expose an application over HTTPS
The simplest approach is to use Nginx installed on the server as the single entry point, with one block per application:
server {
server_name status.mysite.com;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
listen 80;
}
The two Upgrade and Connection lines are required for applications that use WebSockets, such as Uptime Kuma or n8n. Then get the certificate with Certbot: see Nginx and HTTPS. For a complete example with a database, see installing n8n on a VPS with Docker (in French).
7. Limit log size
By default, Docker keeps container logs with no size limit. A chatty container can fill the disk in a few weeks. Set a global limit:
cat > /etc/docker/daemon.json <<'EOF'
{
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" }
}
EOF
systemctl restart docker
The limit applies to containers created after this change. Recreate existing containers with docker compose up -d --force-recreate.
8. Update containers
cd /opt/uptime-kuma
docker compose pull
docker compose up -d
docker image prune -f
Compose only recreates the containers whose image has changed; the last command deletes old unused images. Read the release notes before moving to a new major version, especially for databases.
9. Back up
Back up the project folder, which contains the Compose file and the volumes. For a database, do not copy the raw files while it is running: export it from the container first.
docker compose exec -T postgres pg_dump -U n8n n8n | gzip > /var/backups/n8n.sql.gz
Adapt the service, user and database names to your project, then send everything off the server: see backups with Borg.
10. Useful commands
docker compose ps # status of the project's services
docker compose logs -f --tail 100 # live logs
docker compose restart kuma # restart one service
docker compose down # stop and remove containers (not volumes)
docker stats # live CPU and memory usage
docker system df # disk space used by Docker
Frequently asked questions
Does Docker need a lot of memory?
Docker itself uses little, in the order of a few tens of megabytes. What counts is the sum of the applications. A 2 GB VPS comfortably runs two or three light services; plan for 4 GB or more if you add a database and tools like n8n.
docker-compose or docker compose?
docker compose, with a space. It is version 2, built into Docker as a plugin. The old docker-compose program, written in Python, is no longer maintained. The files remain compatible.
Does Docker work on every VPS?
On a VPS based on full KVM virtualisation, yes, without restriction, since you have your own Linux kernel. Lightweight container solutions such as OpenVZ, on the other hand, often cause problems.
How do two containers talk to each other?
Services declared in the same Compose file share an internal network and reach each other by service name, for example postgres:5432. There is no need to publish a port for this, and it is better for security not to.



