📑 Daftar Isi
- What You Need Before Starting
- Step-by-Step Docker Setup on a VPS
- 1. Update the System
- 2. Install Docker Engine
- 3. Add Your User to the docker Group
- 4. Verify the Install
- 5. Run Your First Container
- 6. Set Up Docker Compose for Multi-Container Stacks
- 7. Make Containers Survive a Reboot
- Essential Docker Commands
- Production Hardening for Docker on a VPS
- Troubleshooting Common Docker Issues on a VPS
- FAQ
How to Set Up Docker on a VPS: Complete Step-by-Step Guide for Production
Skip the small talk. You need containers running on a VPS, you need them stable, and you don’t have all day. This is the exact path I use on fresh servers — tested on Ubuntu 24.04 and Rocky Linux 9 — from a bare OS to a container stack that survives a reboot. Follow the order. It matters more than you think.
Think of it like moving into a new apartment with pre-packed boxes. Every app ships with everything it needs inside its own box. Unpack, plug in, done. No more “works on my machine” drama. That’s what we’re building here.
Now, the “why” — because if you’re here, you’ve probably hit the classic wall. Your app runs fine in dev, then you deploy to the VPS and it breaks. Different PHP version. Missing extension. Wrong folder permissions. A library that changed behavior silently between environments. Debugging that on a production box under load is miserable, and it burns hours you simply don’t have. Containerizing the app removes that entire class of problems, because the image you test locally is the exact same image that runs on the server. That consistency alone justifies the setup time, and it’s precisely why teams keep moving workloads to containers even on small single-VPS setups.
The second reason is dependency hell — I see it in real tickets constantly. A Python service pins thirty pip packages to specific versions. A PHP app on the same box needs its own runtime and extensions. A database wants dedicated resource limits. On bare metal, all of that fights over one OS. Containers give you isolation instead: each service lives in its own world with its own OS layer, dependencies, and config. Two apps needing different PHP versions on one VPS? No problem at all.
But let’s be honest before you start: containers are not magic. They share the host kernel, so a sloppy image or a container with a memory leak can still take your VPS down. You need resource limits, log rotation, and proper restart policies. That’s exactly what the second half of this guide covers. Installing Docker takes five minutes — running it safely takes the whole guide. Don’t skip the hardening and troubleshooting sections. I’ve cleaned up after enough “just pulled an image and forgot about it” disasters to know the difference.
What You Need Before Starting
- A VPS with at least 2GB RAM. 1GB works for light stacks, but you’ll fight for memory.
- A 64-bit Linux OS: Ubuntu 22.04/24.04, Debian 12, or Rocky/AlmaLinux 9.
- Root access or a sudo user.
- Your VPS public IP handy, for testing container access.
If the box isn’t hardened yet, check the SSH hardening guide first. Root login over SSH with password auth is basically an open invitation for botnets.
Step-by-Step Docker Setup on a VPS
1. Update the System
sudo apt update && sudo apt upgrade -y
On RHEL-family systems:
sudo dnf update -y
2. Install Docker Engine
Use Docker’s official repository. Don’t use the distro package — it lags behind. And absolutely do not pipe random install scripts from the internet into your shell. Ubuntu/Debian, in order:
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
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
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Rocky/Alma:
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
Note: docker-ce-cli, containerd.io, buildx, and the compose plugin aren’t optional extras. They’re part of the stack. Install the whole set, not just docker-ce, or some features will silently not work.
3. Add Your User to the docker Group
sudo usermod -aG docker $USER
Log out and back in (or run newgrp docker) so the group takes effect. Verify with groups — you should see docker in the list.
4. Verify the Install
docker --version
docker compose version
sudo systemctl status docker
Expected output, roughly:
Docker version 27.4.1, build 9c5847f
Docker Compose version v2.31.0
● docker.service - Docker Application Container Engine
Loaded: loaded (...; enabled; preset: enabled)
Active: active (running) since ...
If it’s not running, sudo systemctl start docker and check again. That fixes it nine times out of ten.
5. Run Your First Container
docker run hello-world
You’ll get the classic “Hello from Docker!” message. Then run something actually useful — Nginx on port 8080 so it doesn’t collide with anything already on port 80:
docker run -d --name nginx-test -p 8080:80 nginx:alpine
Test with curl http://IP_VPS:8080. Welcome page? Done. That’s a working Docker setup on your VPS, and honestly the fastest I’ve ever gone from zero to serving traffic on a fresh box.

