Caddy JSON #

Meskipun sebagian besar pengguna berinteraksi dengan Caddy melalui berkas Caddyfile yang ringkas dan ramah manusia, format tersebut sebenarnya hanyalah sebuah abstraksi tingkat atas. Di balik layar, mesin runtime Caddy tidak memahami Caddyfile secara langsung. Seluruh parameter konfigurasi Caddy dikelola dan dieksekusi menggunakan format data asli (native format) yaitu Caddy JSON. Memahami Caddy JSON sangat penting bagi kita jika ingin membangun integrasi programatik yang dinamis, memanfaatkan fitur-fitur tingkat lanjut yang tidak memiliki pintasan (shortcut) di Caddyfile, atau melakukan otomatisasi deployment berbasis kode menggunakan sistem eksternal. Kita akan mengupas tuntas arsitektur pohon konfigurasi Caddy JSON, proses kompilasi adapter, anatomi routes dan handlers, otomatisasi sertifikat TLS, serta panduan pengambilan keputusan kapan harus memilih JSON dibandingkan Caddyfile.


Mengapa Memahami Caddy JSON? #

Caddyfile dirancang untuk memudahkan penulisan konfigurasi secara manual oleh manusia. Namun, kesederhanaan Caddyfile mengorbankan tingkat kebebasan konfigurasi (expressiveness). Beberapa fitur modul Caddy yang sangat granular—seperti manajemen rotasi log akses yang kompleks, penentuan kebijakan jabat tangan TLS yang sangat spesifik per-alamat IP, atau perutean tingkat TCP/UDP (Layer 4 proxy)—sering kali tidak didukung secara penuh oleh sintaksis Caddyfile.

Caddy JSON adalah representasi langsung dari struktur objek (struct) bahasa pemrograman Go di dalam kode sumber Caddy. Dengan menggunakan JSON, kita mendapatkan keuntungan berikut:

  • Akses Fitur 100%: Seluruh opsi konfigurasi dari setiap modul Caddy (baik bawaan maupun plugin pihak ketiga) terekspos secara penuh tanpa batasan.
  • Otomatisasi Programatik: Memungkinkan aplikasi eksternal (seperti skrip Python, dashboard Node.js, Kubernetes operator, atau modul Terraform) untuk memproduksi (generate) dokumen konfigurasi secara dinamis langsung dari basis data.
  • Integrasi dengan Config API: Perubahan konfigurasi instan via REST API Caddy mewajibkan dokumen dikirim dalam format JSON.
  • Konsistensi Keadaan: Menghilangkan ambiguitas penafsiran parser Caddyfile, karena JSON memiliki skema data yang pasti dan divalidasi langsung oleh pustaka standar Go.

Pipa Kompilasi Konfigurasi (Compilation Pipeline) #

Caddy menggunakan arsitektur yang sangat modular. Ketika kita memberikan file konfigurasi Caddyfile ke Caddy, berkas tersebut melewati pipa pemrosesan (pipeline) berikut sebelum akhirnya diaktifkan di memori:

flowchart TD
    Caddyfile["1. Berkas Caddyfile (Fisik)"] --> Adapter["2. Caddyfile Adapter (Parser)"]
    Adapter --> JSON_AST["3. Dokumen JSON AST (Native Format)"]
    JSON_AST --> Validation["4. Validasi Skema & Modul Runtime"]
    
    subgraph Caddy_Internal["Mesin Internal Caddy"]
        Validation -->|Lolos| Active_Mem["5. Konfigurasi Aktif di Memori RAM"]
        Validation -->|Gagal| Rollback["6. Batalkan, Tetap Gunakan Config Lama"]
    end

    style Caddyfile stroke:#0288d1,stroke-width:2px
    style Active_Mem stroke:#43a047,stroke-width:2px
    style Rollback stroke:#e53935,stroke-dasharray:5,5

Proses di atas menunjukkan bahwa Caddyfile Adapter bertindak sebagai penerjemah yang mengubah deklarasi deklaratif Caddyfile menjadi berkas JSON Abstract Syntax Tree (AST). Perintah caddy adapt dapat kita gunakan untuk melihat hasil terjemahan ini secara langsung:

# Mengonversi Caddyfile lokal ke bentuk JSON mentah
caddy adapt --config /etc/caddy/Caddyfile --adapter caddyfile

# Memformat output JSON menggunakan jq agar mudah dibaca manusia
caddy adapt --config /etc/caddy/Caddyfile --adapter caddyfile | jq .

# Menyimpan hasil konversi ke berkas konfigurasi JSON permanen
caddy adapt --config /etc/caddy/Caddyfile --adapter caddyfile | jq . > /etc/caddy/config.json

