Kembali ke blog
Agustus 19, 2026Panduan

Cara Mendeploy Aplikasi dengan Docker Compose di VPS

Panduan produksi praktis untuk Docker Compose v2 di Ubuntu: instal mesin, tulis berkas Compose dengan jaringan dan volume, tangani rahasia, taruh reverse proxy di depan, perbarui tanpa panik, dan cadangkan volume bernama.

Cara Mendeploy Aplikasi dengan Docker Compose di VPS

Docker Compose adalah cara biasa menjalankan tumpukan kecil di satu VPS: aplikasi web, basis data, cache Redis, dan reverse proxy, dijelaskan dalam satu berkas YAML. Ini bukan Kubernetes. Anda tidak akan mendapat failover multi-node. Anda akan mendapat penyiapan yang dapat diulang, ditinjau, dan dibuat ulang di VPS Hiddence baru dalam hitungan menit — tepat yang dibutuhkan sebagian besar proyek sampingan dan produk kecil.

Artikel ini mengasumsikan Anda ingin sesuatu yang lebih dekat ke produksi daripada docker run pada malam Jumat. Kita akan menginstal Docker Engine dan plugin Compose, membuat pengguna deploy non-root, menulis berkas Compose dengan healthcheck dan kebijakan restart, menjaga rahasia di luar Git, memublikasikan hanya port reverse proxy ke internet, dan mendefinisikan rutinitas pembaruan serta cadangan. Tumpukan contoh adalah aplikasi web khas plus PostgreSQL plus Caddy, tetapi pola yang sama bekerja untuk Node, PHP, Python, atau sekelompok worker.

Mengapa Compose di satu VPS masih masuk akal

Orang meloncat ke Kubernetes karena sedang tren, lalu menghabiskan akhir pekan pada YAML untuk satu situs web. Compose tetap dapat dibaca. Anda memberi versi berkas, mendokumentasikan variabel lingkungan, dan dapat membedakan perubahan sebelum menerapkan. Isolasi cukup baik: kontainer aplikasi yang disusupi tidak boleh memegang soket UNIX basis data jika Anda memakai jaringan dan pengguna hak minimum. Batas sumber daya menghentikan satu kebocoran memori membekukan seluruh VPS.

  • Satu berkas mendeskripsikan seluruh tumpukan
  • Volume bernama bertahan setelah pembuatan ulang kontainer
  • Jaringan Docker internal menjaga Postgres dari internet publik
  • restart: unless-stopped mencakup sebagian besar reboot
  • Mudah disalin ke VPS kedua ketika Anda tumbuh dari yang pertama

Persyaratan

Gunakan VPS dengan RAM cukup untuk aplikasi plus Postgres. Paket 2 GB adalah minimum realistis untuk aplikasi + basis data + proxy. Ubuntu 24.04 diasumsikan. Jangan instal Docker dari Snap acak tanpa membaca apa yang dilakukannya pada cgroups; langkah di bawah memakai repositori apt resmi Docker.

  • Ubuntu 22.04/24.04, root atau sudo
  • 2 GB RAM disarankan (1 GB hanya untuk tumpukan kecil tanpa Postgres)
  • Domain yang mengarah ke VPS jika Anda ingin HTTPS otomatis
  • Git atau cara lain menyalin proyek ke server

Langkah 1: Instal Docker Engine dan Compose v2

Compose v2 adalah plugin yang dipanggil sebagai docker compose (dengan spasi), bukan biner Python docker-compose lama. Instal keduanya dari repositori Docker agar Anda mendapat patch keamanan terkini.

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

Langkah 2: Pengguna deploy dan tata letak direktori

Menjalankan Compose sebagai root berfungsi, tetapi pengguna khusus dengan keanggotaan grup docker lebih bersih. Taruh proyek di /opt atau /srv, bukan di /root, agar cadangan dan izin jelas. Jangan pernah membuat direktori yang berisi .env dapat ditulis dunia.

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

Langkah 3: Tulis berkas Compose yang berpikiran produksi

Berkas di bawah adalah templat. Ganti image aplikasi dengan milik Anda. Postgres tidak dipublikasikan ke 0.0.0.0:5432 — hanya Caddy yang dipublikasikan di 80 dan 443. Healthcheck menghentikan reverse proxy mengirim lalu lintas ke aplikasi yang masih memigrasikan basis data. Sematkan tag image; latest adalah cara kerusakan kejutan terjadi.

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:

