Skip to content

Production Deployment

Breeze ships as pre-built Docker images on GitHub Container Registry. A single docker compose up -d brings up a fully working production stack with automatic TLS.

The core stack (docker-compose.yml) includes:

Service Image Purpose
Binaries Init ghcr.io/lanternops/breeze/binaries Copies agent/viewer binaries to a shared volume, then exits
Caddy caddy:2.8-alpine Reverse proxy, auto-TLS, security headers
API ghcr.io/lanternops/breeze/api Hono API server
Web ghcr.io/lanternops/breeze/web Astro SSR dashboard
Portal ghcr.io/lanternops/breeze/portal Astro SSR customer portal, served under /portal (see Customer Portal)
PostgreSQL pgvector/pgvector:pg16 Primary database
Redis redis:7-alpine Job queue, caching, rate limiting
Coturn coturn/coturn:4-alpine TURN relay server for WebRTC remote desktop (opt-in via --profile turn)

The pgvector extension is required only if you enable the Workspace built-in (BREEZE_WORKSPACE_ENABLED=true), which uses it for content embeddings. Workspace ships inside the API image but is off by default: with the flag unset, no Workspace migrations run and any Postgres 16 image works. The bundled POSTGRES_IMAGE_REF default is pgvector-capable regardless, so enabling Workspace later needs no database swap — supply your own Postgres image and you must provide pgvector yourself before setting the flag, or the API will fail to start with extension "vector" is not available.

An optional monitoring stack (Prometheus, Grafana, Alertmanager, Loki, Promtail, exporters) is available as a separate overlay — see Monitoring.

An optional m365-graph-read-executor sidecar (published as ghcr.io/lanternops/breeze/m365-graph-read-executor) isolates the Microsoft 365 customer Graph-read certificate and Key Vault access away from the API. It is only exercised when Customer Graph-read consent is enabled (M365_CUSTOMER_GRAPH_READ_ONBOARDING_ENABLED=true); otherwise it sits idle and can be left undeployed. It is not part of the bundled compose files by default — see Customer Microsoft 365 Graph-read consent for the full variable contract, including the private base URL and read-only signing-key mount it depends on.

  1. Prepare the server

    You need Docker and Docker Compose on a Linux VPS. See Prerequisites.

  2. Clone and configure

    Terminal window
    git clone https://github.com/LanternOps/breeze.git
    cd breeze
    cp .env.example .env
  3. Set your domain and secrets

    Edit .env and set these required values:

    Terminal window
    BREEZE_DOMAIN=breeze.yourdomain.com
    ACME_EMAIL=admin@yourdomain.com

    Generate all secrets at once:

    Terminal window
    for key in JWT_SECRET APP_ENCRYPTION_KEY MFA_ENCRYPTION_KEY \
    ENROLLMENT_KEY_PEPPER MFA_RECOVERY_CODE_PEPPER \
    METRICS_SCRAPE_TOKEN SESSION_SECRET TURN_SECRET; do
    echo "${key}=$(openssl rand -hex 32)"
    done
    echo "AGENT_ENROLLMENT_SECRET=$(openssl rand -hex 32)"
    echo "POSTGRES_PASSWORD=$(openssl rand -base64 24 | tr -d '/+=')"
    echo "REDIS_PASSWORD=$(openssl rand -hex 32)"

    Paste the output into .env.

    Then set these required deployment-mode flags. Without them the API refuses to boot in production:

    Terminal window
    # Self-hosted by default. Set to "true" only if you're running the hosted SaaS edition.
    IS_HOSTED=false
    # Trust anchor (raw base64 Ed25519 public key) for the official Breeze GitHub
    # releases. This is a public key — also embedded in the agent and CI — so it is
    # safe to commit. Leave the default below unless you build and sign your own binaries.
    RELEASE_ARTIFACT_MANIFEST_PUBLIC_KEYS=yzx8ftmcls6uBetFC5SYnZhBo+cbur3IX50TbBthTso=

    If Breeze is behind a reverse proxy, also set TRUST_PROXY_HEADERS=true and list the proxy’s CIDRs in TRUSTED_PROXY_CIDRS. See Environment Variables for details.

  4. Configure TURN server

    The TURN server enables remote desktop connectivity across NATs and firewalls. Set the server’s public IP address:

    Terminal window
    # Set to this server's public IP address (required for remote desktop)
    TURN_HOST=203.0.113.10
    # Optional: TURN realm (defaults to breeze.local)
    TURN_REALM=breeze.local

    Coturn is behind a Docker Compose profile and does not start by default. To enable it:

    Terminal window
    docker compose --profile turn up -d

    Or add COMPOSE_PROFILES=turn to your .env file for persistent activation.

  5. Start the stack

    Terminal window
    docker compose up -d

    That’s it. On first start, the API container automatically runs database migrations and seeds the initial admin user. Caddy obtains a TLS certificate from Let’s Encrypt.

  6. Verify the deployment

    Terminal window
    # Check health
    curl https://breeze.yourdomain.com/health
    # Check running containers
    docker compose ps
    # View API logs
    docker compose logs -f api

The monitoring stack lives in a separate compose overlay file (docker-compose.monitoring.yml) and includes Prometheus, Grafana, Alertmanager, Loki, Promtail, and database exporters.

To deploy with monitoring:

Terminal window
docker compose -f docker-compose.yml -f docker-compose.monitoring.yml up -d

Add a Grafana password to .env:

Terminal window
GRAFANA_ADMIN_PASSWORD=$(openssl rand -base64 16 | tr -d '/+=')

Grafana is available at http://127.0.0.1:3000 (localhost only by default).

By default, images pull the latest tag. To pin to a specific release:

Terminal window
# In .env
BREEZE_VERSION=0.67.1

Then pull and restart:

Terminal window
docker compose pull && docker compose up -d

Override default resource limits via environment variables:

Terminal window
# Redis memory limit (default: 256mb)
REDIS_MAXMEMORY=512mb

Redis password authentication is required in production. The API will refuse to start if REDIS_URL doesn’t carry a password. The secret-generation loop above already produces REDIS_PASSWORD; pair it with a REDIS_URL that uses it:

Terminal window
REDIS_PASSWORD=$(openssl rand -hex 32)
REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379

The docker-compose.yml passes REDIS_PASSWORD to the Redis container automatically. See Environment Variables for details.

Terminal window
cd breeze
git pull origin main
docker compose pull
docker compose up -d

Pre-built images are pulled from GHCR. Database migrations run automatically on startup.