Skip to content

Deployment

Janus is designed as a local-first, single-user gateway. This page covers Docker deployment and production-oriented configuration.

Docker

Pull pre-built image

Multi-arch images (amd64 + arm64) are published to GitHub Container Registry:

docker pull ghcr.io/amanverasia/janus:latest
mkdir -p janus-data
janus config-init --path janus-data/config.yaml
# Edit janus-data/config.yaml — add providers, API keys via ${ENV_VAR}

docker compose up -d

The compose file mounts ./janus-data to /home/janus/.janus inside the container. This persists:

  • config.yaml — seed config (loaded once on first startup)
  • janus.db — SQLite database (providers, combos, usage, inventory, etc.)

The image runs the app as user janus (uid 1000). On first docker compose up, Docker may create ./janus-data on the host as root, which blocks SQLite from opening janus.db. The container entrypoint fixes ownership of the mounted data directory on each start (no manual chown needed).

If you still hit permission errors (e.g. host uid is not 1000), fix ownership once:

sudo chown -R 1000:1000 janus-data

Or, without sudo, using a throwaway root container:

docker run --rm -u 0 -v "$(pwd)/janus-data:/data" alpine sh -c 'chown -R 1000:1000 /data'

The compose file passes an explicit list of variables from your host .env into the container: the provider API keys, the OAuth client ids/secrets, and INVENTORY_ENCRYPTION_KEY, INVENTORY_ENCRYPTION_PREVIOUS_KEY, INVENTORY_PUSH_TOKEN and JANUS_ALLOW_INSECURE_DEV_KEY. A variable that is only in .env but not listed under environment: never reaches Janus — add it there (or in a docker-compose.override.yml):

OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GEMINI_API_KEY=...
INVENTORY_ENCRYPTION_KEY=...  # encrypts inventory and gateway provider credentials
INVENTORY_PUSH_TOKEN=...

Retain INVENTORY_ENCRYPTION_KEY with your backup material. Janus fails clearly if an encrypted credential is present but the matching key is unavailable; when rotating the key, set INVENTORY_ENCRYPTION_PREVIOUS_KEY to the old key and restart so stored credentials are re-sealed automatically (see the inventory docs). If no encryption key is configured while real credentials exist, startup refuses to continue unless JANUS_ALLOW_INSECURE_DEV_KEY=1 opts into plaintext storage. Dashboard configuration export is plaintext by design; a raw database backup remains encrypted.

Build from source

git clone https://github.com/amanverasia/Janus.git
cd Janus
docker compose up -d --build

Default bind address

The Docker image binds to 0.0.0.0:20128 (all interfaces). Enable API key auth when exposing Janus beyond localhost:

server:
  host: 0.0.0.0
  require_api_key: true

Or toggle require_api_key at runtime from the dashboard Settings page.

Remote access

API endpoints

When host: 0.0.0.0, clients connect to http://<host>:20128/v1/.... Require API keys and use TLS termination (reverse proxy) for anything beyond a trusted LAN.

Dashboard authentication

Every client, including 127.0.0.1 and localhost, is redirected to /dashboard/login until it authenticates with a valid Janus API key. DB-managed keys must be active and have Allow dashboard login (can_login=true); configured static keys are also accepted. The session cookie is scoped to /dashboard and marked Secure when the request arrives over HTTPS, so a TLS-terminating reverse proxy should forward X-Forwarded-Proto. Username/password login and the loopback bypass are not supported. Legacy dashboard credential settings are purged during database initialization. See Dashboard — Authentication.

The require_api_key setting controls API endpoint authentication only. It does not make the dashboard anonymous when disabled.

Reverse proxy

Janus does not terminate TLS itself. Put nginx, Caddy, or Traefik in front:

location / {
    proxy_pass http://127.0.0.1:20128;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_buffering off;   # important for SSE streaming
}

For streaming (stream: true), disable proxy buffering.

Data backup

Back up the entire data directory:

tar czf janus-backup.tar.gz janus-data/

The SQLite database is the source of truth after first startup. Restoring janus.db restores providers, combos, pricing overrides, usage history, budgets, inventory keys, and cooldown state.

Health check

curl http://localhost:20128/v1/health
# {"status": "ok"}

The root URL / redirects to the Cloudline dashboard at /dashboard/ui.

Inventory submissions, previews, push requests, and bulk actions accept bodies up to 4 MiB; backup imports accept up to 16 MiB including multipart overhead. Larger requests return 413, including requests without Content-Length. All credential checks share the CHECK_CONCURRENCY limit (default 8), including scheduled and bulk checks.

Inventory submission rate limits use the resolved client address. Behind a reverse proxy, forward X-Forwarded-For and configure Uvicorn's FORWARDED_ALLOW_IPS to trust only your proxy addresses (loopback is trusted by default). Untrusted forwarded headers do not change the rate-limit bucket.