Skip to main content

Production checklist

Before relying on HQ for real work, walk through this list. Most items take under a minute.
1

Pin image versions

Don’t track :latest in production. Pin all images to the same release tag (e.g., v0.1.3) in your .env. See Pinning image versions below.
2

Disable Supabase email signup

In your Supabase project: Authentication → Providers → Email → disable “Allow new users to sign up.” This prevents anyone with your Supabase URL from creating an account. Add users manually via the Supabase dashboard.
3

Enable MFA on your Supabase account

Your Supabase dashboard has full access to every table. Enable multi-factor auth in your Supabase account settings.
4

Use Tailscale or a reverse proxy

Default port bindings are loopback-only (localhost). To access HQ from other devices, use Tailscale (recommended — private, no port exposure) or a reverse proxy with TLS (Caddy, Cloudflare Tunnel, nginx).
5

Generate strong secrets

If you set GATEWAY_AUTH_TOKEN manually, use a strong random value: openssl rand -hex 32. Never reuse secrets across environments.
6

Set up backups

Back up your Supabase database, gateway state volume, and .env file. See Backing up below. At minimum, enable Supabase Pro backups or schedule a weekly pg_dump.
7

Set up monitoring

Check gateway heartbeats in Settings → Gateways and review command history in Settings → System. Set up log forwarding if your infrastructure supports it. See Monitoring below.
8

Review the security model

Read Security model → and SECURITY.md to understand trust boundaries — especially the Docker socket mount and service role key implications.

Updating HQ

Updating is a pull-and-restart — no rebuilds required.
cd ~/.yourhq          # or wherever you installed
docker compose pull   # download latest images
docker compose up -d  # restart with new images
The UI’s Supabase config lives in the workspace registry (runtime, not baked into the image), so updates never require a UI rebuild. Just pull and restart. Update from the UI: Settings → Gateways → hover a gateway row → Update gateway. This runs docker compose pull && up -d on the gateway host via the runner daemon.
Check GitHub releases before updating. If a release includes new migrations, run them in Supabase SQL Editor before pulling the new images. Skipping this step can cause the UI to show a “schema out of date” banner.

Upgrading across versions

When a new HQ version ships with database schema changes, you need to run the new migrations before (or immediately after) pulling the new images. Here’s the full process.

1. Check what changed

Read the release notes for the version you’re upgrading to. Each release lists any new migration files. You can also diff migration files directly:
# See what migrations were added since your current version
git log v0.1.0..v0.2.0 --oneline -- db/migrations/

2. Back up before upgrading

Always back up before applying new migrations. At minimum:
# Database
pg_dump \
  "postgresql://postgres:<db-password>@db.<project-ref>.supabase.co:5432/postgres" \
  -Fc -f pre-upgrade-$(date +%Y%m%d).dump

# Gateway state volume
docker run --rm \
  -v yourhq-gateway-state:/src \
  -v $(pwd):/dst \
  alpine tar -czf /dst/gateway-state-pre-upgrade.tar.gz -C /src .

3. Apply new migrations

Open your Supabase project’s SQL Editor and run each new migration file in filename order. All HQ migrations are idempotent (IF NOT EXISTS, CREATE OR REPLACE), so re-running an already-applied migration is safe.
You can check which migrations have been applied by querying the schema version table:
SELECT * FROM _schema_version ORDER BY applied_at;
If this table doesn’t exist, you’re on a pre-v0.1.0 install — apply all migrations from 001 onward.

4. Pull and restart

cd ~/.yourhq
docker compose pull
docker compose up -d

5. Verify

After restarting:
  • The UI should load without a “schema out of date” banner
  • Settings → System should show all services as healthy
  • Settings → Gateways should show your gateway(s) with a green heartbeat

Downgrading

HQ migrations are forward-only. There are no down migrations. If an upgrade breaks something:
  1. Restore your database from the pre-upgrade pg_dump
  2. Pin your images to the previous version tag (see Pinning image versions)
  3. Open an issue on GitHub with the error

Version compatibility

All images in a running stack should be from the same release. Don’t mix a v0.2.0 UI with v0.1.0 gateway images — the UI may reference database columns or RPCs that don’t exist yet. Pin all images to the same tag when you pin any of them.

Backing up

Three things to back up. Don’t skip any of them. (HQ hosted handles backups and updates automatically.)

1. Supabase — workspace data

Supabase stores everything: contacts, tasks, agents, knowledge items, routines, collections, usage history, audit log, and gateway registrations. Losing it means losing your entire workspace.Option A: Supabase built-in backups (Pro tier)Supabase Pro includes daily automatic backups and point-in-time recovery. Enable under Project Settings → Database → Backups. This is the easiest path if you’re on Pro.Option B: pg_dump (free, manual)
pg_dump \
  "postgresql://postgres:<db-password>@db.<project-ref>.supabase.co:5432/postgres" \
  -Fc -f supabase-backup-$(date +%Y%m%d).dump
Your database password and project ref are in Supabase → Project Settings → Database → Connection string.To restore:
pg_restore \
  "postgresql://postgres:<db-password>@db.<project-ref>.supabase.co:5432/postgres" \
  -d postgres supabase-backup-20260101.dump
Recommended frequency: daily for active workspaces, weekly for light use.
The gateway-state Docker volume holds everything that lives on the gateway: agent git branches, OpenClaw config, Chrome profiles, desktop shortcuts, auth tokens, plugins, and the VNC password.Losing this means losing your agents’ memory, skills, and browser sessions. They can be reprovisioned, but you’ll lose accumulated state.Back up the volume:
docker run --rm \
  -v yourhq-gateway-state:/src \
  -v $(pwd):/dst \
  alpine tar -czf /dst/gateway-state-$(date +%Y%m%d).tar.gz -C /src .
This creates a compressed archive in your current directory.Restore from backup:
# Stop the gateway first
docker compose stop gateway dispatcher runner

# Restore into the volume
docker run --rm \
  -v yourhq-gateway-state:/dst \
  -v $(pwd):/src \
  alpine sh -c "cd /dst && tar -xzf /src/gateway-state-20260101.tar.gz"

# Restart
docker compose up -d
Better option: git remote sync. Set GIT_REMOTE_URL in .env to push every agent branch to GitHub or Gitea automatically. Even if you lose the Docker volume, the branches survive. See Configuration → Git remote sync.Recommended frequency: weekly for agent state, or enable git remote sync for continuous backup of agent branches.
.env contains GATEWAY_AUTH_TOKEN, optional git credentials, and other secrets. It’s a flat text file and easy to lose track of.Store a copy in:
  • A password manager (1Password, Bitwarden, etc.)
  • An encrypted note
  • Your secure git repo (private, access-controlled)
Never commit .env to a public repo. The file contains keys that give full database access to your workspace.

Monitoring

Container health

# Check all container status
docker compose ps

# Tail all logs
docker compose logs -f

# Check a specific service
docker compose logs -f gateway
docker compose logs -f runner
docker compose logs -f dispatcher
All services should show Status: Up (ui, gateway, dispatcher, runner, embedder, file-processor, plugin-runner). If a container is in Restarting state, tail its logs to find the error.

Gateway heartbeat

The gateway daemon writes last_seen_at to Supabase every 30 seconds. The UI shows heartbeat health on Settings → Gateways.
  • Healthy (green) — last_seen_at updated within 90 seconds
  • Stale (amber) — status is ready but heartbeat is old
  • No signal — gateway has never reported (possibly never started)
If you see “Stale”, the runner daemon is likely stopped. Check:
docker compose logs -f runner | tail -50

Command history

Settings → System → Commands shows every command the runner has processed, with status, stdout, and stderr. Useful for diagnosing failed provisions or auth flows.

Disk usage

Agent branches, Chrome profiles, and logs accumulate over time:
# Check volume sizes
docker system df -v

# Check gateway-state volume specifically
docker run --rm -v yourhq-gateway-state:/data alpine du -sh /data/*
If the volume is large, the most likely culprit is Chrome profile data. You can clear an agent’s Chrome profile from the agent detail page → Operations → Clear browser profile.

Pinning image versions

By default, HQ tracks :latest. For production installs where stability matters more than freshness:
# In .env
UI_IMAGE=ghcr.io/yourhq/yourhq-ui:2026.04.20
GATEWAY_IMAGE=ghcr.io/yourhq/yourhq-gateway:2026.04.20
DISPATCHER_IMAGE=ghcr.io/yourhq/yourhq-dispatcher:2026.04.20
RUNNER_IMAGE=ghcr.io/yourhq/yourhq-runner:2026.04.20
Then pull and restart:
docker compose pull && docker compose up -d
Pin to the same date tag across all four images — mixing versions can cause incompatibilities.

Restarting individual services

You don’t need to restart the whole stack for most changes:
# Restart just the UI (e.g. after changing ALLOWED_ORIGINS)
docker compose restart ui

# Restart gateway after .env changes
docker compose up -d gateway

# Force-recreate a service (e.g. after changing port bindings)
docker compose up -d --force-recreate gateway
See Configuration → Section 7 for which variables require which restart.

Uninstalling

docker compose down -v deletes all Docker volumes, including the gateway-state volume with all agent files. Back up first if you want to keep anything.
cd ~/.yourhq
docker compose down -v   # stops containers and deletes volumes
cd ..
rm -rf .yourhq
To also delete the Supabase project: go to Supabase → Settings → General → Delete project.