# Deploy (build/lokal): SiCABE Control Panel > Untuk deploy PRODUKSI dari image registry (menyatu dengan gateway: docker-proxy + > config-agent + nginx, dengan profile), pakai **`deploy/stack/`**. Compose di sini > untuk **build lokal** dan menjalankan panel sendirian. Panel (Next.js) dijalankan sebagai container. Image produksi memakai **Next.js standalone** + user non-root (`nextjs`, uid 1001). > Gateway **per host** (`deploy/host-gateway/`) adalah stack terpisah — sudah punya > compose sendiri. Panel hanya perlu menunjuk ke gateway itu lewat `SICABE_HOSTS`. ## 1. Build image ```bash ./scripts/build-image.sh # tag default: sicabe/control-panel: + :latest ./scripts/build-image.sh --tag sicabe/control-panel:1.0.0 ./scripts/build-image.sh --push --registry registry.example.com/sicabe ./scripts/build-image.sh --no-cache --platform linux/amd64 ``` Script memverifikasi Dockerfile/`public/`, menandai `:latest` + ``, dan mencetak ukuran image. ## 2. Siapkan env ```bash cd deploy/panel cp env.example .env # lalu isi NEXTAUTH_SECRET, api key gateway, dll. # NEXTAUTH_SECRET: openssl rand -base64 32 ``` **Penting:** panel di container **tidak bisa membaca filesystem platform** → tiap host di `SICABE_HOSTS` **wajib** punya `configUrl` (config-agent). Karena itu **tidak perlu** mount folder platform: katalog Trino, project dbt, dokumen OPA, `.env` platform, dan CA Trino semuanya dibaca lewat agent (HTTP). Env `TRINO_CATALOG_DIR` / `DBT_PROJECT_DIR` / `OPA_DATA_FILE` / `SICABE_ENV_FILE` / `TRINO_CA_FILE` hanya dipakai `LocalConfigStore` (dev/single-host tanpa agent) — jangan diandalkan di container. ## 3. Jalankan ```bash cd deploy/panel docker compose up -d # pakai image hasil build # docker compose up -d --build # sekaligus build dari source docker compose logs -f panel ``` Buka `http://localhost:3000` (atau `PANEL_PORT`). ## Yang persisten | Data | Lokasi | | --- | --- | | Config panel (hosts/modules/services/service types) | volume `panel-data` → `/app/data/config.json` | | Monitored state | volume `panel-data` → `/app/data/state.json` | Backup/pindah config lewat **Settings → Backup** (Export/Import) — tanpa menyentuh volume. Untuk migrasi dari deployment host lama: Export di yang lama → Import di yang baru. ## Catatan jaringan & TLS - **Default compose = `network_mode: host`.** Gateway host (`deploy/host-gateway`) hanya membuka `127.0.0.1:22375` (aman), sehingga panel di **bridge network tidak bisa** menjangkaunya lewat `host.docker.internal`. Terverifikasi: dari bridge, Keycloak (`0.0.0.0:12080`) **bisa**, gateway (`127.0.0.1:22375`) **gagal**; dari host network keduanya **bisa**. Host network juga membuat `127.0.0.1` & SAN sertifikat Trino (`localhost`) cocok. Port ditentukan `PORT` (default 3000), bukan `ports:`. - **Varian bridge** (bila gateway dibuka ke alamat non-loopback): lihat komentar di `docker-compose.yml` — hapus `network_mode: host`, tambah `ports:` + `extra_hosts: host.docker.internal`. - **TLS Trino**: sertifikat self-signed Trino hanya mencakup SAN `localhost / trino / sicabe-trino / trino-coordinator / 127.0.0.1`. `host.docker.internal` **tidak** termasuk → verifikasi TLS gagal di bridge. - CA Trino **tidak perlu** file lokal bila host punya `configUrl` (agent `/ca`). ## Login di produksi = SSO (Keycloak) Container berjalan `NODE_ENV=production`, dan **login kredensial dev dinonaktifkan di produksi** (hanya provider `keycloak` yang aktif — cek `GET /api/auth/providers`). Artinya: - Login lewat tombol **Sign in with Keycloak** di browser (bukan form dev). - **`NEXTAUTH_URL` harus sama dengan URL publik panel**, dan panel Keycloak (`scripts/create-keycloak-client.sh`, `APP_URL`) harus punya redirect URI `${NEXTAUTH_URL}/api/auth/callback/keycloak`. Kalau panel disajikan di port selain 3000, jalankan ulang script dengan `APP_URL=http://:`. - Image **gagal-cepat** bila `NEXTAUTH_SECRET` atau `KEYCLOAK_CLIENT_SECRET` kosong (memang disengaja) — panel menolak start dengan pesan jelas. ## Update ```bash ./scripts/build-image.sh --tag sicabe/control-panel:1.1.0 PANEL_TAG=1.1.0 docker compose up -d # dari deploy/panel ``` ## Healthcheck Image menyertakan `HEALTHCHECK` (`GET /api/auth/session` → 200 walau belum login), jadi `docker compose ps` menampilkan status `healthy`.