Kako raspoređivati aplikacije s Docker Compose na VPS
Praktičan produkcijski vodič za Docker Compose v2 na Ubuntu: instalacija mehanizma, Compose datoteka s mrežama i volumenima, tajne, obrnuti proxy ispred, ažuriranja bez panike i sigurnosne kopije imenovanih volumena.

Docker Compose uobičajen je način da na jednom VPS vodite mali stog: web aplikacija, baza, Redis predmemorija i obrnuti proxy u jednoj YAML datoteci. To nije Kubernetes. Nećete dobiti failover na više čvorova. Dobit ćete ponovljivo, pregledivo postavljanje koje za minute podignete na novom Hiddence VPS — upravo ono što većini sporednih projekata i malih proizvoda treba.
Ovaj članak pretpostavlja da želite nešto bliže produkciji od docker run u petak navečer. Instalirat ćemo Docker Engine i Compose dodatak, napraviti korisnika za raspoređivanje bez root u svakodnevici, napisati Compose datoteku s healthcheckovima i politikom restarta, tajne držati izvan Gita, na internet objaviti samo portove obrnutog proxyja i opisati rutinu ažuriranja i kopije. Primjer stoga je tipična web aplikacija plus PostgreSQL plus Caddy, ali isti obrazac radi za Node, PHP, Python ili skup radnika.
Zašto Compose na jednom VPS još ima smisla
Ljudi skaču na Kubernetes jer je moderno, pa vikend provedu nad YAML-om za jedan web. Compose ostaje čitljiv. Verzionirate datoteku, dokumentirate varijable okruženja i možete vidjeti diff izmjene prije primjene. Izolacija je dovoljna: kompromitirani kontejner aplikacije ne bi trebao držati UNIX utičnicu baze ako ste koristili mreže i korisnike s najmanjim privilegijama. Limiti resursa sprječavaju jedno curenje memorije da zamrzne cijeli VPS.
- Jedna datoteka opisuje cijeli stog
- Imenovani volumeni prežive ponovno stvaranje kontejnera
- Unutarnje Docker mreže drže Postgres izvan javnog interneta
- restart: unless-stopped pokriva većinu ponovnih pokretanja
- Lako se kopira na drugi VPS kada prvi postane tjesan
Zahtjevi
Koristite VPS s dovoljno RAM za aplikaciju plus Postgres. Plan od 2 GB realističan je minimum za aplikaciju + bazu + proxy. Pretpostavlja se Ubuntu 24.04. Ne instalirajte Docker iz slučajnog Snap-a bez čitanja što radi s cgroups; koraci ispod koriste službeni Docker apt repozitorij.
- Ubuntu 22.04/24.04, root ili sudo
- Preporučeno 2 GB RAM (1 GB samo za sićušne stogove bez Postgres)
- Domena koja pokazuje na VPS ako želite automatski HTTPS
- Git ili drugi način da kopirate projekt na poslužitelj
Korak 1. Instalirajte Docker Engine i Compose v2
Compose v2 dodatak je koji se poziva kao docker compose (s razmakom), ne stari python binarni docker-compose. Instalirajte oba iz Docker repozitorija da dobivate aktualne sigurnosne zakrpe.
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 versionKorak 2. Korisnik za raspoređivanje i raspored direktorija
Compose kao root radi, ali namjenski korisnik u grupi docker čišći je. Projekt stavite u /opt ili /srv, ne u /root, da su kopije i dopuštenja očita. Nikad ne dajte svijetu pisanje u direktorij s .env.
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/appKorak 3. Napišite Compose datoteku s produkcijskom pameti
Datoteka ispod predložak je. Zamijenite image aplikacije svojim. Postgres nije objavljen na 0.0.0.0:5432 — van ide samo Caddy na 80 i 443. Healthcheckovi sprječavaju obrnuti proxy da šalje promet aplikaciji koja još migrira bazu. Pričvrstite oznake imagea; latest je kako dolaze iznenadna pucanja.
# /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:Korak 4. Tajne, .env i Caddyfile
Lozinke stavite u .env na poslužitelju, chmod 600, i nikad ne commitajte tu datoteku. Generirajte lozinke s openssl rand -base64 32. Caddyfile treba samo obrnuto proksirati na ime usluge aplikacije u Docker mreži.
# /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=100Korak 5. Vatrozid, zapisnici i limiti resursa
UFW treba dopustiti samo 22, 80 i 443. Docker ponekad zaobiđe UFW za objavljene portove; ako treba strogi vatrozid na hostu, pogledajte aktualnu interakciju Docker + UFW za vaš Ubuntu ili objavite portove samo na 127.0.0.1 i stavite Caddy na host. Postavite limite memorije u Compose da Postgres ne pojede VPS.
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 dockerKorak 6. Ažuriranja i vraćanja
Dosadno ažuriranje koje radi: povucite nove imagee, up -d, gledajte healthcheckove, držite prethodnu oznaku u gitu da se vratite. Ne radite docker compose down na produkcijskoj bazi osim ako namjeravate prestati primati promet. down -v briše volumene — to je brisanje, ne restart.
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 volumesNapravite sigurnosne kopije imenovanih volumena
Snimka VPS dobra je. Logički dump Postgresa bolji je ako vraćate na drugo računalo. Zakažite to cronom. Jednom testirajte vraćanje, inače nemate kopije — imate datoteke za koje se nadate da su kopije.
# 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 appRješavanje problema
Čitajte docker compose ps i docker compose logs usluga. Neuspjesi povlačenja imagea obično su limiti ghcr/Docker Hub ili privatni image bez prijave. 502 od Caddyja znači da aplikacija nije zdrava ili je hostname proxyja pogrešan (iz Caddy kontejnera koristite ime Compose usluge, ne localhost). Pogreške dopuštenja na volumenima često znače da image radi kao uid 1000 dok je direktorij na hostu root.
- compose: command not found — instalirali ste staro ime binarne datoteke; koristite docker compose
- port is already allocated — nešto drugo drži 80/443 (Apache, Nginx, drugi Caddy)
- database connection refused — aplikacija je krenula prije nego što je Postgres spreman; healthcheck + depends_on s condition
- disk pun — docker system df, zatim oprezan prune neiskorištenih imagea
- permission denied na docker.sock — korisnik nije u grupi docker ili treba nova sesija prijave
Sigurnost
Ne objavljujte portove baze. Ne uključujte privileged: true. Ne montirajte /var/run/docker.sock u kontejner aplikacije osim ako namjerno pišete upravitelj kontejnera. Držite mehanizam ažurnim. Skenirajte vlastite imagee ako ih gradite. .env na disku i dalje je tajna datoteka — ograničite tko može ssh na kutiju.
- Nema javnih 5432 / 3306 / 6379
- Pričvrstite digest imagea ili barem nepromjenjive oznake
- chmod 600 .env
- Unattended-upgrades host OS-a i dalje vrijede
- Jedan Compose projekt po aplikaciji smanjuje radijus oštećenja
Savjeti
- compose.yaml u gitu; .env.example bez pravih tajni
- Profili za opcionalne radnike (docker compose --profile workers up -d)
- Gledajte docker stats kada dimenzionirate VPS
- Bind mount izvornog koda nije produkcijski obrazac — ispecite image
- Kada stog poraste na nekoliko VPS, tek tada gledajte orkestraciju — ne ranije
Produkcijskiji Compose na jednom VPS je: službeni Docker paketi, zaključan .env, YAML koji ne objavljuje bazu, healthcheckovi, obrnuti proxy na 80/443, UFW, oznake imagea na koje se možete vratiti i dumpovi Postgresa koje ste barem jednom vratili. Počnite od predloška u ovom članku, zamijenite image aplikacije i docker compose down -v tretirajte kao destruktivnu naredbu s istim poštovanjem kao rm -rf.