Langkah 4: Rahasia, .env, dan Caddyfile

Taruh kata sandi di .env di server, chmod 600, dan jangan pernah meng-commit berkas itu. Hasilkan kata sandi dengan openssl rand -base64 32. Caddyfile hanya perlu reverse-proxy ke nama layanan aplikasi di jaringan Docker.

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

Langkah 5: Firewall, log, dan batas sumber daya

UFW seharusnya hanya mengizinkan 22, 80, dan 443. Docker kadang melewati UFW untuk port yang dipublikasikan; jika Anda butuh firewall host yang ketat, cari interaksi Docker + UFW terkini untuk versi Ubuntu Anda atau publikasikan port hanya di 127.0.0.1 dan taruh Caddy host di depan. Atur batas memori di Compose agar Postgres tidak memakan 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

Langkah 6: Pembaruan dan rollback

Pembaruan membosankan yang berhasil: tarik image baru, up -d, pantau healthcheck, simpan tag sebelumnya di git agar bisa disematkan kembali. Jangan docker compose down pada basis data produksi kecuali Anda berniat berhenti menerima lalu lintas. down -v menghapus volume — itu penghapusan, bukan 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

Cadangkan volume bernama

Snapshot VPS bagus. Dump logis Postgres lebih baik jika Anda perlu memulihkan ke mesin lain. Jadwalkan ini dengan cron. Uji pemulihan sekali, atau Anda tidak punya cadangan — Anda punya berkas yang Anda harap adalah cadangan.

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

Pemecahan masalah

Baca docker compose ps dan docker compose logs service. Kegagalan tarik image biasanya batas laju ghcr/Docker Hub atau image pribadi tanpa login. 502 dari Caddy berarti aplikasi tidak sehat atau nama host proxy salah (pakai nama layanan Compose, bukan localhost, dari dalam kontainer Caddy). Kesalahan izin di volume sering berarti image berjalan sebagai uid 1000 sementara direktori host adalah root.

  • compose: command not found — Anda menginstal nama biner lama; gunakan docker compose
  • port is already allocated — sesuatu lain memiliki 80/443 (Apache, Nginx, Caddy lain)
  • database connection refused — aplikasi mulai sebelum Postgres siap; gunakan healthcheck + depends_on condition
  • disk penuh — docker system df, lalu prune image yang tidak terpakai dengan hati-hati
  • permission denied di docker.sock — pengguna tidak di grup docker, atau Anda butuh sesi login baru

Keamanan

Jangan publikasikan port basis data. Jangan jalankan privileged: true. Jangan pasang /var/run/docker.sock ke kontainer aplikasi kecuali Anda sengaja menulis pengelola kontainer. Jaga mesin tetap diperbarui. Pindai image Anda jika Anda membangunnya. .env di disk masih berkas rahasia — batasi siapa yang bisa ssh ke kotak itu.

  • Tidak ada 5432 / 3306 / 6379 publik
  • Sematkan digest image atau setidaknya tag yang tidak berubah
  • chmod 600 .env
  • Unattended-upgrades untuk OS host masih penting
  • Satu proyek Compose per aplikasi menjaga radius ledakan lebih kecil

Tips

  • Taruh compose.yaml di git; jaga .env.example tanpa rahasia nyata
  • Gunakan profiles untuk worker opsional (docker compose --profile workers up -d)
  • Tonton docker stats saat Anda menentukan ukuran VPS
  • Untuk bind mount kode sumber, ini bukan pola produksi — panggang image
  • Jika tumpukan tumbuh ke beberapa VPS, baru lihat orkestrasi — tidak sebelumnya

Penyiapan Compose yang mirip produksi di satu VPS adalah: paket Docker resmi, .env yang dikunci, berkas YAML yang tidak memublikasikan basis data, healthcheck, reverse proxy di 80/443, UFW, tag image yang bisa di-rollback, dan dump Postgres yang pernah Anda pulihkan sekali. Mulai dari templat di artikel ini, ganti image aplikasi, dan perlakukan docker compose down -v sebagai perintah destruktif dengan rasa hormat yang sama seperti rm -rf.