Docker Compose

Caddy dengan Docker Compose #

Docker Compose adalah perkakas (tool) standar industri yang sangat efisien untuk mendefinisikan dan menjalankan aplikasi Docker multi-kontainer. Di lingkungan produksi modern, server web seperti Caddy hampir tidak pernah berjalan sendirian; ia biasanya dipasangkan dengan layanan backend (Node.js, Go, Python), basis data (PostgreSQL, MySQL), cache (Redis), dan antrean pesan. Dengan menulis satu file deklaratif docker-compose.yml, kita dapat menyatukan seluruh stack ini ke dalam satu siklus hidup terpadu yang dapat disimpan di dalam sistem kontrol versi (version control).

Artikel ini akan membahas secara mendalam penerapan Caddy menggunakan Docker Compose, mulai dari pengaturan struktur proyek, isolasi jaringan tingkat produksi, integrasi khusus dengan PHP-FPM menggunakan shared socket, hingga skenario HTTPS lokal untuk lingkungan pengembangan kita.


Mengapa Memilih Docker Compose untuk Caddy? #

Untuk memahami mengapa Docker Compose sangat direkomendasikan dibandingkan eksekusi manual perintah docker run, mari kita lihat perbandingannya:

Tanpa Compose (docker run manual):          Dengan Compose (Satu File):
──────────────────────────────────          ────────────────────────────
docker run ... caddy                        docker compose up -d
docker run ... app
docker run ... db
docker network create ...
docker network connect ...
docker network connect ...
(Sangat manual, rawan kesalahan)            (Deklaratif, konsisten, terdokumentasi)

Dengan beralih ke Docker Compose, kita mendapatkan manfaat berupa:

  • Konsistensi Lingkungan (Reproducibility) — Anggota tim developer lain dapat menjalankan stack aplikasi yang identik hanya dengan menyalin file Compose.
  • Isolasi Jaringan (Network Isolation) — Kita dapat mengisolasi basis data dari internet dan Caddy, sehingga hanya backend aplikasi kita yang dapat terhubung ke basis data.
  • Manajemen Volume Otomatis — Memastikan pemetaan data sertifikat TLS Let’s Encrypt selalu persisten secara teratur.

Struktur Direktori Proyek yang Direkomendasikan #

Agar pengerjaan proyek kita teratur dan mudah dikelola, kita disarankan menggunakan struktur direktori berikut:

proyek-kita/
  ├── docker-compose.yml       ← File konfigurasi utama multi-service
  ├── docker-compose.dev.yml   ← File override khusus untuk development lokal
  ├── .env                     ← File variabel lingkungan (JANGAN di-commit ke Git!)
  ├── .env.example             ← Contoh template file .env (wajib di-commit)
  ├── Caddyfile                ← Konfigurasi Caddy utama
  └── app/
      ├── Dockerfile           ← Konfigurasi build backend kita
      └── src/                 ← Sumber kode aplikasi backend

Setup Minimal: Caddy dan Aplikasi Backend #

Mari kita buat konfigurasi Docker Compose paling mendasar yang menghubungkan Caddy sebagai reverse proxy ke sebuah container aplikasi Node.js.

1. Membuat Berkas docker-compose.yml #

services:
  caddy:
    image: caddy:2.8.4
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - app

  app:
    image: node:20-alpine
    working_dir: /app
    volumes:
      - ./app:/app
    command: node server.js
    # PENTING: Kita tidak perlu membuka ports (ports mapping) untuk container 'app'
    # ke host. Keamanan terjaga karena hanya Caddy yang memiliki akses ke port app.

volumes:
  caddy_data:      # Menyimpan sertifikat SSL agar persisten
  caddy_config:    # Menyimpan file konfigurasi adaptasi internal

2. Membuat Berkas Caddyfile #

Pada Caddyfile kita, kita cukup merujuk pada nama layanan (service name) yang kita definisikan di file docker-compose.yml (yaitu app):

# Mengarahkan domain kita
situsku.com {
    # Docker DNS internal menerjemahkan 'app' menjadi alamat IP internal container Node.js
    reverse_proxy app:3000
}

Kita dapat menjalankan seluruh stack ini dengan perintah:

# Menjalankan seluruh layanan di background
docker compose up -d

# Memeriksa status kontainer yang sedang aktif
docker compose ps

Konfigurasi Produksi: Isolasi Jaringan Caddy + Node.js + PostgreSQL #

Pada skenario produksi nyata, kita wajib menerapkan prinsip hak akses terendah (principle of least privilege) pada arsitektur jaringan kita. Database tidak boleh diakses langsung dari internet, bahkan database juga tidak boleh dihubungi secara langsung oleh Caddy.

Berikut adalah susunan docker-compose.yml tingkat produksi dengan isolasi jaringan ganda:

