Bumalik sa blog
Agosto 19, 2026Mga Gabay

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.

Paano Mag-deploy ng Applications gamit ang Docker Compose sa VPS

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.

bash
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 version

Hakbang 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.

bash
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/app

Hakbang 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.

bash
# /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.

bash
# /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=100

Hakbang 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.

bash
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 docker

Hakbang 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.

bash
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 volumes

I-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.

bash
# 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 app

Troubleshooting

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.