Pasang Caddy + 9Router di Server Pakai Docker Compose
Jadi ceritanya gue lagi pengen punya AI router sendiri yang bisa diakses dari mana aja, bukan cuma dari localhost. Pilihan jatuh ke 9Router — gateway yang nyambungin Claude Code, Codex, Cursor, Cline, sampai Copilot ke 40+ provider AI gratisan, plus auto-fallback biar nggak kena limit.
Targetnya simpel: 9Router jalan di server, di-reverse-proxy sama Caddy biar dapet HTTPS otomatis, dan semua dijalanin pakai Docker Compose. Yang enak, 9Router udah punya login sendiri di dashboard-nya, jadi kita nggak perlu nambahin basic auth lagi di Caddy. Hemat satu langkah.
Yang perlu disiapin
Sebelum mulai, pastiin dulu hal-hal ini ada:
- Server (VPS atau apa aja) yang bisa di-SSH, idealnya Ubuntu/Debian.
- Domain yang udah diarahin ke IP server lewat A record. Misalnya
9router.domainlo.com. Ini wajib kalau mau HTTPS otomatis dari Caddy. - Port
80dan443kebuka di firewall, biar Caddy bisa ambil sertifikat dari Let's Encrypt.
Masuk ke server lewat SSH
Pertama, login ke server:
ssh user@ip-server-lo
Kalau Docker belum keinstall, gas pakai script resminya. Ini ngurusin Docker Engine sekalian plugin Compose-nya:
curl -fsSL https://get.docker.com | sh
Cek bentar biar yakin udah kepasang:
docker --version
docker compose version
Kalau dua-duanya ngeluarin versi, berarti aman.
Bikin struktur folder
Gue suka naro semua di satu folder biar gampang dirawat:
mkdir -p ~/9router && cd ~/9router
Di dalam folder ini nanti ada tiga file: docker-compose.yml, Caddyfile, dan satu file .env buat nyimpen secret.
Nyusun docker-compose.yml
Ini inti dari semuanya. Kita jalanin dua service: 9router sebagai aplikasinya, dan caddy sebagai reverse proxy yang ngurusin HTTPS.
services:
9router:
image: decolua/9router:latest
container_name: 9router
restart: unless-stopped
environment:
- DATA_DIR=/app/data
- NODE_ENV=production
# Wajib true karena diakses lewat HTTPS dari Caddy
- AUTH_COOKIE_SECURE=true
# Password login pertama kali ke dashboard
- INITIAL_PASSWORD=${INITIAL_PASSWORD}
# Wajibin Bearer API key di endpoint /v1 karena nanti kebuka ke internet
- REQUIRE_API_KEY=true
volumes:
- ./data:/app/data
# Sengaja TIDAK pakai "ports", biar 9Router cuma bisa diakses lewat Caddy
expose:
- "20128"
caddy:
image: caddy:2-alpine
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
depends_on:
- 9router
volumes:
caddy_data:
caddy_config:
Hal yang sengaja gue lakuin di sini:
- 9Router nggak buka
portske host. Cuma pakaiexpose, jadi container ini cuma bisa diakses dari jaringan Docker internal — alias cuma lewat Caddy. Ini lapisan keamanan kecil tapi penting, biar port20128nggak nyangsang ke publik. AUTH_COOKIE_SECURE=truekarena dashboard bakal dilayanin lewat HTTPS. Kalau ini false, cookie login bisa rewel di belakang reverse proxy.REQUIRE_API_KEY=truemaksa endpoint/v1/*minta Bearer API key. Karena servernya kebuka ke internet, ini ngehindarin orang sembarangan make router lo.- Folder
./datanyimpen SQLite-nya (./data/db/data.sqlite), jadi data provider dan config-nya aman walau container di-restart.
Caddyfile yang super ringkas
Nah ini bagian yang bikin Caddy menang banget dibanding Nginx buat use case kayak gini. Cukup segini:
9router.domainlo.com {
reverse_proxy 9router:20128
}
Beneran cuma itu. Caddy bakal otomatis ngurus sertifikat TLS dari Let's Encrypt, perpanjangan, sampai redirect HTTP ke HTTPS. Nama 9router di situ ngarah ke service Docker-nya, jadi Caddy nyamperin container lewat jaringan internal Compose.
Inget tadi gue bilang nggak perlu nambahin basic auth? Ini alasannya: 9Router udah punya halaman login sendiri buat dashboard, dan endpoint /v1 udah dijagain pakai API key lewat REQUIRE_API_KEY=true. Kalau kita tumpuk lagi pakai basic auth Caddy, malah dobel dan bikin ribet pas mau pakai API-nya dari Claude Code atau Cline. Jadi kita biarin auth-nya diurus 9Router aja.
Nyimpen secret di .env
Daripada nulis password langsung di compose file, mending taro di .env yang sefolder sama docker-compose.yml:
# .env
INITIAL_PASSWORD=password-kuat-punya-lo
Docker Compose otomatis baca file .env ini dan ngisi ${INITIAL_PASSWORD} tadi. Jangan lupa kasih password yang beneran kuat, karena ini gerbang ke dashboard lo.
Jalanin semuanya
Sekarang tinggal nyalain:
docker compose up -d
Cek statusnya, mestinya dua container jalan:
docker compose ps
Mau ngintip log buat mastiin Caddy berhasil ambil sertifikat? Pantau aja:
docker compose logs -f caddy
Begitu Caddy bilang sertifikat udah keluar buat domain lo, buka https://9router.domainlo.com di browser. Lo bakal disambut halaman login 9Router. Masukin password yang tadi diset di .env, dan dashboard kebuka.
Sertifikat pertama kadang butuh beberapa detik. Kalau pas awal kena error TLS, tungguin sebentar terus refresh. Pastiin juga A record domain udah propagasi ke IP server.
Konekin ke tool lo
Di dalam dashboard, masuk ke Providers, sambungin salah satu yang gratisan kayak Kiro AI atau OpenCode Free, terus bikin API key. Habis itu, di tool kayak Claude Code atau Cline tinggal arahin ke endpoint lo:
Endpoint: https://9router.domainlo.com/v1
API Key: [salin dari dashboard]
Model: kr/claude-sonnet-4.5
Karena REQUIRE_API_KEY=true, request ke /v1 tanpa Bearer key bakal ditolak. Jadi router lo aman walau alamatnya kebuka di internet.
Perawatan harian
Beberapa command yang sering kepake:
# Update ke versi terbaru
docker compose pull && docker compose up -d
# Restart kalau perlu
docker compose restart
# Matiin semua
docker compose down
Data lo tetap aman di folder ./data walau container-nya diturunin, jadi nggak perlu takut config-nya ilang.
Penutup
Segitu doang. Dengan Caddy yang ngurus HTTPS otomatis dan 9Router yang udah bawa auth sendiri, kita dapet AI router yang aman diakses dari mana aja tanpa perlu ribet setting basic auth. Docker Compose bikin seluruh stack ini gampang dipindah atau di-reproduce di server lain — tinggal copy tiga file, docker compose up -d, kelar.
Selamat ngoprek, dan jangan lupa pakai password yang kuat ya.