Struktur Top-Level Caddy JSON #

Dokumen Caddy JSON diorganisasikan ke dalam beberapa blok kunci utama yang mewakili ruang lingkup fungsional server:

{
  "admin": {
    // Mengontrol listen address, TLS, dan akses kontrol Admin API
  },
  "logging": {
    // Mendefinisikan penulisan log sistem dan log akses
  },
  "storage": {
    // Menentukan lokasi penyimpanan sertifikat TLS (disk, Redis, database)
  },
  "apps": {
    // Blok aplikasi utama tempat rute dan logika server didefinisikan
    "http": {
      // Server web HTTP, rute, penanganan domain
    },
    "tls": {
      // Kebijakan otomatisasi sertifikat SSL/TLS
    }
  }
}

Setiap blok ini memetakan langsung ke struktur modul internal Caddy. Pembedaan yang paling krusial terletak di dalam blok apps yang menampung modul fungsional Caddy.


Komparasi Apple-to-Apple: Caddyfile vs JSON #

Untuk memahami bagaimana Caddy menerjemahkan sintaksis Caddyfile ke JSON, mari kita bandingkan secara langsung konfigurasi virtual host yang melayani file statis, kompresi data, reverse proxy, dan penulisan log akses:

Berkas Caddyfile #

# Konfigurasi Caddyfile standar
example.com {
    encode gzip zstd
    reverse_proxy localhost:3000
    
    log {
        output file /var/log/caddy/access.log
        format json
    }
}

Hasil Konversi Caddy JSON #

Berikut adalah dokumen JSON hasil terjemahan Caddyfile di atas (beberapa bagian default telah disederhanakan untuk kejelasan):

{
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "listen": [":443", ":80"],
          "routes": [
            {
              "match": [{"host": ["example.com"]}],
              "handle": [
                {
                  "handler": "subroute",
                  "routes": [
                    {
                      "handle": [
                        {
                          "handler": "encode",
                          "encodings": {
                            "gzip": {},
                            "zstd": {}
                          }
                        }
                      ]
                    },
                    {
                      "handle": [
                        {
                          "handler": "reverse_proxy",
                          "upstreams": [{"dial": "localhost:3000"}]
                        }
                      ]
                    }
                  ]
                }
              ],
              "terminal": true
            }
          ],
          "logs": {
            "logger_names": {
              "example.com": "log0"
            }
          }
        }
      }
    },
    "tls": {
      "automation": {
        "policies": [
          {
            "subjects": ["example.com"],
            "issuers": [
              {
                "module": "acme",
                "ca": "https://acme-v02.api.letsencrypt.org/directory"
              }
            ]
          }
        ]
      }
    }
  },
  "logging": {
    "logs": {
      "log0": {
        "writer": {
          "output": "file",
          "filename": "/var/log/caddy/access.log"
        },
        "encoder": {"format": "json"}
      }
    }
  }
}

Jika kita perhatikan hasil konversi di atas:

  1. Blok domain example.com diterjemahkan menjadi pencocok host ("match": [{"host": ["example.com"]}]).
  2. Port :443 dan :80 didefinisikan secara otomatis di dalam array "listen" karena Caddy mengaktifkan fitur HTTPS otomatis secara default.
  3. Struktur penulisan log dipisahkan dari konfigurasi HTTP utama dan diletakkan pada objek "logging" di tingkat teratas (top-level) dengan nama logger unik (log0) yang dirujuk oleh virtual host.

Anatomi Rute (Routes) dan Matchers di JSON #

Rute (route) adalah unit pemrosesan terkecil di dalam modul HTTP Caddy yang menentukan bagaimana sebuah permintaan dari pengguna diproses. Struktur sebuah rute di Caddy JSON terdiri dari tiga properti utama:

{
  "routes": [
    {
      "@id": "optional-id-penanda",
      "match": [
        // Kriteria penyaringan request (Matchers)
      ],
      "handle": [
        // Rantai pemrosesan request (Handlers)
      ],
      "terminal": true
    }
  ]
}

1. Matchers (Kondisi Penyaringan) #

Matchers adalah blok kondisi yang menentukan apakah rute ini harus memproses request yang masuk atau mengabaikannya. Caddy menyediakan berbagai macam matcher bawaan yang sangat fleksibel:

"match": [
  {
    "host": ["example.com", "api.example.com"],
    "path": ["/api/v1/*", "/assets/*.png"],
    "method": ["GET", "POST"],
    "header": {
      "Content-Type": ["application/json"],
      "X-Requested-With": ["XMLHttpRequest"]
    },
    "remote_ip": {
      "ranges": ["192.168.1.0/24", "10.0.0.0/8"]
    },
    "query": {
      "format": ["json"]
    }
  }
]