6. Set Up Docker Compose for Multi-Container Stacks
mkdir -p ~/app/html && cd ~/app
nano docker-compose.yml
Minimal compose file:
services:
web:
image: nginx:alpine
container_name: web-server
restart: unless-stopped
ports:
- '8081:80'
volumes:
- ./html:/usr/share/nginx/html
docker compose up -d
That restart: unless-stopped line is what keeps the container alive across host reboots unless you stop it manually. Non-negotiable for anything touching production.
7. Make Containers Survive a Reboot
No restart policy means a host reboot kills your containers permanently. Fix existing containers:
docker update --restart unless-stopped nginx-test
Or set it at run time:
docker run -d --restart unless-stopped --name app-web -p 8082:80 nginx:alpine
Policy reference:
| Policy | Behavior | Use When |
|---|---|---|
| no | Never restarts | Testing only |
| on-failure | Restarts on error exit | One-shot jobs |
| always | Always restarts, incl. daemon start | Services that must never stop |
| unless-stopped | Restarts unless you stopped it manually | Safest default for production |
Also make sure the daemon itself starts on boot: sudo systemctl enable docker.

Essential Docker Commands
These are the commands I run daily. Memorize them slowly, they’ll stick:
| Command | What It Does |
|---|---|
| docker ps | List running containers |
| docker ps -a | List all containers, including stopped |
| docker images | List local images |
| docker pull name | Fetch an image from a registry |
| docker run -d -p 80:80 name | Start a container detached |
| docker exec -it name bash | Get a shell inside a container |
| docker logs name | View container logs |
| docker stop / start / restart | Control container lifecycle |
| docker system df | Show disk usage for images, containers, volumes |
| docker inspect name | Dump low-level container details |
Production Hardening for Docker on a VPS
Install is step one. Running it safely is step two. These are the limits I set on every production container:
- Set memory and CPU limits:
docker run -m 512m --cpus 1 .... A runaway container can drain host RAM and take everything down with it. - Enable log rotation: without it, container logs grow forever and fill the disk. Set
max-size: 10mandmax-file: 3in the logging driver config or in Compose. - Don’t run as root inside the container: use
userin the image or--userat runtime. - Pin image versions:
nginx:1.27-alpine, not a movinglatesttag in production. - Keep images updated: rebuild or re-pull on a schedule; old images carry old CVEs.
This pairs well with the VPS monitoring with Netdata guide — you want to see memory and disk trends before they bite you, not after.
Troubleshooting Common Docker Issues on a VPS
| Symptom | Cause | Fix |
|---|---|---|
| Can’t connect to the Docker daemon | User not in docker group, or daemon down | Check systemctl status docker; re-login after usermod |
| Port already in use | Another service holds the port | ss -tulpn to find the holder; change the port mapping |
| Container exits immediately | App crashes on start, or wrong CMD | docker logs name — the answer is always there |
| Permission denied | User missing from docker group | Add the user, re-login |
| Container killed by OOM | Host RAM exhausted | dmesg | tail, add memory limits, or add RAM |
| Disk full | Images and volumes piling up | docker system df, prune what’s unused |
docker system prune -a removes ALL images not used by running containers. Keep a backup of your Dockerfiles and compose files before pruning. When unsure, use docker container prune — that only removes stopped containers.FAQ
Q: What’s the difference between a Docker container and a VM?
A VM carries a full OS with its own kernel — heavy and resource-hungry. A container shares the host kernel and carries only the app and its dependencies, so it’s far lighter and boots in seconds. The trade-off: containers can’t run a different OS family than the host.
Q: Is 1GB RAM enough for Docker on a VPS?
For a light stack, yes — Nginx plus a small database fits. The catch is discipline: set memory limits on every container, or one hungry service will eat the host RAM. 2GB is noticeably more comfortable, and I’d start there for anything beyond experiments.
Q: Is Docker safe for production servers?
Yes, when configured properly. Non-negotiable items: don’t run containers as root, only use images you trust, set restart policies and resource limits, keep the engine and images updated. The danger is almost never Docker itself — it’s the sloppy way it gets deployed.
Q: Dockerfile or docker-compose.yml — what’s the difference?
A Dockerfile is the recipe for building one image — the instructions for installing your app. docker-compose.yml is the recipe for running a set of containers together — services, ports, volumes, environment. They usually work as a pair: Dockerfile builds, compose runs.
Q: How do I update an image that’s already running?
Pull the new image, stop the old container, and start it again with the new image. With Compose: docker compose pull then docker compose up -d. Check docker logs after the update, and back up anything stored in volumes first — updates that touch state deserve extra caution.
That’s the whole path — install, run, harden, troubleshoot. Try it on a fresh VPS in order, and resist the urge to skip the hardening section. If you want to keep going down the rabbit hole, the Nginx tuning on low-RAM VPS guide is a solid next read for squeezing performance out of the same box. Done.