Paano Mag-deploy ng Applications gamit ang Docker Compose sa VPS
Praktikal na production guide sa Docker Compose v2 sa Ubuntu: i-install ang engine, magsulat ng Compose file na may networks at volumes, hawakan ang secrets, maglagay ng reverse proxy sa harap, mag-update nang walang panic, at mag-back up ng named volumes.

Karaniwang paraan ang Docker Compose para magpatakbo ng maliit na stack sa isang VPS: web app, database, Redis cache, at reverse proxy, inilalarawan sa iisang YAML file. Hindi ito Kubernetes. Hindi ka makakakuha ng multi-node failover. Makakakuha ka ng repeatable, reviewable na setup na kayang gawing muli sa bagong Hiddence VPS sa loob ng ilang minuto — eksaktong kailangan ng karamihan sa side projects at maliliit na products.
Ipinapalagay ng artikulong ito na gusto mo ng mas malapit sa production kaysa docker run sa Biyernes ng gabi. I-i-install natin ang Docker Engine at Compose plugin, gagawa ng non-root deploy user, magsusulat ng Compose file na may healthchecks at restart policies, itatago ang secrets sa labas ng Git, ipo-publish lang ang reverse-proxy ports sa internet, at tutukuyin ang update at backup routine. Typical web app plus PostgreSQL plus Caddy ang example stack, pero gumagana ang parehong pattern para sa Node, PHP, Python, o bungkos ng workers.
Bakit may saysay pa rin ang Compose sa iisang VPS
Tumatalon ang mga tao sa Kubernetes dahil uso, tapos ginugugol ang weekend sa YAML para sa isang website. Nanatiling nababasa ang Compose. Binibigyan mo ng version ang file, idinodokumento ang environment variables, at pwede mong i-diff ang change bago i-apply. Sapat ang isolation: hindi dapat hawak ng compromised app container ang database UNIX socket kung gumamit ka ng networks at least-privilege users. Pinipigilan ng resource limits ang isang memory leak na mag-freeze sa buong VPS.
- Isang file ang naglalarawan ng buong stack
- Nabubuhay ang named volumes pagkatapos mag-recreate ng container
- Pinapanatili ng internal Docker networks ang Postgres palayo sa public internet
- Tinatakpan ng restart: unless-stopped ang karamihan ng reboots
- Madaling kopyahin sa pangalawang VPS kapag lumaki ka sa una
Mga kailangan
Gumamit ng VPS na may sapat na RAM para sa app plus Postgres. Realistic minimum ang 2 GB plan para sa app + database + proxy. Ipinapalagay ang Ubuntu 24.04. Huwag mag-install ng Docker mula sa random Snap nang hindi binabasa kung ano ang ginagawa nito sa cgroups; ginagamit ng mga hakbang sa ibaba ang official apt repository ng Docker.
- Ubuntu 22.04/24.04, root o sudo
- Inirerekomenda ang 2 GB RAM (1 GB lang para sa napakaliit na stacks na walang Postgres)
- Domain na tumuturo sa VPS kung gusto mo ng automatic HTTPS
- Git o ibang paraan para kopyahin ang project papunta sa server
Hakbang 1: I-install ang Docker Engine at Compose v2
Plugin ang Compose v2 na tinatawag bilang docker compose (may space), hindi ang lumang docker-compose Python binary. I-install pareho mula sa repository ng Docker para makakuha ka ng kasalukuyang security patches.
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 versionHakbang 2: Deploy user at directory layout
Gumagana ang pagpapatakbo ng Compose bilang root, pero mas malinis ang dedicated user na miyembro ng docker group. Ilagay ang project sa /opt o /srv, hindi sa /root, para malinaw ang backups at permissions. Huwag kailanman world-write ang directory na naglalaman ng .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/appHakbang 3: Magsulat ng production-minded Compose file
Template ang file sa ibaba. Palitan ang app image ng iyo. Hindi nai-publish ang Postgres sa 0.0.0.0:5432 — Caddy lang ang nai-publish sa 80 at 443. Pinipigilan ng healthchecks ang reverse proxy na magpadala ng traffic sa app na nagmi-migrate pa ng database. I-pin ang image tags; latest ang paraan kung paano nangyayari ang surprise breakage.
# /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:Hakbang 4: Secrets, .env, at Caddyfile
Ilagay ang passwords sa .env sa server, chmod 600, at huwag kailanman i-commit ang file na iyon. Mag-generate ng passwords sa openssl rand -base64 32. Kailangan lang ng Caddyfile mag-reverse-proxy sa app service name sa Docker network.
# /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=100Hakbang 5: Firewall, logs, at resource limits
Dapat payagan lang ng UFW ang 22, 80, at 443. Minsan nilalampasan ng Docker ang UFW para sa published ports; kung kailangan mo ng mahigpit na host firewall, hanapin ang kasalukuyang Docker + UFW interaction para sa Ubuntu version mo o mag-publish ng ports sa 127.0.0.1 lang at maglagay ng host Caddy sa harap. Magtakda ng memory limits sa Compose para hindi makain ng Postgres ang 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 dockerHakbang 6: Updates at rollbacks
Ang boring update na gumagana: mag-pull ng bagong images, up -d, bantayan ang healthchecks, itago ang previous tag sa git para ma-pin pabalik. Huwag mag-docker compose down sa production database maliban kung balak mong tumigil sa pagtanggap ng traffic. Tinatanggal ng down -v ang volumes — wipe iyon, hindi 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 volumesI-back up ang named volumes
Maganda ang VPS snapshot. Mas maganda ang logical Postgres dump kung kailangan mong mag-restore sa ibang machine. I-schedule ito sa cron. Subukan ang restore nang minsan, o wala kang backups — may files ka lang na inaasahan mong backups.
# 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 appTroubleshooting
Basahin ang docker compose ps at docker compose logs service. Karaniwang ghcr/Docker Hub rate limits o private image na walang login ang image pull failures. Ang 502 mula sa Caddy ay nangangahulugang hindi healthy ang app o mali ang proxy hostname (gamitin ang Compose service name, hindi localhost, mula sa loob ng Caddy container). Madalas ang permission errors sa volumes ay nangangahulugang tumatakbo ang image bilang uid 1000 habang root ang host directory.
- compose: command not found — na-install mo ang lumang binary name; gamitin ang docker compose
- port is already allocated — may ibang may-ari ng 80/443 (Apache, Nginx, ibang Caddy)
- database connection refused — nagsimula ang app bago handa ang Postgres; gumamit ng healthcheck + depends_on condition
- puno ang disk — docker system df, tapos i-prune ang unused images nang maingat
- permission denied sa docker.sock — wala ang user sa docker group, o kailangan mo ng bagong login session
Security
Huwag mag-publish ng database ports. Huwag magpatakbo ng privileged: true. Huwag mag-mount ng /var/run/docker.sock sa app container maliban kung sadyang nagsusulat ka ng container manager. Panatilihing updated ang engine. I-scan ang sarili mong images kung ikaw ang bumubuo. Secret file pa rin ang .env sa disk — limitahan kung sino ang pwedeng mag-ssh sa box.
- Walang public 5432 / 3306 / 6379
- I-pin ang image digests o hindi bababa sa immutable tags
- chmod 600 .env
- Mahalaga pa rin ang unattended-upgrades para sa host OS
- Isang Compose project bawat app para mas maliit ang blast radius
Mga tips
- Ilagay ang compose.yaml sa git; panatilihing walang totoong secrets ang .env.example
- Gumamit ng profiles para sa optional workers (docker compose --profile workers up -d)
- Panoorin ang docker stats kapag sine-size ang VPS
- Para sa bind mounts ng source code, hindi ito production pattern — magluto ng image
- Kung lumaki ang stack sa ilang VPS, saka tumingin sa orchestration — hindi bago
Ang production-ish Compose setup sa isang VPS ay: official Docker packages, naka-lock na .env, YAML file na hindi nagpu-publish ng database, healthchecks, reverse proxy sa 80/443, UFW, image tags na pwedeng i-roll back, at Postgres dumps na na-restore mo na nang minsan. Magsimula sa template sa artikulong ito, palitan ang app image, at ituring ang docker compose down -v na destructive command na may parehong respeto sa rm -rf.