Hoe om toepassings met Docker Compose op 'n VPS te ontplooi
'n Praktiese produksiegids tot Docker Compose v2 op Ubuntu: installeer die enjin, skryf 'n Compose-lêer met netwerke en volumes, hanteer geheime, sit 'n reverse proxy voor, dateer op met nul-paniek-uitrol, en rugsteun genoemde volumes.

Docker Compose is die gewone manier om 'n klein stapel op een VPS te laat loop: 'n webtoepassing, 'n databasis, 'n Redis-kas, en 'n reverse proxy, beskryf in een YAML-lêer. Dit is nie Kubernetes nie. Jy sal nie multi-node-oorname kry nie. Jy sal 'n herhaalbare, hersienbare opstelling kry wat jy binne minute op 'n nuwe Hiddence-VPS kan herskep — juis wat die meeste syprojekte en klein produkte nodig het.
Hierdie artikel aanvaar jy wil iets nader aan produksie hê as docker run op 'n Vrydagaand. Ons installeer Docker Engine en die Compose-inprop, skep 'n nie-root ontplooi-gebruiker, skryf 'n Compose-lêer met gesondheidskontroles en herbeginbeleide, hou geheime uit Git, publiseer slegs die reverse-proxy-poorte na die internet, en definieer 'n opdaterings- en rugsteunroetine. Die voorbeeldstapel is 'n tipiese webtoepassing plus PostgreSQL plus Caddy, maar dieselfde patroon werk vir Node, PHP, Python, of 'n klomp werkers.
Hoekom Compose op 'n enkele VPS steeds sin maak
Mense spring na Kubernetes omdat dit modes is, en spandeer dan die naweek aan YAML vir een webwerf. Compose bly leesbaar. Jy weergawes die lêer, jy dokumenteer omgewingsveranderlikes, en jy kan 'n verandering diff voordat jy dit toepas. Isolasie is goed genoeg: 'n gekompromitteerde toepassinghouer behoort nie die databasis se UNIX-soket te hou as jy netwerke en minste-voorreg-gebruikers gebruik het nie. Hulpbronlimiete keer dat een geheuelek die hele VPS vries.
- Een lêer beskryf die hele stapel
- Genoemde volumes oorleef houerherskepping
- Interne Docker-netwerke hou Postgres van die publieke internet af
- restart: unless-stopped dek die meeste herbeginne
- Maklik om na 'n tweede VPS te kopieer wanneer jy die eerste ontgroei
Vereistes
Gebruik 'n VPS met genoeg RAM vir die toepassing plus Postgres. 'n 2 GB-plan is 'n realistiese minimum vir toepassing + databasis + proxy. Ubuntu 24.04 word aanvaar. Moenie Docker vanaf 'n ewekansige Snap installeer sonder om te lees wat dit aan cgroups doen nie; die stappe hieronder gebruik Docker se amptelike apt-bewaarplek.
- Ubuntu 22.04/24.04, root of sudo
- 2 GB RAM aanbeveel (1 GB slegs vir piepklein stapels sonder Postgres)
- 'n Domein wat na die VPS wys as jy outomatiese HTTPS wil hê
- Git of 'n ander manier om die projek na die bediener te kopieer
Stap 1: Installeer Docker Engine en Compose v2
Compose v2 is 'n inprop wat as docker compose (met 'n spasie) aangeroep word, nie die ou docker-compose Python-binêre nie. Installeer albei vanaf Docker se bewaarplek sodat jy huidige sekuriteitskolle kry.
ssh root@YOUR_VPS_IP
apt update && apt -y install ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/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/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt update
apt -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
docker version
docker compose versionStap 2: Ontplooi-gebruiker en gidsuitleg
Om Compose as root te laat loop werk, maar 'n toegewyde gebruiker met lidmaatskap in die docker-groep is skoner. Sit die projek in /opt of /srv, nie in /root nie, sodat rugsteune en toestemmings duidelik is. Moet nooit die gids wat .env bevat wêreld-skryfbaar maak nie.
adduser --disabled-password --gecos '' deploy
usermod -aG docker deploy
mkdir -p /srv/app
chown deploy:deploy /srv/app
chmod 750 /srv/app
# Log in as deploy for the rest of the file editing:
# su - deploy
# cd /srv/appStap 3: Skryf 'n produksie-ingestelde Compose-lêer
Die lêer hieronder is 'n sjabloon. Vervang die toepassingbeeld met joune. Postgres word nie na 0.0.0.0:5432 gepubliseer nie — slegs Caddy word op 80 en 443 gepubliseer. Gesondheidskontroles keer dat 'n reverse proxy verkeer stuur na 'n toepassing wat nog die databasis migreer. Pin beeldetikette; latest is hoe verrassingsbreuke gebeur.
# /srv/app/compose.yaml
services:
caddy:
image: caddy:2.8-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
depends_on:
app:
condition: service_healthy
app:
image: ghcr.io/example/webapp:1.4.2
restart: unless-stopped
env_file: .env
environment:
DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8000/health"]
interval: 10s
timeout: 3s
retries: 10
networks:
- frontend
- backend
db:
image: postgres:16-alpine
restart: unless-stopped
env_file: .env
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 10
networks:
- backend
networks:
frontend: {}
backend: {}
volumes:
pgdata:
caddy_data:
caddy_config:Stap 4: Geheime, .env en Caddyfile
Sit wagwoorde in .env op die bediener, chmod 600, en commit nooit daardie lêer nie. Genereer wagwoorde met openssl rand -base64 32. Die Caddyfile hoef slegs na die toepassing se diensnaam op die Docker-netwerk te reverse-proxy.
# /srv/app/.env (permissions 600)
POSTGRES_USER=app
POSTGRES_PASSWORD=change-me-long-random
POSTGRES_DB=app
# plus any APP_SECRET / NEXTAUTH_SECRET your image needs
# /srv/app/Caddyfile
app.example.com {
encode gzip
reverse_proxy app:8000
}
chmod 600 /srv/app/.env
cd /srv/app
docker compose up -d
docker compose ps
docker compose logs -f --tail=100Stap 5: Firewall, logboeke en hulpbronlimiete
UFW moet slegs 22, 80 en 443 toelaat. Docker omseil soms UFW vir gepubliseerde poorte; as jy 'n streng gasheer-firewall nodig het, soek die huidige Docker + UFW-wisselwerking vir jou Ubuntu-weergawe op, of publiseer poorte slegs op 127.0.0.1 en sit 'n gasheer-Caddy voor. Stel geheuelimiete in Compose sodat Postgres nie die VPS kan opeet nie.
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
# Optional in compose.yaml under app or db:
# deploy:
# resources:
# limits:
# memory: 512M
# Logs (do not let json-file grow forever):
# { "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }
# in /etc/docker/daemon.json then: systemctl restart dockerStap 6: Opdaterings en terugrol
Die vervelige opdatering wat werk: trek nuwe beelde, up -d, dophou gesondheidskontroles, hou die vorige etiket in git sodat jy terug kan pin. Moenie docker compose down op 'n produksiedatabasis doen nie tensy jy van plan is om verkeer te stop. down -v vee volumes uit — dit is 'n uitvee, nie 'n herbegin nie.
cd /srv/app
git pull # if the Compose file lives in git
# Change the image tag in compose.yaml, then:
docker compose pull
docker compose up -d
docker compose ps
# Rollback: set the old tag, pull, up -d again
# NEVER: docker compose down -v # this deletes named volumesRugsteun genoemde volumes
'n VPS-momentopname is goed. 'n Logiese Postgres-dump is beter as jy op 'n ander masjien moet herstel. Skeduleer dit met cron. Toets 'n herstel een keer, anders het jy nie rugsteune nie — jy het lêers wat jy hoop rugsteune is.
# Postgres dump while the db container is running:
docker compose exec -T db pg_dump -U app app | gzip > /var/backups/app-$(date +%F).sql.gz
# Copy off-box
# scp /var/backups/app-*.sql.gz backup-host:~
# Restore sketch (maintenance window):
# gunzip -c app-2026-08-19.sql.gz | docker compose exec -T db psql -U app appFoutopsporing
Lees docker compose ps en docker compose logs service. Beeldtrek-mislukkings is gewoonlik ghcr/docker hub-tempolimiete of 'n private beeld sonder 'n aanmelding. 502 vanaf Caddy beteken die toepassing is nie gesond nie of die proxy-gasheernaam is verkeerd (gebruik die Compose-diensnaam, nie localhost nie, van binne die Caddy-houer). Toestemmingfoute op volumes beteken dikwels die beeld loop as uid 1000 terwyl die gasheergids root is.
- compose: command not found — jy het die ou binêre naam geïnstalleer; gebruik docker compose
- poort is al toegeken — iets anders besit 80/443 (Apache, Nginx, 'n ander Caddy)
- databasisverbinding geweier — toepassing het begin voordat Postgres gereed was; gebruik healthcheck + depends_on-voorwaarde
- skyf vol — docker system df, dan prune ongebruikte beelde versigtig
- toestemming geweier op docker.sock — gebruiker nie in docker-groep nie, of jy het 'n nuwe aanmeldsessie nodig
Sekuriteit
Moenie databasispoorte publiseer nie. Moenie privileged: true laat loop nie. Moenie /var/run/docker.sock in 'n toepassinghouer monteer nie tensy jy doelbewus 'n houerbestuurder skryf. Hou die enjin opgedateer. Skandeer jou eie beelde as jy hulle bou. .env op skyf is steeds 'n geheimlêer — beperk wie na die boks kan ssh.
- Geen publieke 5432 / 3306 / 6379 nie
- Pin beeld-digests of ten minste onveranderlike etikette
- chmod 600 .env
- Unattended-upgrades vir die gasheer-OS saak nog steeds
- Een Compose-projek per toepassing hou die ontploffingsradius kleiner
Wenke
- Sit compose.yaml in git; hou .env.example sonder ware geheime
- Gebruik profiele vir opsionele werkers (docker compose --profile workers up -d)
- Dophou docker stats wanneer jy die VPS grootte gee
- Vir bind-mounts van bronkode is dit nie die produksiepatroon nie — bak 'n beeld
- As die stapel tot verskeie VPS'e groei, kyk dan na orkestrasie — nie vroeër nie
'n Produksie-agtige Compose-opstelling op een VPS is: amptelike Docker-pakkette, 'n toegeslote .env, 'n YAML-lêer wat nie die databasis publiseer nie, gesondheidskontroles, 'n reverse proxy op 80/443, UFW, beeldetikette wat jy kan terugrol, en Postgres-dumps wat jy werklik een keer herstel het. Begin vanaf die sjabloon in hierdie artikel, vervang die toepassingbeeld, en behandel docker compose down -v as 'n vernietigende opdrag met dieselfde respek as rm -rf.