Docs

Self-host Headquarters

Run the full CRM on your own machine or VPS. Start locally in minutes, then harden for always-on production when you’re ready.

Prerequisites

Install these before you clone the repo:

Quick start

The fastest path is the bootstrap script. It starts the Supabase Docker stack, applies migrations, writes public env keys, serves the API, and runs the app.

  1. Clone the repo
    git clone https://github.com/JTCorrin/Headquarters.git
    cd Headquarters
  2. Start the stack
    ./scripts/dev-up.sh

    Same as pnpm dev:stack. First run applies migrations. Use pnpm dev:stack:reset when you want a clean database.

  3. Open the app at http://127.0.0.1:5173, sign up, and create an organisation through onboarding.

Useful helpers:

./scripts/dev-status.sh   # URLs and public key summary
./scripts/dev-down.sh     # stop api-v1 serve + supabase stop

Backend only (no Vite): ./scripts/dev-up.sh --no-app, then pnpm dev in another terminal.

ServiceURL
Apphttp://127.0.0.1:5173
Supabase APIhttp://127.0.0.1:54321
Studiohttp://127.0.0.1:54323

Production (always-on VPS)

Self-hosting for real use means a durable host with Docker, Node 22+, pnpm, and the Supabase CLI — plus a reverse proxy with TLS in front of the Node app. There is no first-party Docker Compose file today; the app process is Node, and Supabase runs via the CLI’s Docker stack.

  1. Provision the host with Docker (daemon up), Node.js 22+, pnpm 10, Supabase CLI ~2.111, git, and a reverse proxy (Caddy, nginx, or Traefik).
  2. Clone and install
    git clone https://github.com/JTCorrin/Headquarters.git
    cd Headquarters
    pnpm install --frozen-lockfile
  3. Point Auth at your public origin in supabase/config.toml: set site_url, additional_redirect_urls (include https://your.domain/auth/callback), and ensure auth email/SMTP settings match how you want signup confirmation to work.
  4. Start Supabase and load edge secrets
    supabase start

    Write supabase/functions/.env (Edge loads these on container create). At minimum pin CORS and cron secrets — generate strong random values and keep them offline:

    API_CORS_ORIGIN=https://your.domain
    APP_BASE_URL=https://your.domain
    PUBLIC_SUPABASE_URL=https://supabase.your.domain
    MAILBOX_SYNC_SECRET=…
    RECURRING_INVOICES_CRON_SECRET=…
    PLAYBOOKS_CRON_SECRET=…

    PUBLIC_SUPABASE_URL here must be the browser-reachable Kong/API URL (not only loopback), so Auth and signed Storage URLs work for users. After writing functions/.env, bounce the stack with supabase stop then supabase start so Edge picks up secrets. Do not remove the edge-runtime container alone.

  5. Configure the app .env
    PUBLIC_SUPABASE_URL=https://supabase.your.domain
    PUBLIC_SUPABASE_ANON_KEY=…   # from `supabase status`
    PUBLIC_API_BASE_URL=         # leave empty — same-origin /api/v1 proxy
    APP_BASE_URL=https://your.domain

    Leave PUBLIC_HOSTED_BILLING, PUBLIC_BILLING_API_URL, PUBLIC_LANDING_URL, and BILLING_CLAIM_SECRET unset. Those are for the hosted SaaS product only.

  6. Build and run the Node app
    pnpm build
    pnpm start

    pnpm start runs node build/index.js (adapter-node). Put it behind systemd (or similar) and reverse-proxy HTTPS to that process. Prefer empty PUBLIC_API_BASE_URL so browsers only call /api/v1 on your app origin.

  7. Schedule background jobs with host cron (or another scheduler). Hit the Edge function URLs with the matching secret headers — for example every minute for mailbox sync and every few minutes for recurring invoices and playbooks:
    • mailbox-sync — header x-mailbox-sync-secret
    • jobs-recurring-invoices — header x-recurring-invoices-cron-secret
    • jobs-playbooks — header x-playbooks-cron-secret

    Keep secrets out of process argv (use a small wrapper script that reads a file). Protect these endpoints with network isolation; they use shared secrets rather than end-user JWTs.

Core CRM works without OAuth or mailbox providers. Invite email goes through the inviter’s personal mailbox SMTP (Settings → Mail), not a platform SMTP env var.

Optional integrations

Copy values into .env / edge env using the repo’s .env.example as the guide. Common optional pieces:

  • Google / Azure login (Supabase Auth external providers + config.toml)
  • Mailbox Microsoft / Google OAuth (separate from login)
  • Google Calendar OAuth
  • Cron secrets for mailbox sync, recurring invoices, and playbooks

Bootstrap scripts do not overwrite secrets you have already set. Org AI providers use user-stored vault keys, not platform env vars.

License

Headquarters is source-available under the Elastic License 2.0 (not OSI “open source”).

  • Allowed: use, modify, and self-host — including for your own internal business operations — as long as you keep copyright and license notices.
  • Not allowed: offering Headquarters (or a substantial set of its features) to third parties as a hosted or managed service without a separate license.

Help

Source, issues, and deeper backend notes live in the GitHub repository. Prefer issues for bugs and self-host snags so others can find the same fix.

Compare self-host vs hosted pricing · Back to Headquarters