2. Handlers (Rantai Pemrosesan) #

Jika kriteria pada match terpenuhi, Caddy akan mengeksekusi array handle secara berurutan (handler chain). Setiap handler diidentifikasi oleh nama modulnya di dalam properti "handler".

Berikut adalah contoh-contoh konfigurasi JSON untuk jenis handler yang paling sering digunakan:

A. Handler file_server (Penyaji Berkas Statis) #

Handler ini digunakan untuk menyajikan berkas fisik dari disk penyimpanan server:

{
  "handler": "file_server",
  "root": "/var/www/html",
  "index_names": ["index.html", "index.htm"],
  "browse": {} // Mengaktifkan modul penjelajah direktori interaktif
}

B. Handler reverse_proxy (Penerus Lalu Lintas) #

Handler ini mengarahkan request ke server backend, lengkap dengan konfigurasi penyeimbang beban (load balancing), serta active dan passive health checks:

{
  "handler": "reverse_proxy",
  "upstreams": [
    {"dial": "10.0.1.10:3000"},
    {"dial": "10.0.1.11:3000"}
  ],
  "load_balancing": {
    "selection_policy": {
      "policy": "least_conn"
    }
  },
  "health_checks": {
    "active": {
      "uri": "/healthz",
      "interval": "10s",
      "timeout": "3s",
      "expect_status": 200
    },
    "passive": {
      "fail_duration": "30s",
      "max_fails": 3
    }
  }
}

C. Handler static_response (Respons Statis / Pengalihan) #

Digunakan untuk mengembalikan teks statis atau melakukan pengalihan URL (URL redirection):

{
  "handler": "static_response",
  "status_code": 301,
  "headers": {
    "Location": ["https://example.com{http.request.uri}"],
    "Content-Type": ["text/html; charset=utf-8"]
  }
}

D. Handler encode (Kompresi Data) #

Digunakan untuk melakukan kompresi payload respons guna menghemat bandwidth jaringan:

{
  "handler": "encode",
  "encodings": {
    "gzip": {},
    "zstd": {}
  },
  "minimum_length": 1024 // Hanya kompresi respons dengan ukuran > 1KB
}

Konfigurasi TLS dan Otomatisasi Sertifikat di JSON #

Modul TLS di Caddy JSON dikonfigurasi di bawah jalur apps.tls. Struktur ini memungkinkan kita mendefinisikan kebijakan otentikasi sertifikat secara sangat dinamis untuk berbagai skenario domain.

Berikut adalah konfigurasi lengkap TLS yang mendefinisikan dua kebijakan otomatisasi sertifikat yang berbeda:

{
  "apps": {
    "tls": {
      "automation": {
        "policies": [
          {
            # Kebijakan 1: Domain publik menggunakan ACME (Let's Encrypt & ZeroSSL)
            "subjects": ["example.com", "*.example.com"],
            "issuers": [
              {
                "module": "acme",
                "ca": "https://acme-v02.api.letsencrypt.org/directory",
                "email": "[email protected]",
                "challenges": {
                  "dns": {
                    "provider": {
                      "name": "cloudflare",
                      "api_token": "{env.CF_API_TOKEN}"
                    }
                  }
                }
              },
              {
                "module": "acme",
                "ca": "https://acme.zerossl.com/v2/DV90",
                "external_account": {
                  "key_id": "{env.ZEROSSL_KEY_ID}",
                  "mac_key": "{env.ZEROSSL_MAC_KEY}"
                }
              }
            ]
          },
          {
            # Kebijakan 2: Domain jaringan lokal internal menggunakan CA internal
            "subjects": ["app.local", "database.local"],
            "issuers": [
              {
                "module": "internal"
              }
            ]
          }
        ]
      }
    }
  }
}

Dalam konfigurasi di atas:

  • Kebijakan pertama mendefinisikan domain wildcard *.example.com yang menggunakan tantangan DNS-01 Cloudflare untuk validasi, dengan token API yang dibaca langsung dari variabel lingkungan sistem ({env.CF_API_TOKEN}). Caddy juga menyiapkan ZeroSSL dengan binding akun eksternal (External Account Binding / EAB) sebagai cadangan otomatis.
  • Kebijakan kedua mengonfigurasi domain lokal (app.local) untuk menggunakan modul internal PKI Caddy guna membuat sertifikat lokal yang ditandatangani oleh Root CA internal mandiri.

Konfigurasi Logging Global dan Rotasi Log #