services:
  caddy:
    image: caddy:2.8.4
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - jaringan_depan
    depends_on:
      - app

  app:
    build:
      context: ./app
      dockerfile: Dockerfile
    restart: unless-stopped
    environment:
      NODE_ENV: production
      DATABASE_URL: postgresql://user_aplikasi:${DB_PASSWORD}@db:5432/database_app
      PORT: 3000
    networks:
      - jaringan_depan    # Menghubungkan app dengan Caddy
      - jaringan_belakang   # Menghubungkan app dengan Database
    depends_on:
      db:
        condition: service_healthy # Menunggu database siap menerima koneksi
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: user_aplikasi
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: database_app
    volumes:
      - data_postgres:/var/lib/postgresql/data
    networks:
      - jaringan_belakang   # Hanya ada di jaringan belakang
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user_aplikasi -d database_app"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

networks:
  jaringan_depan:
    # Jaringan yang melayani trafik web publik
  jaringan_belakang:
    # Jaringan privat internal yang sangat aman

volumes:
  caddy_data:
  caddy_config:
  data_postgres:

Visualisasi Arsitektur Aliran Jaringan #

flowchart TD
    INTERNET["INTERNET"] --> caddy["caddy (jaringan_depan)"]
    caddy --> app["app (jaringan_depan & jaringan_belakang)"]
    app --> db["db (jaringan_belakang)"]

Dengan struktur jaringan terpisah seperti di atas:

  • Proses Caddy tidak dapat menjangkau Database secara langsung, meminimalkan dampak eksploitasi jika Caddy berhasil ditembus.
  • Database berada di dalam jaringan_belakang yang tertutup rapat dari akses luar server.
  • Aplikasi app bertindak sebagai jembatan karena terhubung ke kedua jaringan tersebut.

Kita harus menaruh kata sandi database di dalam file .env lokal:

# File .env (simpan di host lokal, JANGAN di-commit ke Git)
DB_PASSWORD=KunciRahasiaDatabaseKita123!

Integrasi PHP-FPM dengan Shared Unix Socket Volume #

Menjalankan aplikasi PHP (seperti WordPress atau Laravel) di Docker dengan performa tinggi membutuhkan integrasi FastCGI. Komunikasi antara Caddy dan PHP-FPM akan berjalan jauh lebih cepat jika menggunakan Unix Socket daripada koneksi TCP (port 9000).

Untuk mewujudkan hal ini di Docker Compose, kita harus memetakan satu shared volume untuk membagi file .sock:

services:
  caddy:
    image: caddy:2.8.4
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - ./public:/var/www/html:ro        # Web root dipetakan secara read-only
      - caddy_data:/data
      - caddy_config:/config
      - php_socket:/run/php              # Tempat menyimpan file socket
    depends_on:
      - php-fpm

  php-fpm:
    image: php:8.3-fpm-alpine
    restart: unless-stopped
    volumes:
      - ./public:/var/www/html:rw        # PHP-FPM membutuhkan hak akses tulis
      - php_socket:/run/php              # Membagikan socket ke Caddy
    # Kita mengonfigurasi PHP-FPM agar mendengarkan pada berkas socket unix
    command: >
      sh -c "
        sed -i 's|listen = 127.0.0.1:9000|listen = /run/php/php-fpm.sock|' 
          /usr/local/etc/php-fpm.d/www.conf &&
        sed -i 's|;listen.owner = www-data|listen.owner = root|' 
          /usr/local/etc/php-fpm.d/www.conf &&
        sed -i 's|;listen.group = www-data|listen.group = root|' 
          /usr/local/etc/php-fpm.d/www.conf &&
        sed -i 's|;listen.mode = 0660|listen.mode = 0666|' 
          /usr/local/etc/php-fpm.d/www.conf &&
        php-fpm
      "      

volumes:
  caddy_data:
  caddy_config:
  php_socket:      # Named volume bersama untuk socket unix PHP

Konfigurasi Caddyfile kita untuk melayani PHP-FPM via Unix Socket:

situsku.com {
    root * /var/www/html
    
    # Meneruskan seluruh request file php ke Unix socket di volume bersama
    php_fastcgi unix//run/php/php-fpm.sock
    
    encode gzip
    file_server
}

HTTPS Lokal pada Lingkungan Development #

Salah satu fitur paling disukai dari Caddy adalah kemampuannya menyediakan HTTPS otomatis untuk lingkungan lokal (localhost) menggunakan Otoritas Sertifikat (CA) internal Caddy. Kita dapat menyusun file Compose khusus untuk pengembangan agar browser kita mempercayai sertifikat lokal kita.

1. Membuat Berkas docker-compose.dev.yml #

services:
  caddy:
    image: caddy:2.8.4
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile.dev:/etc/caddy/Caddyfile:ro
      - caddy_data_dev:/data
      - caddy_config_dev:/config
      # Memetakan folder sertifikat lokal internal ke host agar dapat dibaca
      - ./sertifikat-dev:/root/.local/share/caddy/pki/authorities/local

  app:
    image: node:20-alpine
    environment:
      NODE_ENV: development
    volumes:
      - ./app:/app
    command: node server.js

volumes:
  caddy_data_dev:
  caddy_config_dev:

2. Membuat Berkas Caddyfile.dev #

{
    # Menegaskan penggunaan Otoritas Sertifikat internal (Local CA)
    local_certs
}

# Domain lokal kita
localhost, app.localhost {
    reverse_proxy app:3000
}

