Lucky Wirasakti
5 min read

Pasang Caddy + 9Router di Server Pakai Docker Compose

#Docker#Caddy#DevOps#Self-Hosting

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 80 dan 443 kebuka 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 ports ke host. Cuma pakai expose, jadi container ini cuma bisa diakses dari jaringan Docker internal — alias cuma lewat Caddy. Ini lapisan keamanan kecil tapi penting, biar port 20128 nggak nyangsang ke publik.
  • AUTH_COOKIE_SECURE=true karena dashboard bakal dilayanin lewat HTTPS. Kalau ini false, cookie login bisa rewel di belakang reverse proxy.
  • REQUIRE_API_KEY=true maksa endpoint /v1/* minta Bearer API key. Karena servernya kebuka ke internet, ini ngehindarin orang sembarangan make router lo.
  • Folder ./data nyimpen 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.