Dua Kontainer Host-mode Bentrok di Port 8642
Dua container Hermes dengan network_mode: host saling rebut port 8642. Fix: satu container gateway run + HERMES_DASHBOARD=1, dashboard jalan in-process.
Gejalanya: dua kontainer, satu port 8642
Pagi ini container hermes dan hermes-dashboard saya sama-sama mati-meriah. Lognya pendek tapi jelas: api_server gagal bind ke 0.0.0.0:8642 dengan address already in use. Awalnya saya kira ini bug di image terbaru — push terakhir cuma docker compose pull biasa. Tapi setelah baca compose lagi, penyebabnya ketahuan: kedua service pakai network_mode: host.
Kenapa network_mode: host bikin port bentrok
Dengan network_mode: host, container tidak dapat namespace jaringan sendiri; ia pakai stack jaringan host secara langsung. Implikasinya dua hal: ports: diabaikan (tidak ada NAT), dan setiap proses yang bind ke sebuah port langsung menempati port itu di host. Jadi kalau dua container host-mode sama-sama menjalankan gateway yang mendengarkan 8642, yang kedua pasti gagal. Dokumentasi Compose menyebutnya terang-terangan: dua container host-mode yang mau port yang sama akan collide, yang satu mati dengan bind: address already in use.
Di kasus saya, hermes-dashboard bukan cuma panel — ia juga menyalakan gateway + api_server sendiri karena butuh deteksi liveness gateway. Dua gateway, dua api_server, satu port. Tabrakan.
Solusi: satu kontainer, dashboard in-process
Perbaikkannya simpel dan justru menyederhanakan: jalankan satu kontainer gateway run dengan HERMES_DASHBOARD=1. Menurut dokumentasi Docker Hermes, dashboard sudah dijalankan sebagai layanan ter-supervisi s6-rc di dalam container yang sama bersama gateway — tidak perlu container terpisah. Untuk mengikatnya ke loopback agar auth gate tidak menyala, set HERMES_DASHBOARD_HOST=127.0.0.1 (default dalam container memang 0.0.0.0, dan dokumentasi dashboard mencatat auth gate otomatis aktif kalau bind non-loopback).
Compose finalnya jadi begini:
services:
hermes:
image: nousresearch/hermes-agent:latest
container_name: hermes
restart: unless-stopped
environment:
- HERMES_UID=${HERMES_UID:-10000}
- HERMES_GID=${HERMES_GID:-10000}
- TZ=Asia/Jakarta
- HERMES_DASHBOARD=1
- HERMES_DASHBOARD_HOST=127.0.0.1
command: ["gateway", "run"]
Service dashboard lama dibuang. Tidak ada lagi dua container host-mode. Perbandingan sebelum/sesudah:
Sesudah:
Kenapa ini lebih baik, bukan cuma lebih pendek
Dokumentasi Hermes punya bagian khusus — “Why one container with many profiles, not many containers” — dan alasannya masuk akal untuk setup personal: satu image, satu venv, satu cache Playwright, bukan N buah duplikat per container. Sebelum migrasi s6, pola “satu container per profile” memang perlu karena belum ada supervisor dalam container. Sekarang s6 jadi PID 1, jadi satu container sudah cukup untuk menjalankan gateway sekaligus dashboard yang diawasi ulang otomatis saat crash.
Pelajaran buat setup Docker yang lain: network_mode: host itu pisau bermata dua. Ia menghilangkan overhead NAT (berguna untuk monitoring agent atau DNS), tapi membuat collision port jadi masalah level host, bukan level container. Kalau kamu butuh dua proses yang sama-sama bind port, lebih aman pakai bridge network + ports: eksplisit, atau gabungkan ke satu container seperti yang saya lakukan.
Penutup
Bug pagi ini bukan di image, melainkan di asumsi: dua container host-mode tidak pernah bisa berbagi port 8642. Menyatukannya jadi satu container dengan HERMES_DASHBOARD=1 + bind 127.0.0.1 memperbaiki crash dan menyederhanakan compose. Kalau kamu mau baca lebih jauh soal self-host dashboard yang aman, lihat catatan embed blog ini atau tulisan soal Docker multi-stage.