Salah satu fitur Caddy JSON yang tidak dapat dikonfigurasi secara leluasa di Caddyfile adalah penyesuaian parameter rotasi log secara presisi.

Di dalam Caddy JSON, kita dapat mendefinisikan logger log akses yang menulis ke file fisik, membatasi ukuran berkas maksimal, menentukan retensi hari, dan format enkoder secara terstruktur:

{
  "logging": {
    "logs": {
      "access_logs": {
        "writer": {
          "output": "file",
          "filename": "/var/log/caddy/access.log",
          "roll_size_mb": 100,      // Rotasi file setelah mencapai ukuran 100MB
          "roll_gzip": true,         // Kompres file log lama ke format gzip
          "roll_keep": 10,           // Hanya simpan maksimal 10 file arsip log
          "roll_keep_days": 90       // Simpan file arsip log selama maksimal 90 hari
        },
        "encoder": {
          "format": "json"           // Log ditulis dalam struktur JSON terpadu
        },
        "include": ["http.log.access"],
        "level": "INFO"
      }
    }
  }
}

Menjalankan Caddy dengan Berkas JSON #

Setelah dokumen konfigurasi JSON kita siap, kita dapat menjalankannya secara langsung di server menggunakan beberapa metode:

1. Menjalankan Server secara Langsung (Command Line) #

Perintah caddy run akan membaca berkas JSON secara native jika kita melewatkan parameter --adapter:

# Jalankan Caddy menggunakan konfigurasi JSON secara native
caddy run --config /etc/caddy/config.json

# Jalankan di latar belakang (background daemon)
caddy start --config /etc/caddy/config.json

2. Memuat Konfigurasi JSON via Admin API (Zero-Downtime Reload) #

Jika Caddy sudah berjalan aktif di server, kita dapat mengirimkan berkas JSON baru tersebut langsung ke endpoint /load untuk memicu reload zero-downtime secara instan:

# Kirim file JSON ke Admin API local
curl -s -X POST http://localhost:2019/load \
  -H "Content-Type: application/json" \
  --data-binary @/etc/caddy/config.json

Kapan Memilih JSON vs Caddyfile? #

Untuk membantu kita mengambil keputusan taktis yang tepat saat merancang arsitektur infrastruktur web server Caddy, gunakan pedoman kriteria perbandingan berikut:

Tetap gunakan CADDYFILE jika:
  ✓ Konfigurasi ditulis, dibaca, dan dipelihara secara manual oleh manusia.
  ✓ Tim operasi tidak terbiasa dengan struktur JSON yang sangat verbose.
  ✓ Kebutuhan deployment standar (seperti hosting file statis, proxy HTTP dasar).
  ✓ Tidak ada kebutuhan integrasi otomatisasi programatik dari aplikasi luar.

Pertimbangkan beralih ke JSON jika:
  ✓ Konfigurasi diproduksi secara otomatis oleh kode program aplikasi (dynamic generation).
  ✓ Mengelola platform SaaS multi-tenant dengan ribuan domain dinamis via REST API.
  ✓ Membutuhkan opsi konfigurasi granular tingkat lanjut yang tidak didukung Caddyfile.
  ✓ Membangun infrastruktur Kubernetes menggunakan Caddy Ingress Controller.
  ✓ Membutuhkan sistem log yang memiliki aturan rotasi file yang sangat ketat.

Ringkasan #

  • Format Asli Server: Caddy JSON adalah format konfigurasi native dan merupakan kebenaran tunggal (single source of truth) dari seluruh fungsionalitas server Caddy.
  • Proses Kompilasi: Gunakan perintah caddy adapt untuk melihat representasi JSON dari berkas Caddyfile yang kita buat guna mempermudah pembelajaran struktur pohon objek.
  • Struktur Pohon Utama: Dokumen JSON Caddy diorganisasikan ke dalam empat blok utama: apps (aplikasi), admin (API), logging (pencatatan log), dan storage (penyimpanan TLS).
  • Fleksibilitas Rute: Blok rute HTTP terdiri dari match (kriteria penyaringan) dan handle (rantai pemrosesan handler) yang dievaluasi secara berurutan.
  • Identifikasi Konsisten: Selalu sisipkan tag @id pada objek-objek rute dinamis agar dapat dimanipulasi dengan aman via Config API tanpa memedulikan pergeseran indeks array.
  • Konfigurasi Granular: JSON menyediakan kontrol penuh terhadap parameter tingkat lanjut seperti rotasi berkas log otomatis, penyimpanan SSL/TLS klaster terdistribusi, dan Layer 4 proxying.

← Sebelumnya: Reload Config   Berikutnya: Basic Auth →

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