セルフホスティングのクイックスタート
Docker で自分のサーバーやマシン上に Multica を実行します(Kubernetes では Helm が利用できます)。所要時間は約10分です。
このページでは、Docker を使って Multica サーバー(バックエンド + フロントエンド + PostgreSQL)を自分のマシンやサーバー上で実行する手順を案内します。完了すると、ワークスペース、イシュー、コメント、エージェントの構成を含むデータが、完全にあなたの管理下に置かれます。
エージェントの 実行 は、依然としてローカルで動かすデーモンと、そのマシンにインストールされた AI コーディングツールに依存します — Cloud とまったく同じです。セルフホスティングはサーバー層を置き換えるだけで、実行層を置き換えるわけではありません。
前提条件
- Docker がインストールされ、
docker composeを実行できること - Git(任意ですが、ソースを取得できるので推奨)
- 常時稼働させられるマシン(ローカル / 内部ネットワーク / クラウドホストのいずれでも可)
- デーモンを実行するマシン に AI コーディングツールが最低1つインストールされていること(サーバーを実行するマシンである必要はなく、開発用ノートパソコンでも構いません)
1. プロジェクトを取得してバックエンドを起動する
すでに Kubernetes を使っていますか? Docker をスキップして代わりに Helm チャートを使ってください — 下記の Kubernetes デプロイへ移動し、初回ログインのために ステップ4に戻ってきてください。
git clone https://github.com/multica-ai/multica.git
cd multica
make selfhostmake selfhost は次のことを行います。
.envがなければ.env.exampleから生成し、ランダムな JWT_SECRET を併せて作成します- 公式の Docker イメージ(PostgreSQL、Multica backend、Multica frontend)を取得します
docker-compose.selfhost.ymlを使ってすべてのサービスを起動します- バックエンドの
/healthエンドポイントが準備できるまで待機します
起動後の継続的なプロダクションプローブには、データベースや migration の問題でチェックが失敗するようにしたい場合は /readyz を使ってください。
バックエンドのコンテナは起動時に データベースの migration を自動で実行します(docker/entrypoint.sh がサーバー起動前に ./migrate up を実行)— バックエンドのログで migration の出力を確認できます。バージョンアップグレードも同じ方法で処理されます。
イメージがまだ公開されていませんか? make selfhost がイメージを取得できない場合、まだリリースされていないバージョンタグにいる可能性があります。安定リリースに切り替えるか、ソースからビルドしてください: make selfhost-build。
起動すると次のようになります。
- フロントエンド: http://localhost:3000
- バックエンド: http://localhost:8080
ポートは 127.0.0.1 でのみ待ち受けます。 docker-compose.selfhost.yml は公開されるすべてのポートを loopback にバインドします — ss -tlnp には 0.0.0.0:8080 は表示されず、設計上、他のマシンからはサービスにアクセスできません。デフォルトの JWT_SECRET と Postgres の認証情報は、公開インターネット上に置いては絶対にいけません。マシン間アクセスが必要な場合は、TLS を終端するリバースプロキシをスタックの前に置いてください — ステップ5b — マシン間: リバースプロキシを前に置くを参照してください。
2. 重要: プロダクションの安全設定を維持する
docker-compose.selfhost.yml はデフォルトで APP_ENV を production に設定し、MULTICA_DEV_VERIFICATION_CODE を空のままにするため、公開インスタンスには固定コードがありません。
MULTICA_DEV_VERIFICATION_CODE はローカルまたは非公開のテスト自動化でのみ設定してください。APP_ENV が non-production のときに固定コードが有効になっていると、コードをリクエストできる誰もがその固定値でサインインできてしまいます。認証設定 → 固定のローカルテストコードを参照してください。
公開デプロイの前には、.env に APP_ENV=production が設定され、MULTICA_DEV_VERIFICATION_CODE が空であることを必ず確認してください。
3. メールサービスを構成する(任意ですが推奨)
メールを構成しないと、ユーザーはメールで認証コードを受け取れず、サーバーは生成したコードを代わりに stdout に出力します。
2種類の配信バックエンドがサポートされています — ネットワークに合うものを選んでください。
オプション A — Resend(クラウド / 公開インターネットのデプロイ):
-
Resend にサインアップして API key を取得します
-
自分が管理する送信用ドメインを認証します
-
.envに次を設定します。RESEND_API_KEY=re_xxxxxxxxxxxx RESEND_FROM_EMAIL=noreply@yourdomain.com
オプション B — SMTP relay(内部ネットワーク / オンプレミス):
デプロイ環境が api.resend.com に到達できない場合や、すでに内部メールリレー(Microsoft Exchange、Postfix、オンプレミスの SendGrid など)がある場合に使ってください。両方が設定されている場合は SMTP_HOST が Resend より優先されるため、認証メールと招待メールは内部リレーにとどまります。STARTTLS は広告されると自動的にアップグレードされます。ポート 465(SMTPS / 暗黙的 TLS)は接続直後の TLS ハンドシェイクを自動的に有効化し、SMTP_TLS=implicit(別名: smtps、ssl)は非標準の SMTPS ポートで強制的に有効化します。
匿名 Exchange 内部リレー(ポート 25) — ホストが IP で信頼され、認証情報なしで送信する場合:
SMTP_HOST=exchange.internal.example.com
SMTP_PORT=25
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_TLS_INSECURE=false
RESEND_FROM_EMAIL=noreply@yourdomain.com # reused as the From: header認証付き送信(ポート 587、STARTTLS) — リレーがサービスアカウントを必要とし、STARTTLS が広告されると自動的にアップグレードされる場合:
SMTP_HOST=smtp.internal.example.com
SMTP_PORT=587
SMTP_USERNAME=multica
SMTP_PASSWORD=...
SMTP_TLS_INSECURE=false # set true only for private CA / self-signed
RESEND_FROM_EMAIL=noreply@yourdomain.com暗黙的 TLS / SMTPS(ポート 465) — STARTTLS を広告しないアリババクラウド / テンセントの法人メールなどのプロバイダー向け。ポート 465 は暗黙的 TLS を自動的に有効化するため、ここでは SMTP_TLS は省略可能です:
SMTP_HOST=smtp.qiye.aliyun.com
SMTP_PORT=465
SMTP_USERNAME=multica@yourdomain.com
SMTP_PASSWORD=...
SMTP_TLS=implicit # optional on 465; required on a non-standard SMTPS port
RESEND_FROM_EMAIL=noreply@yourdomain.com厳格な公開 relay(例: Google Workspace smtp-relay.gmail.com) が公開 IP からのデフォルトの localhost 挨拶を拒否する場合は、SMTP_EHLO_NAME を relay が期待する FQDN に設定してください — そうしないと接続が切断され、後続のコマンドで不明瞭な EOF として表面化します。デフォルトはコンテナのホスト名で、これは通常は有効な FQDN ではありません。
SMTP_HOST=smtp-relay.gmail.com
SMTP_PORT=587
SMTP_EHLO_NAME=mail.yourdomain.com # FQDN the relay accepts; defaults to the (non-FQDN) container hostname
RESEND_FROM_EMAIL=noreply@yourdomain.comその後、再起動します: docker compose -f docker-compose.selfhost.yml restart backend。再起動時、バックエンドはどのプロバイダーを選んだかを出力します(EmailService: SMTP relay … / Resend API / DEV mode)— 認証情報は決してログに残らないため、この行はヘルプを求めるときに共有しても安全です。
追加の認証構成(OAuth、サインアップの許可リスト)と SMTP 変数の完全なリファレンスは、認証設定と環境変数 → メールを参照してください。
4. 初回ログイン + ワークスペースの作成
http://localhost:3000 を開きます。
- メールアドレスを入力します
- 構成したメールバックエンド(Resend または SMTP relay)から認証コードを受け取ります。どちらも構成していない場合は、サーバーコンテナの stdout からコピーしてください —
[DEV] Verification codeの行を探します - non-production の非公開インスタンスで
MULTICA_DEV_VERIFICATION_CODE=888888を明示的に設定した場合を除き、888888を使わないでください - ログインして最初のワークスペースを作成します
5. CLI を自分のサーバーに向ける
CLI のインストールは Cloud クイックスタート → 2. CLI をインストールする と同じです — Homebrew / スクリプト / PowerShell のいずれかを選んでください。
5a. 同じマシン
CLI とサーバーが同じホストで動作している場合、デフォルト値ですでに動作します。
multica setup self-hostこれにより CLI が http://localhost:8080(バックエンド)と http://localhost:3000(フロントエンド)を指すようになり、ブラウザログインを案内し、PAT をローカルに保存して、デーモンを自動的に起動します。
5b. マシン間: リバースプロキシを前に置く
compose スタックは 127.0.0.1 でのみ待ち受けるため、別のマシンにあるデーモンは http://<server-ip>:8080 に直接接続できません — そして、そうなることを望むべきでもありません。さもなければデフォルトの JWT_SECRET が公開インターネットから到達可能になってしまうからです。TLS を終端し、127.0.0.1:8080(バックエンド)と 127.0.0.1:3000(フロントエンド)へ転送するリバースプロキシをサーバーに置き、CLI を公開 HTTPS URL に向けてください。
multica setup self-host \
--server-url https://<your-domain> \
--app-url https://<your-domain>単一のホスト名でフロントエンドとバックエンドの両方を前段に置く(デーモンと Web アプリの両方に必要な WebSocket サポートを含む)最小限の Caddyfile は次のとおりです。
multica.example.com {
# WebSocket route — must come before the catch-all
@ws path /ws /ws/*
handle @ws {
reverse_proxy 127.0.0.1:8080 {
flush_interval -1
}
}
# Backend API
handle /api/* {
reverse_proxy 127.0.0.1:8080
}
# Everything else → frontend
reverse_proxy 127.0.0.1:3000
}プロキシを立ち上げたら、サーバーの .env に FRONTEND_ORIGIN=https://multica.example.com を設定してバックエンドを再起動してください — そうしないと WebSocket の origin チェックがブラウザを拒否します(トラブルシューティング → WebSocket が接続できない)。
Cloudflare Tunnel も堅実な選択肢です — ホストにポートを一切公開せずに TLS と公開ホスト名を提供してくれます。Nginx で同等に構成する方法(app. / api. を別々のホスト名に分離、WebSocket 用の proxy_set_header Upgrade)も同じくらいうまく動作します。重要な要件は、TLS の終端と /ws での Upgrade ヘッダーの転送です。
6. エージェントの作成 + 最初のタスクの割り当て
Cloud と同じ流れです — Cloud クイックスタート → ステップ5-6を参照してください。
7. 使用量ロールアップのスケジューリング(使用量ダッシュボードに必須)
使用量 / ランタイムのダッシュボードは、rollup_task_usage_hourly() が埋める派生テーブル task_usage_hourly からデータを読み取ります。バンドルされた pgvector/pgvector:pg17 の Postgres イメージには pg_cron が含まれておらず、バックエンドもロールアップをインプロセスで実行しません。rollup_task_usage_hourly() をスケジューリングするものが何もないと、生の task_usage 行は届き続けるのに、ダッシュボードは永遠にゼロのままになります。
サポートされているオプションのいずれか1つを選んでください — 1つあれば十分です。
オプション A — 外部 cron / systemd-timer(最もシンプル)。 任意の帯域外スケジューラから5分ごとにロールアップを実行します。冪等でウォーターマーク駆動なので、取りこぼしたティックは追いつきます。
# /etc/cron.d/multica-rollup — every 5 minutes
*/5 * * * * root docker compose -f /path/to/multica/docker-compose.selfhost.yml \
exec -T postgres psql -U multica -d multica \
-c "SELECT rollup_task_usage_hourly();" >/dev/nullオプション B — Postgres を pg_cron を同梱したイメージに置き換える。 docker-compose.selfhost.yml の pgvector/pgvector:pg17 を、pgvector と pg_cron の両方を備えたイメージ(supabase/postgres、またはカスタムビルド)に置き換え、shared_preload_libraries=pg_cron を設定して再起動してから、ジョブを一度登録します。
CREATE EXTENSION IF NOT EXISTS pg_cron;
SELECT cron.schedule(
'rollup_task_usage_hourly',
'*/5 * * * *',
$$SELECT rollup_task_usage_hourly()$$
);オプション C — まず履歴をバックフィルする(アップグレード経路)。 v0.3.4 → v0.3.5+ へアップグレード中で、既存の task_usage 行がある場合、migration 103 は hourly テーブルがシードされるまで refusing to drop legacy daily rollups: ... とともに migrate up を中断します。バンドルされたバックフィルを一度実行してから、オプション A または B を設定してください。
docker compose -f docker-compose.selfhost.yml exec backend \
./backfill_task_usage_hourly --sleep-between-slices=2s--sleep-between-slices=2s は、忙しい DB での読み取り負荷を調整します。完了後、バックエンドのコンテナを再起動すると(起動時に migration が実行されます)アップグレードが完了します。
完全なリファレンス — Kubernetes の CronJob テンプレートとアップグレード順序を含む — は、リポジトリの SELF_HOSTING_ADVANCED.md → Usage Dashboard Rollup にあります。
Kubernetes デプロイ(代替手段)
すでに Kubernetes クラスターを運用している場合、リポジトリには deploy/helm/multica/ に Helm チャートも同梱されています。k8s 用の make selfhost に相当します — 同じバックエンドイメージ、フロントエンドイメージ、pgvector/pgvector:pg17 の Postgres を Deployment / Service / Ingress としてパッケージングし、values.yaml からレンダリングされた1つの ConfigMap を併せて提供します。k3s + Traefik + local-path を基準に作成されており、Ingress コントローラーとデフォルトの ReadWriteOnce StorageClass があるあらゆるクラスターで動作するはずです。
このチャートは シークレット値をテンプレート化しません。multica-secrets という名前の Secret を名前で参照するため、実際の JWT / DB / Resend / Google キーが git や values.yaml に置かれる必要はまったくありません。ネームスペースと Secret を kubectl で一度作成してください。
kubectl create namespace multica
kubectl -n multica create secret generic multica-secrets \
--from-literal=JWT_SECRET="$(openssl rand -hex 32)" \
--from-literal=POSTGRES_PASSWORD="$(openssl rand -hex 16)" \
--from-literal=RESEND_API_KEY="" \
--from-literal=GOOGLE_CLIENT_SECRET="" \
--from-literal=CLOUDFRONT_PRIVATE_KEY="" \
--from-literal=MULTICA_DEV_VERIFICATION_CODE=""その後、チャートをインストールします。
git clone https://github.com/multica-ai/multica.git
cd multica
helm install multica deploy/helm/multica -n multicaデフォルト値はホスト名 multica.dev.lan(web)と api.multica.dev.lan(バックエンド)を想定しています。これらを /etc/hosts(またはローカル DNS)に追加し、Ingress に到達可能な任意のノード IP を指すようにしてください。別のホスト名を使うには、deploy/helm/multica/values.yaml をコピーして ingress.frontend.host / ingress.backend.host と、それに対応する backend.config.appUrl / frontendOrigin / localUploadBaseUrl / googleRedirectUri を編集し、-f my-values.yaml でインストールしてください。
コールドクラスターでは、バックエンドが Postgres を待ち、migration を実行する間、数分間 Running 状態だが Ready ではないことがあります — startupProbe がこれを吸収するため、ポッドは再起動されないはずです。Ready になったら:
curl -H "Host: api.multica.dev.lan" http://<ingress-ip>/healthz
# {"status":"ok","checks":{"db":"ok","migrations":"ok"}}その後 http://multica.dev.lan を開き、上記の ステップ4 — 初回ログインから続けてください。CLI を Ingress のホスト名に向けます。
multica setup self-host \
--server-url http://api.multica.dev.lan \
--app-url http://multica.dev.lanチャートを変更せずに最新のイメージだけを取得するには、kubectl -n multica rollout restart deploy/multica-backend deploy/multica-frontend を実行してください。特定の Multica リリースに固定するには、values ファイルで images.backend.tag / images.frontend.tag を設定して helm upgrade を実行してください。helm -n multica uninstall multica はワークロードを削除しますが、PVC と Secret は保持します。kubectl delete namespace multica はすべてを消去します。
完全なリファレンス — 3つのログインモード、web イメージにビルド時に焼き込まれた REMOTE_API_URL に対する backend ExternalName の回避策、リソース制限、TLS — は、リポジトリの SELF_HOSTING.md にあります。
よくある問題
- バックエンドが起動しない:
docker compose -f docker-compose.selfhost.yml logs backendでコンテナのログを確認してください。たいていは.envの不正なDATABASE_URLまたはJWT_SECRETが原因です - 認証コードが届かない: メールバックエンドが構成されていない場合(Resend も SMTP もない)→
docker compose logs backendで[DEV] Verification codeを探してください - WebSocket が接続できない: 公開デプロイでは、
FRONTEND_ORIGINを実際のフロントエンドのドメインに必ず設定する必要があります。トラブルシューティング → WebSocket が接続できないを参照してください - 使用量 / ランタイムのダッシュボードがゼロのまま:
rollup_task_usage_hourly()がスケジューリングされていません — 上記の ステップ7とトラブルシューティング → 使用量ダッシュボードがゼロと表示されるを参照してください migrate upがrefusing to drop legacy daily rollupsで失敗する:v0.3.4 → v0.3.5+のアップグレード経路ガードです。まずbackfill_task_usage_hourlyを実行してください — ステップ7 → オプション Cを参照してください
次のステップ
- 環境変数 — 完全な env リファレンス
- 認証設定 — Resend / OAuth / サインアップ許可リストの詳細
- GitHub 連携 — GitHub App を接続して、PR がイシューに自動でリンクされ、マージするとイシューが閉じられるように設定する
- トラブルシューティング — うまくいかないときはここから始めてください
- デスクトップアプリ —
~/.multica/desktop.jsonを通じた任意のデスクトップ設定。Web フロントエンド + CLI が依然として最速のセルフホスティング経路です