kossakovsky/selfhost-ai: Bedah Installer yang Menumpuk 40+ Container AI dalam Satu Perintah

· Review

Hampir semua orang yang mulai main self-hosted AI berakhir di titik yang sama: satu terminal, tiga halaman docker-compose.yml yang berbeda, dan file .env yang sudah tidak tahu variabel mana yang milik aplikasi mana. Repositori ini menjawab masalah itu dengan cara yang menarik: bukan satu aplikasi, tapi satu installer yang menghasilkan seluruh stack. Posting ini adalah review kode milik orang lain — repo-nya dikelola oleh Cole Medin, bukan produk saya. Saya hanya membaca source-nya dan menuliskan temuannya.

Apa ini dan siapa maintainer-nya

kossakovsky/selfhost-ai berlicensi Apache-2.0, 944 star dan 238 fork saat ditulis. Repo dibuat 5 Mei 2025, commit terakhir 10 Oktober 2026. Bahasa utamanya Shell, dengan 391 file: 313 JSON (konfigurasi library AI), 26 skrip shell sekitar 295 KB, 13 YAML, 2 Python.

Satu detail yang langsung menarik: nama endpoint telemetry masih memakai path lama.

# scripts/telemetry.sh
# The /n8n-install path is the project's original name; the Scarf endpoint
# was registered before the selfhost-ai rename and must stay unchanged
SCARF_ENDPOINT="https://kossakovsky.gateway.scarf.sh/n8n-install"

Artinya repo ini pernah bernama n8n-install lalu diganti nama, dan endpoint Scarf-nya sengaja dibiarkan agar statistik lama tidak putus. Telemetri bisa dimatikan lewat SCARF_ANALYTICS=false atau DO_NOT_TRACK=1, dan ID yang dikirim cuma 12 karakter hex acak (od -An -tx1 -N6 /dev/urandom).

Arsitektur: pola 8 langkah, bukan satu skrip raksasa

Instead satu install.sh raksasa, repo ini memecah instalasi jadi 8 skrip bernomor yang dipanggil berurutan. install.sh hanya orkestrator; tiap langkah punya satu tanggung jawab:

# scripts/install.sh
required_scripts=(
    "01_system_preparation.sh" "02_install_docker.sh"
    "03_generate_secrets.sh"   "04_wizard.sh"
    "05_configure_services.sh" "06_run_services.sh"
    "07_final_report.sh"       "08_fix_permissions.sh"
)

Yang paling keputusan desainnya justru ada di pemecahan .env. Skrip 03_generate_secrets.sh punya tabel deklaratif berisi tipe dan panjang tiap secret:

declare -A VARS_TO_GENERATE=(
    ["ENCRYPTION_KEY"]="hex:64"          # Langfuse, 32 bytes -> 64 hex chars
    ["N8N_ENCRYPTION_KEY"]="secret:64"   # base64, 48 bytes -> 64 chars
    ["N8N_RUNNERS_AUTH_TOKEN"]="secret:64"
    ["CRAWL4AI_API_TOKEN"]="secret:48"
    ["GOST_USERNAME"]="fixed:gost"
)
SERVICES_NEEDING_HASH=("PROMETHEUS" "SEARXNG" "COMFYUI" "PADDLEOCR" "RAGAPP"
    "LT" "DOCLING" "TEMPORAL_UI" "WELCOME" "INVOKEAI" "OPENCLAW")

Tabel itu yang membuat password Caddy basic auth bisa di-hash otomatis lewat caddy hash-password dari image Caddy, lalu ditulis sebagai <NAMA>_PASSWORD_HASH. Menambah layanan baru cukup menambah satu baris, bukan menulis ulang logika.

Lapisan kedua: profile Docker Compose. docker-compose.yml-nya hefty 68 KB, tapi hampir setiap service punya profiles: ["nama"]. Saya hitung ada 44 nama profile berbeda, termasuk cpu, gpu-nvidia, gpu-amd, comfyui-nvidia, ollama-template. Profile *-template sengaja tidak pernah diaktifkan langsung — dipakai lewat extends: oleh generator. Hasilnya: satu host CPU dan satu host 8 GPU berjalan dari file yang sama.

Lapisan ketiga: update. update.sh menjalankan pembaruan dua tahap supaya logika updater sendiri ikut ter-update sebelum dipakai, dan direktori milik user dicadangkan dulu sebelum git reset --hard menimpanya:

# scripts/utils.sh
PRESERVE_DIRS=("python-runner")

# scripts/update.sh
if ! BACKUP_PATH=$(backup_preserved_dirs); then
    log_error "Backup failed. Aborting update to prevent data loss."
    exit 1
fi

Ada dua mode: make update memakai git reset --hard (buang commit lokal, cocok untuk instalasi standar), make git-pull memakai GIT_MODE=merge untuk orang yang fork dan menyimpan modifikasi sendiri.

Detail teknis yang menarik

Caddy memblokir endpoint metrik n8n di edge. Ini bagian paling rapi dari seluruh file:

{$N8N_HOSTNAME} {
    # n8n serves its unauthenticated Prometheus metrics on the UI port
    respond /metrics* 404
    reverse_proxy n8n:5678
}

Prometheus mengambilnya lewat jaringan internal, jadi tidak perlu dibuka ke internet.

