Docker changed how small teams run software: instead of installing PHP, Node, Postgres and Redis by hand and hoping they get along, you describe the whole stack in one file and start it with a single command. The same file runs on your laptop, on a colleague's machine and on the server. All it needs is a Linux box with root access and a real kernel, which is exactly what a KVM VPS is. This guide covers why Docker and a VPS fit together, why people run their containers at RS Computers, how to install Docker properly (including the firewall mistake almost everyone makes once), a ready-to-use Compose stack with automatic HTTPS and a web UI, backups, when to step up to a small Kubernetes node, and twelve things worth self-hosting on day one.
Why containers belong on a VPS
- One file, any server. A
docker-compose.ymlcaptures every service, version, port and volume. Rebuilding the server or moving to a bigger plan isgit cloneplusdocker compose up -d. - Many apps, no conflicts. Run a PHP 7.4 legacy app next to a PHP 8.3 one, two different Postgres versions, and a Node service, each in its own container with its own dependencies.
- Updates you can undo. Pull the new image, restart, and if something breaks, pin the previous tag and restart again. The data lives in volumes and survives either way.
- Resource limits per app. Give the database 1 GB and cap the scraper at half a CPU so one misbehaving service cannot starve the rest.
- The whole self-hosting world opens up. Almost every open-source application ships an official image: Nextcloud, Gitea, n8n, Vaultwarden, Uptime Kuma, Plausible, MinIO, Mattermost and thousands more.
Why run Docker at RS Computers
- Real KVM virtual machines. Many budget "VPS" products are themselves containers (OpenVZ or LXC). Docker inside them needs special nesting support and some features never work: overlay storage drivers, memory limits, certain network modes. Every RS Computers plan is a full KVM machine with its own kernel, so Docker behaves exactly as it does on a dedicated server, with no surprises.
- NVMe storage. Image layers, build caches and database volumes are small-file, random-access workloads. NVMe makes
docker pull, image builds and database containers noticeably faster than SATA SSDs. - Unmetered traffic on a fast port. Pulling images, serving media and syncing files never run into a bandwidth cap: 1 Gb/s on VPS plans and 10 Gb/s on VDS plans.
- Your own IPv4 and IPv6. Publish services on ports of your choosing, run a mail server or a game server, and let partners allow-list a single stable address.
- Pick the OS Docker likes. Debian 12, Ubuntu 24.04, AlmaLinux and Rocky Linux are all available at order time; Debian and Ubuntu are the most tested hosts for Docker.
- Free weekly backups. A full backup of the server, volumes included, every week, restorable from the client area, on top of whatever volume backups you script yourself.
- Choose your region. Prishtina, Amsterdam or Dublin, with the plans page showing which locations currently have capacity. Keep EU data in the EU, or Kosovo data in Kosovo.
- Grow in place. When your stack outgrows the plan, upgrade vCPU, memory and disk from the client area; your containers and volumes stay where they are.
Which plan for your stack?
| Plan | Comfortable for |
|---|---|
| VPS Nano (1 vCPU, 1 GB, 20 GB NVMe) | A reverse proxy plus two or three light containers: Uptime Kuma, Vaultwarden, a static site, a Telegram bot. |
| VPS Micro (2 vCPU, 2 GB, 40 GB NVMe) | A typical self-hosting box: Caddy, Portainer, Nextcloud or Gitea, a Postgres database, n8n. Also enough for a single-node k3s with a few small services. |
| VPS Mini (4 vCPU, 4 GB, 80 GB NVMe) | Ten or more containers, a CI runner, Mattermost or Mailcow, staging copies of client projects, or a more serious k3s node. |
| VDS Small to Large (4 to 16 vCPU, 8 to 32 GB, up to 960 GB NVMe, 10 Gb/s) | Production stacks for several clients, databases with real traffic, media servers, object storage with MinIO, or a Kubernetes node that actually gets busy. |
Getting a server
- On the VPS & VDS plans page, choose a location and click Get on a plan.
- Select Debian 12 or Ubuntu 24.04. Paste your SSH public key if you have one; otherwise a root password is generated for you.
- Pay. The server is built automatically, normally within a couple of minutes, and the IP addresses and root password arrive by email.
- For anything with a web interface, create DNS
Arecords (andAAAAfor IPv6) such asportainer.example.comandstatus.example.compointing at the server.
Install Docker the right way
Step 1: Docker Engine and Compose
ssh root@YOUR_SERVER_IP
apt update && apt full-upgrade -y
curl -fsSL https://get.docker.com | sh
docker --version && docker compose version
Docker's convenience script installs the Engine, the Compose plugin and the Buildx plugin from Docker's own repository, so apt upgrade keeps them current.
Step 2: Log rotation and a non-root user
Container logs grow forever by default and have filled many a disk. Cap them once, globally:
cat > /etc/docker/daemon.json <<'EOF'
{
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" }
}
EOF
systemctl restart docker
Then create a user for daily work and add it to the docker group (which is root-equivalent, so treat that account like root):
adduser deploy
usermod -aG docker deploy
Step 3: The firewall trap
This is the mistake that bites almost everyone once. Docker writes its own iptables rules, so a port you publish with -p 5432:5432 or ports: ["5432:5432"] is reachable from the entire internet even if ufw says it is blocked. Databases, Redis instances and admin panels get exposed this way every day.
The clean solution is simple: never publish a port to 0.0.0.0 unless it is meant to be public. Bind internal services to localhost and put one reverse proxy in front of everything web-facing:
# exposed to the world: only the reverse proxy
ports:
- "80:80"
- "443:443"
# reachable only from this server (for a local tool or SSH tunnel)
ports:
- "127.0.0.1:5432:5432"
# best of all for containers that only talk to each other: no ports at all,
# they reach each other by service name on the Compose network
With that habit, ufw can stay simple:
apt install -y ufw
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable
A starter stack: Caddy, Portainer and Uptime Kuma with automatic HTTPS
This gives you a reverse proxy that obtains certificates by itself, a web UI to manage containers from your phone, and a status page that watches your other services. Create /opt/stack/docker-compose.yml, replacing the two hostnames:
services:
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
portainer:
image: portainer/portainer-ce:lts
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
uptime-kuma:
image: louislam/uptime-kuma:1
restart: unless-stopped
volumes:
- kuma_data:/app/data
volumes:
caddy_data:
caddy_config:
portainer_data:
kuma_data:
Notice that Portainer and Uptime Kuma publish no ports at all. Only Caddy is reachable from outside, and it forwards by hostname. Create /opt/stack/Caddyfile:
portainer.example.com {
reverse_proxy portainer:9000
}
status.example.com {
reverse_proxy uptime-kuma:3001
}
Start it and watch the certificates arrive:
cd /opt/stack && docker compose up -d
docker compose logs -f caddy
Open https://portainer.example.com within a few minutes of starting (Portainer locks its first-run setup page after a timeout; if that happens, docker compose restart portainer) and create the admin account. From now on you can add any application as a new Compose "stack" in Portainer's UI, and give it a hostname by adding three lines to the Caddyfile followed by docker compose restart caddy.
Everyday operations
Updating
cd /opt/stack && docker compose pull && docker compose up -d
docker image prune -f
Pin image tags for anything important (postgres:16, not postgres:latest) so an update never jumps a major version by accident. Tools such as Watchtower can automate pulls, but for databases a monthly manual update is the safer habit.
Backing up volumes
Your plan's free weekly backup covers the whole server. For application-level copies you control, archive a volume with a throwaway container:
docker run --rm -v stack_portainer_data:/data -v /root/backups:/backup alpine \
tar czf /backup/portainer-$(date +%F).tgz -C /data .
For databases, dump rather than copy files: docker compose exec -T db pg_dump -U app app | gzip > /root/backups/db-$(date +%F).sql.gz. Put either in a cron job or an n8n workflow and ship the files to object storage off the server.
Limits and health
services:
worker:
image: ghcr.io/example/worker:1.4
restart: unless-stopped
mem_limit: 512m
cpus: "0.5"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
docker stats shows live usage per container, and Portainer graphs it. If you are regularly near the memory ceiling, that is the moment to upgrade the plan rather than to add swap.
When Compose is not enough: a small Kubernetes node with k3s
For most self-hosters and small businesses, Docker Compose is the right tool for years. Kubernetes starts to pay off when you need rolling deployments with health-based rollbacks, declarative configuration managed in Git, Helm charts for complex applications, or the same manifests in development and production. k3s is a lightweight, fully compliant Kubernetes distribution that fits on a single VPS and comes with the Traefik ingress controller and local storage already configured:
curl -sfL https://get.k3s.io | sh -
kubectl get nodes
kubectl create deployment hello --image=nginx:1.27 --port=80
kubectl expose deployment hello --port=80
kubectl get pods -o wide
VPS Micro is enough to learn and run a few small services; VPS Mini or a VDS is a comfortable single-node production host. When you later add a second server, it joins the same cluster with one command. One honest note: a single node is not high availability, so keep the weekly backups and treat it as a very capable single server.
Twelve things worth self-hosting with Docker
- Nextcloud: files, calendar and contacts for the team, with your data in your own data centre.
- Gitea or GitLab: private Git hosting, issues and CI runners, without per-seat pricing.
- n8n: automation and AI agents with unlimited executions (see our n8n guide).
- Vaultwarden: a Bitwarden-compatible password manager for the whole company on a tiny footprint.
- Uptime Kuma: monitoring and a public status page for your sites and services.
- Plausible or Umami: privacy-friendly website analytics with no cookie banner headaches.
- WordPress and other sites: each site in its own container with its own PHP version, all behind one Caddy or Traefik (or see the WordPress on a VPS guide for the control-panel route).
- Postgres, MySQL, Redis: databases for your applications, bound to localhost and backed up by cron.
- MinIO: S3-compatible object storage for backups and application uploads, on a VDS with lots of NVMe.
- Mattermost or Rocket.Chat: team chat you own, with a WireGuard tunnel (see our WireGuard guide) if you want it private.
- Game servers: Minecraft (
itzg/minecraft-server), Valheim, Palworld and others ship as images and take minutes to start. - CI runners and staging: a GitHub Actions or GitLab runner in a container, plus throwaway staging copies of client projects on their own subdomains.
Security checklist
- Publish only 80 and 443; bind everything else to
127.0.0.1or to no port at all. - Keep the host updated with
unattended-upgrades; update images monthly with pinned tags. - Use SSH keys only, and remember the
dockergroup equals root. - Give containers only the volumes they need; mount config files read-only (
:ro). - Put admin UIs (Portainer, databases' web tools) behind Caddy with an extra
basic_authblock or behind your WireGuard VPN. - Set memory limits so a leak in one container cannot take the server down.
- Back up volumes off the server and test a restore once; the weekly server backup is your second line.
Frequently asked questions
Does Docker work on all your plans?
Yes. Every plan is a KVM virtual machine with its own kernel, so Docker, Compose, Podman and k3s all run without special configuration, even on VPS Nano.
Can I run Docker on Windows Server?
Windows containers are possible on Windows Server (available on VPS Mini and all VDS plans), but the vast majority of images are Linux images. For Docker, order Debian or Ubuntu.
How many containers fit on VPS Micro?
It depends on the containers, not the count. Light services use 30 to 100 MB each, so a reverse proxy plus eight or ten small apps is realistic on 2 GB. A Java application or a large database changes the maths; watch docker stats.
Should I start with Compose or Kubernetes?
Compose. Learn it, run with it, and move to k3s when you feel a specific need such as rolling updates or Helm charts. Everything you learn about images, volumes and networks carries over.
Can RS Computers set this up for me?
Yes. Message us on Telegram or email info@rscomputers-ks.com with what you want to run and we will quote a setup, including the reverse proxy, HTTPS and backups.
Conclusion
A KVM VPS with NVMe storage and your own IP is the natural home for containers: Docker runs the way it was designed to, the whole stack is one file you can rebuild anywhere, and the apps the open-source world has built are a docker compose up away. Start with the Caddy and Portainer stack above, bind nothing to the public internet except the proxy, keep the weekly backups, and add services as you need them. Pick a plan, and your first container can be running before the coffee is cold.