Config API #
Config API adalah jantung dari kemampuan dynamic reconfiguration Caddy. Melalui serangkaian endpoint REST, kamu bisa memanipulasi setiap aspek konfigurasi Caddy yang sedang berjalan — menambah route baru, mengubah upstream, mematikan site tertentu — semua secara real-time tanpa restart dan tanpa downtime.
Ini sangat berbeda dari pendekatan tradisional di mana setiap perubahan konfigurasi memerlukan edit file dan reload proses. Dengan Config API, kamu bisa membangun sistem yang benar-benar dinamis — misalnya, platform multi-tenant yang menambahkan konfigurasi Caddy baru setiap kali customer baru mendaftar.
HTTP Methods dan Semantiknya #
GET /config/PATH → Baca nilai pada PATH
POST /config/PATH → Tambahkan elemen baru ke array pada PATH
PUT /config/PATH → Ganti nilai pada PATH (atau seluruh node)
PATCH /config/PATH → Merge data ke dalam struktur pada PATH
DELETE /config/PATH → Hapus nilai pada PATH
Perbedaan kunci antara POST dan PUT:
- POST ke array: menambahkan elemen baru (append)
- PUT ke path: mengganti nilai yang sudah ada (replace)
Membaca Konfigurasi (GET) #
# Baca seluruh konfigurasi
curl -s http://localhost:2019/config/ | jq .
# Baca bagian spesifik — apps
curl -s http://localhost:2019/config/apps/ | jq 'keys'
# Output: ["http", "tls"]
# Baca konfigurasi HTTP
curl -s http://localhost:2019/config/apps/http/ | jq .
# Lihat semua server yang dikonfigurasi
curl -s http://localhost:2019/config/apps/http/servers/ | jq 'keys'
# Lihat routes server pertama
curl -s http://localhost:2019/config/apps/http/servers/srv0/routes/ | jq .
# Lihat listen addresses
curl -s http://localhost:2019/config/apps/http/servers/srv0/listen/ | jq .
# Output: [":443", ":80"]
Menambah Route Baru (POST ke Array) #
Skenario umum: platform multi-tenant yang perlu menambahkan domain baru tanpa restart.
# Tambahkan route baru untuk domain baru
# POST ke /routes/ — menambahkan ke akhir array routes
curl -X POST http://localhost:2019/config/apps/http/servers/srv0/routes/ \
-H "Content-Type: application/json" \
-d '{
"@id": "new-customer-site",
"match": [
{
"host": ["newcustomer.example.com"]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{"dial": "newcustomer-app:3000"}
]
}
]
}
]
}
],
"terminal": true
}'
Caddy akan langsung mulai menerima request untuk newcustomer.example.com dan mendapatkan sertifikat TLS otomatis.
Mengubah Upstream (PUT) #
# Lihat upstream yang ada sekarang
curl -s http://localhost:2019/config/apps/http/servers/srv0/routes/ | \
jq '.[0].handle[0].routes[0].handle[0].upstreams'
# Ganti upstream dengan set baru (misalnya setelah scaling)
curl -X PUT \
"http://localhost:2019/config/apps/http/servers/srv0/routes/0/handle/0/routes/0/handle/0/upstreams" \
-H "Content-Type: application/json" \
-d '[
{"dial": "backend-1:3000"},
{"dial": "backend-2:3000"},
{"dial": "backend-3:3000"},
{"dial": "backend-4:3000"}
]'
Update via @id (Lebih Reliable)
#
# Jika konfigurasi menggunakan @id, gunakan endpoint /id/
# Tidak terpengaruh perubahan posisi dalam array
# Lihat route yang akan diubah
curl -s http://localhost:2019/id/main-app-route | jq .
# Update handler dalam route
curl -X PATCH http://localhost:2019/id/main-app-route \
-H "Content-Type: application/json" \
-d '{
"handle": [
{
"handler": "subroute",
"routes": [{
"handle": [{
"handler": "reverse_proxy",
"upstreams": [
{"dial": "new-backend-1:3000"},
{"dial": "new-backend-2:3000"}
],
"load_balancing": {
"selection_policy": {"policy": "least_conn"}
}
}]
}]
}
]
}'
Menghapus Route (DELETE) #
# Hapus route berdasarkan index
curl -X DELETE http://localhost:2019/config/apps/http/servers/srv0/routes/2
# Hapus route berdasarkan @id (lebih safe)
curl -X DELETE http://localhost:2019/id/old-customer-site
# Hapus semua routes (hati-hati!)
curl -X DELETE http://localhost:2019/config/apps/http/servers/srv0/routes/
Pola Lengkap: Deployment Script dengan Config API #
Contoh script deployment yang menggunakan Config API untuk zero-downtime deploy:
#!/bin/bash
# zero-downtime-deploy.sh
set -e
APP_NAME="$1" # Nama aplikasi
NEW_IMAGE="$2" # Docker image baru
DOMAIN="$3" # Domain yang akan di-update
NEW_PORT="$4" # Port container baru
CADDY_API="http://localhost:2019"
echo "[1/5] Starting new container..."
docker run -d \
--name "${APP_NAME}-new" \
-p "${NEW_PORT}:3000" \
--network app-network \
"${NEW_IMAGE}"
echo "[2/5] Waiting for new container to be healthy..."
for i in $(seq 1 30); do
STATUS=$(curl -sf "http://localhost:${NEW_PORT}/health" \
-o /dev/null -w "%{http_code}" 2>/dev/null || echo "0")
if [ "$STATUS" = "200" ]; then
echo " ✓ New container is healthy"
break
fi
if [ $i -eq 30 ]; then
echo " ✗ Health check timeout, rolling back"
docker rm -f "${APP_NAME}-new"
exit 1
fi
echo " Waiting... ($i/30)"
sleep 2
done
echo "[3/5] Updating Caddy upstream..."
# Cari route ID untuk domain ini
ROUTE_ID="${APP_NAME}-route"
# Update upstream ke container baru
curl -s -X PATCH "${CADDY_API}/id/${ROUTE_ID}" \
-H "Content-Type: application/json" \
-d "{
\"handle\": [{
\"handler\": \"subroute\",
\"routes\": [{
\"handle\": [{
\"handler\": \"reverse_proxy\",
\"upstreams\": [{\"dial\": \"${APP_NAME}-new:3000\"}],
\"health_checks\": {
\"active\": {
\"uri\": \"/health\",
\"interval\": \"10s\"
}
}
}]
}]
}]
}"
echo " ✓ Caddy updated to new container"
echo "[4/5] Draining old container (10s)..."
sleep 10
echo "[5/5] Removing old container..."
docker rm -f "${APP_NAME}-old" 2>/dev/null || true
docker rename "${APP_NAME}-new" "${APP_NAME}-old"
echo "✓ Deployment complete!"
Menambah dan Menghapus Header Global via API #
# Tambah header security ke semua response
# Caranya: tambahkan handler 'headers' sebelum handler lain
curl -X POST \
"http://localhost:2019/config/apps/http/servers/srv0/routes/0/handle/0/routes" \
-H "Content-Type: application/json" \
-d '{
"@id": "global-security-headers",
"handle": [{
"handler": "headers",
"response": {
"set": {
"Strict-Transport-Security": ["max-age=31536000; includeSubDomains"],
"X-Frame-Options": ["SAMEORIGIN"],
"X-Content-Type-Options": ["nosniff"]
},
"delete": ["Server", "X-Powered-By"]
}
}]
}'
Orkestrasi Multi-Site dari Aplikasi Eksternal #
Contoh: aplikasi Node.js yang mengelola konfigurasi Caddy via API saat customer baru mendaftar:
// caddy-manager.js
const axios = require('axios');
const CADDY_API = 'http://localhost:2019';
async function addCustomerSite(customerId, domain, backendPort) {
const route = {
"@id": `customer-${customerId}`,
match: [{ host: [domain] }],
handle: [{
handler: "subroute",
routes: [{
handle: [{
handler: "reverse_proxy",
upstreams: [{ dial: `customer-${customerId}:${backendPort}` }],
health_checks: {
active: {
uri: "/health",
interval: "30s",
timeout: "5s"
}
}
}]
}]
}],
terminal: true
};
await axios.post(
`${CADDY_API}/config/apps/http/servers/srv0/routes/`,
route,
{ headers: { 'Content-Type': 'application/json' } }
);
console.log(`✓ Site added for ${domain}`);
}
async function removeCustomerSite(customerId) {
await axios.delete(`${CADDY_API}/id/customer-${customerId}`);
console.log(`✓ Site removed for customer ${customerId}`);
}
async function updateCustomerBackend(customerId, newPort) {
const routeData = await axios.get(`${CADDY_API}/id/customer-${customerId}`);
const route = routeData.data;
// Update upstream port
route.handle[0].routes[0].handle[0].upstreams[0].dial =
`customer-${customerId}:${newPort}`;
await axios.put(
`${CADDY_API}/id/customer-${customerId}`,
route,
{ headers: { 'Content-Type': 'application/json' } }
);
console.log(`✓ Backend updated for customer ${customerId}`);
}
// Contoh penggunaan
addCustomerSite('cust-001', 'cust001.myplatform.com', 4001);
addCustomerSite('cust-002', 'cust002.myplatform.com', 4002);
Error Handling dari Config API #
# API mengembalikan error yang informatif jika request tidak valid
# Contoh: POST JSON yang tidak valid
curl -X POST http://localhost:2019/config/apps/http/servers/srv0/routes/ \
-H "Content-Type: application/json" \
-d '{ invalid json }'
# Response:
# HTTP/1.1 400 Bad Request
# {"error": "decoding request: invalid character 'i' looking for beginning of object key string"}
# Contoh: Path yang tidak ada
curl -X GET http://localhost:2019/config/apps/http/servers/nonexistent/
# Response:
# HTTP/1.1 404 Not Found
# {"error": "unknown object key: nonexistent"}
# Selalu cek HTTP status code response
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-X POST http://localhost:2019/config/apps/http/servers/srv0/routes/ \
-H "Content-Type: application/json" \
-d '{ "valid": "route" }')
if [ "$STATUS" != "200" ]; then
echo "API call failed with status $STATUS"
fi
Ringkasan #
- Config API menggunakan HTTP methods secara semantis: GET membaca, POST menambah ke array, PUT mengganti, PATCH merge, DELETE menghapus.
- Gunakan
@iddi JSON konfigurasi untuk referensi yang stabil — jauh lebih reliable dari navigasi via index numerik yang berubah saat routes ditambah/dihapus.- Config API memungkinkan platform multi-tenant yang menambah/menghapus konfigurasi Caddy secara programatik setiap customer baru mendaftar.
- Selalu tambahkan health check saat menambah route baru via API agar Caddy langsung memantau backend yang baru ditambahkan.
- Pastikan error handling yang proper saat menggunakan Config API dari aplikasi — cek HTTP status code dan parse error message untuk debugging.
- Perubahan via Config API bersifat sementara (in-memory) — untuk persistensi, gunakan
/loaddengan Caddyfile atau simpan state di database aplikasimu.