Endpoint Bearer tanpa basic auth, dengan alasan tertulis. n8n-MCP dan Ollama API memakai pola yang sama — satu header, satu token:

{$OLLAMA_HOSTNAME} {
    @authorized header Authorization "Bearer {$OLLAMA_CADDY_API_TOKEN}"
    handle @authorized {
        reverse_proxy ollama:11434 {
            flush_interval -1
            transport http { compression off }
        }
    }
    respond 401
}

flush_interval -1 itu wajib: streaming token dari LLM akan rusak kalau Caddy menyiram buffer. Untuk n8n-MCP, komentarnya di file yang sama menjelaskan kenapa basic auth justru ditolak — HTTP hanya boleh punya satu header Authorization, dan klien MCP hanya mengirim Bearer token.

OpenClaw: akses host penuh, ditulis terang-terangan di komentar. Service ini mount /var/run/docker.sock (setara root) plus kunci SSH ke user installer, dan Caddy hanya memproteksi dengan basic auth:

# FULL HOST ACCESS BY DESIGN: docker.sock (root-equivalent) plus an SSH key
# Anyone who passes the OpenClaw basic auth ... controls the server;
# keep the Telegram DM policy on pairing or an allowlist.
volumes:
  - /var/run/docker.sock:/var/run/docker.sock

Untuk anti-impersonasi, openclaw/init.sh membaca subnet jaringan privatnya sendiri lalu jadikan satu-satunya range yang dipercaya gateway, jadi kontainer di jaringan default tidak bisa menyamar jadi user.

Generator Ollama multi-GPU menghasilkan file, bukan mem-patch. generate_ollama_instances.sh lebih suka menghapus file daripada meninggalkan YAML kosong, karena Compose menolak kunci services: kosong yang tidak berisi service. Untuk AMD, pinning GPU dilakukan lewat HIP_VISIBLE_DEVICES, bukan deploy.resources, dengan alasan tertulis di komentar: ROCm meneruskan seluruh /dev/kfd ke setiap kontainer. Template instance sengaja tidak punya blok deploy: — Compose bersifat append saat extends, jadi blok di template akan bocor ke instance yang mengunci device ID tertentu.

Anotasi kualitas yang tidak biasa. Banyak komentar menjelaskan kegagalan spesifik, bukan sekadar menjelaskan apa:

# Pinned: 3.1.4 crashes at startup with SQLite
# ("this.db.exec is not a function") and runs as 'node',
# ignoring the /root/.flowise mount.
image: flowiseai/flowise:3.1.3

Komentar serupa menjelaskan kenapa env_file: .env sengaja tidak dipakai di service crawl4ai (akan menyuntikkan semua secret stack ke crawler), dan kenapa image MinIO diganti ke fork pgsty/silo (repo asli menolak pull anonim).

Kelemahan dan batasan

Ini bukan stack untuk semua orang, dan bukan juga “quick start” yang aman untuk server produksi:

  1. Installer mengubah firewall host secara destruktif. 01_system_preparation.sh menjalankan ufw reset lalu hanya membuka SSH/HTTP/HTTPS. Kalau rule lain sudah dikonfigurasi, semuanya hilang, dan skrip tidak bertanya.
  2. Hanya Ubuntu/Debian. Langkah pertama memanggil apt langsung dengan set -e. Di Armbian atau Fedora yang saya pakai, langkah itu berhenti.
  3. Banyak image latest. Ollama, Grafana, Prometheus, Postiz, dan Weaviate semuanya latest. Hasilnya tidak reproducible: make update bisa menaikkan versi tanpa kamu sadari, dan tidak ada lockfile untuk menjaganya.
  4. OpenClaw setara root. Sudah ditulis jujur di komentar, tapi kalau kamu memakainya di server yang juga menyimpan data lain, anggap seluruh server itu hilang begitu basic auth-nya bocor.
  5. Default Ollama cuma OLLAMA_CONTEXT_LENGTH 8192, sementara service LightRAG meng-hardcode LLM_MODEL: qwen2.5:32b dan EMBEDDING_MODEL: bge-m3:latest. Di host kecil, profil itu tidak muat. Bukan bug, tapi default-nya mengasumsikan mesin besar.
  6. Butuh sudo penuh. Seluruh instalasi jalan sebagai root, jadi tidak cocok di shared hosting atau di mesin yang kamu tidak miliki hak root-nya.

Saya tidak menjalankan instalasi ini di sini, jadi semua klaim di atas berasal dari membaca kode, bukan dari hasil pakai. Yang jelas dari kode: repo ini berat, tapi setiap keputusan yang saya kutip punya alasan yang bisa ditelusuri ke file tertentu.

Siapa yang cocok memakai

Cocok kalau kamu mulai dari nol dengan Ubuntu Server, punya satu domain dan sertifikat Let’s Encrypt, dan ingin n8n plus Ollama plus Open WebUI hidup dalam satu sore — dan siap menerima trade-off yang tertulis di atas. Repo-nya juga punya make doctor untuk diagnosis dan make update-preview untuk melihat apa yang akan berubah sebelum dijalankan.

Tidak cocok kalau server kamu sudah punya firewall sendiri, butuh reproducibility yang ketat, atau kamu hanya mau menjalankan dua layanan tanpa menarik 44 profile ke depan mata.

Repo lengkapnya: https://github.com/kossakovsky/selfhost-ai (Apache-2.0, 944 star saat ditulis).