6.5 KiB
Multica
Open source project management with coding agents. Runs the official ghcr.io/multica-ai/multica-backend and multica-web images, plus Postgres (pgvector). Agents run on this host through the multica CLI daemon.
Layout
| Service | Networks | Exposed |
|---|---|---|
| postgres | internal |
— |
| backend | internal, traefik |
traefik: /ws on FRONTEND_HOST; host: 127.0.0.1:8090 (daemon) |
| frontend | internal, traefik |
traefik: everything else on FRONTEND_HOST |
internalis a plain bridge for service-to-service traffic and outbound egress (email).- Single domain. Next.js proxies
/api,/auth,/uploads,/v1to the backend. It does not proxy websockets, so traefik sends/wsto the backend directly. - No separate API domain. That layout requires
COOKIE_DOMAIN, otherwise every write fails CSRF (403). - The backend entrypoint runs DB migrations on every start.
Setup
cp .env.example .envand fill it in.JWT_SECRET:openssl rand -hex 32POSTGRES_PASSWORD:openssl rand -hex 24. Must be URL-safe; base64 (/,+) breaksDATABASE_URLwith "invalid port".MULTICA_VCS_SECRET_KEY:openssl rand -base64 32
- Point DNS for
FRONTEND_HOSTat traefik. docker compose up -d- Sign in with email. The code is sent via Resend/SMTP, or printed in
docker logs multica-backend-1if email isn't working. - After creating your workspace, optionally set
DISABLE_WORKSPACE_CREATION=trueand/orALLOW_SIGNUP=false, thendocker compose up -d.
Versioning
Server images and the CLI are released together and must run the same version. The tag is pinned in docker-compose.yml (currently v0.6.5), not in .env. Do not use :latest: a docker compose pull would move the server without the CLI.
Local agent daemon
Runs as the multica service user (system UID, home /var/lib/multica, no password, not in docker) under its systemd user unit. Connects to the backend on 127.0.0.1:${BACKEND_HOST_PORT} (8090; 8080 belongs to traefik). The public domain does not resolve back to this host (hairpin NAT returns the router's self-signed cert), so use localhost.
The home directory is required: it holds ~/.multica (config, token, logs), ~/.claude (Claude Code login), ~/.config/systemd/user (autostart unit), ~/multica_workspaces (task checkouts) and ~/.local/bin (CLIs).
Create the user:
sudo useradd --system --user-group --create-home --home-dir /var/lib/multica --shell /bin/bash multica
sudo loginctl enable-linger multica # user systemd manager at boot, no login needed
Shell as multica with a systemd user session (used by every command below and in Upgrade):
sudo -iu multica env XDG_RUNTIME_DIR=/run/user/$(id -u multica) \
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u multica)/bus bash -l
Inside that shell:
export PATH="$HOME/.local/bin:$PATH"
# Claude Code (per-user install and login)
curl -fsSL https://claude.ai/install.sh | bash
claude # log in, then /exit
# multica CLI at the version pinned in docker-compose.yml
VER=0.6.5
cd "$(mktemp -d)"
curl -fsSLO "https://github.com/multica-ai/multica/releases/download/v${VER}/multica-cli-${VER}-linux-amd64.tar.gz"
curl -fsSLO "https://github.com/multica-ai/multica/releases/download/v${VER}/checksums.txt"
sha256sum --check --ignore-missing checksums.txt
tar -xzf "multica-cli-${VER}-linux-amd64.tar.gz" multica
install -m 755 multica ~/.local/bin/multica
cd ~
multica setup self-host --server-url http://localhost:8090 --app-url https://multica.whitney.rip
multica login --token= # personal access token from Settings → Tokens in the web UI
multica daemon start
multica daemon status # expect: running, agent "claude", 1+ workspace
multica daemon autostart enable # captures current PATH; ~/.local/bin must be on it
- Detects agent CLIs on
PATH(claudehere). - The autostart unit only inherits
PATH. Daemon settings go inmultica config set. Other env vars agents need (e.g.GH_TOKEN) go in/var/lib/multica/.config/environment.d/*.conf. - For agents that push code, give
multicaits own git identity and SSH key or token. - Status from
jr:sudo -u multica XDG_RUNTIME_DIR=/run/user/$(id -u multica) systemctl --user status multica-daemon. Logs:multica daemon logsasmultica. - Remove the user:
sudo loginctl disable-linger multica && sudo pkill -u multica && sudo userdel --remove multica. - Sign out of the CLI:
multica auth logout. This only deletes the local token; revoke it in the web UI too.
Upgrade
- Change both image tags in
docker-compose.yml. docker compose pull && docker compose up -d && docker logs -f multica-backend-1- As
multica, reinstall the CLI with the sameVER, thenmultica daemon stop && multica daemon start. - Check that
multica versionmatches the image tag.
Risks
- Agent access: agents run as
multica, which has no docker or sudo, but can still read anything world-readable on this host and reach localhost services (including the backend on 8090). RESEND_API_KEY: the current value doesn't look like a Resend key (those start withre_). Until it's fixed, login codes only appear in the backend logs.- No Redis: auth endpoints have no rate limiting (logged at startup). Acceptable while signup is limited. Adding Redis also requires trusted-proxy settings (
RATE_LIMIT_TRUSTED_PROXIES,MULTICA_TRUSTED_PROXIES) covering traefik and the frontend container, or all users share one rate-limit bucket. - Uploads on local disk (
UPLOADS_DATA_DIR): back it up along withPOSTGRES_DATA_DIR. - Backend on the
traefiknetwork (needed for/ws): other containers on that network can reach it directly.
Possible extensions
- Email via SMTP instead of Resend: set the
SMTP_*vars. - Google login:
GOOGLE_CLIENT_ID/SECRET. The redirect URI is${FRONTEND_ORIGIN}/auth/callback. - GitHub App (PR cards, repo picker):
GITHUB_APP_SLUG,GITHUB_WEBHOOK_SECRET,GITHUB_APP_ID,GITHUB_APP_PRIVATE_KEY. Add them to compose. NeedsMULTICA_PUBLIC_URLreachable from GitHub. - Gitea/Forgejo integration: already enabled (
MULTICA_VCS_*). Connect it per workspace in Settings → Integrations. - S3 storage, Slack/Lark/Telegram bots, server-side LLM (chat titles), plugins: env vars are documented in upstream
.env.exampleanddocker-compose.selfhost.yml. Add the ones you need to the backendenvironment:. - More agents: install another supported CLI (
codex,opencode, ...) on the host and restart the daemon.