Kita jalankan stack development kita:

docker compose -f docker-compose.dev.yml up -d

3. Menginstal Root CA Caddy ke Sistem Host Kita #

Agar browser kita tidak menampilkan peringatan Your connection is not private (SSL untrusted), kita harus mendaftarkan Root CA buatan Caddy (yang telah diekspor ke folder ./sertifikat-dev/) ke dalam basis data sertifikat komputer host kita.

Jika komputer host kita memiliki CLI Caddy yang terinstal, kita cukup menjalankan perintah:

# Menambahkan root sertifikat lokal Caddy ke system trust store host kita
# Perintah ini akan meminta konfirmasi password admin sistem operasi host kita
caddy trust

Jika kita menjalankan perintah di atas, browser kita kini dapat membuka https://localhost dengan indikator gembok hijau yang valid.


Perintah CLI Docker Compose yang Sering Digunakan #

Berikut adalah ringkasan perintah yang wajib kita ketahui saat mengelola Caddy menggunakan Docker Compose:

# 1. Menjalankan stack aplikasi di background
docker compose up -d

# 2. Menjalankan ulang dan memaksa proses build image (jika Dockerfile berubah)
docker compose up -d --build

# 3. Menghentikan total layanan (menghapus kontainer dan jaringan internal)
docker compose down

# 4. Menghentikan total layanan beserta menghapus VOLUMENYA (HATI-HATI: Sertifikat SSL HILANG!)
docker compose down -v

# 5. Memeriksa log aktif Caddy secara real-time
docker compose logs -f caddy

# 6. Memvalidasi Caddyfile di dalam container
docker compose exec caddy caddy validate --config /etc/caddy/Caddyfile

# 7. Memuat ulang (reload) konfigurasi tanpa downtime
docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile

# 8. Membuka terminal shell kontainer Caddy
docker compose exec caddy sh

Pitfall yang Harus Dihindari #

1. Perintah docker compose down -v di Produksi #

Bendera -v menginstruksikan Docker untuk menghapus seluruh volume persisten yang terkait dengan stack tersebut. Menjalankannya di server produksi akan menghapus named volume caddy_data yang menyimpan sertifikat SSL Let’s Encrypt kita. Solusi: Cukup jalankan docker compose down tanpa bendera -v untuk aktivitas pemeliharaan rutin.

2. File Caddyfile Tidak Terbaca Setelah Diedit #

Ketika kita mengedit file Caddyfile di host, container Caddy tidak akan langsung menerapkan perubahan tersebut. Solusi: Kita harus selalu memicu pemuatan ulang secara manual menggunakan perintah: docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile

3. Layanan Backend Gagal Terhubung ke Database saat Startup #

Meskipun kita menulis depends_on: - db, dependensi bawaan Docker hanya memastikan kontainer basis data telah dimulai, bukan memastikan mesin database telah siap melayani request. Akibatnya backend kita dapat mengalami crash karena koneksi ditolak saat pertama kali start. Solusi: Terapkan konfigurasi healthcheck pada basis data kita dan gunakan parameter condition: service_healthy pada blok depends_on di layanan aplikasi backend kita.


Kapan Beralih ke Alternatif / Tidak Menggunakan Ini #

Tetap gunakan Docker Compose jika:
  ✓ Kita mengelola arsitektur multi-service (Caddy + App + Database + Cache) secara terintegrasi.
  ✓ Kita ingin mendokumentasikan topologi jaringan dan volume server kita dalam bentuk kode (IaC).
  ✓ Kita membutuhkan lingkungan pengembangan lokal yang identik dengan lingkungan produksi.
  ✓ Kita ingin menyederhanakan alur kerja tim developer kita.

Pertimbangkan metode lain jika:
  ✗ Kita hanya membutuhkan Caddy sebagai file server statis sederhana pada satu server VM tunggal (Gunakan APT).
  ✗ Infrastruktur kita dikelola oleh sistem orkestrasi besar seperti Kubernetes atau Docker Swarm (Gunakan Helm Chart/Kubernetes Manifest).

Ringkasan #

  • Orkestrasi Deklaratif — Docker Compose mempermudah pengelolaan Caddy beserta seluruh dependensi layanannya dalam satu berkas konfigurasi.
  • Amankan Database — Selalu pisahkan jaringan frontend dan backend agar basis data kita sepenuhnya terlindung dari akses luar.
  • Koneksi Socket PHP — Manfaatkan pemetaan berkas Unix Socket melalui shared volume untuk interaksi super cepat antara Caddy dan PHP-FPM.
  • SSL Development — Gunakan fitur local_certs dan perintah caddy trust untuk mempermudah testing web terenkripsi HTTPS di komputer lokal kita.
  • Reload Graceful — Eksekusi docker compose exec caddy caddy reload untuk memuat ulang file konfigurasi di container produksi tanpa memicu downtime.
  • Lindungi Volume — Hindari penggunaan perintah docker compose down -v di server produksi agar sertifikat TLS berharga kita tidak terhapus.

← Sebelumnya: Docker   Berikutnya: Compile dari Source →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact