Self-hosting Harkly

One machine, Docker Compose, about ten minutes. You get the API, the admin dashboard, the customer portal and a worker, with Postgres and Redis behind them. Harkly is AGPL-3.0; SSO/SAML for admins and audit-log export need a license key, everything else is yours as is.

What you need

  • A Linux box with Docker 24+ and Compose v2, 2 CPUs and 4 GB of RAM
  • Three hostnames pointing at it, e.g. api.example.com, app.example.com and feedback.example.com, with TLS from your own reverse proxy (Caddy, nginx, Traefik) — the containers speak plain HTTP
  • A Resend API key if you want email to leave the box

Install

git clone https://github.com/Rad-Soft/harkly.git && cd harkly
cp .env.example .env

Fill in .env:

settingvalue
HARKLY_API_URLhttps://api.example.com — what browsers call
HARKLY_DASHBOARD_URLhttps://app.example.com
HARKLY_PORTAL_URLhttps://feedback.example.com
COOKIE_DOMAIN.example.com — the sign-in cookie has to cross from api. to app.
PORTAL_DEV_SLUGthe slug of the workspace the portal serves (you pick it at sign-up)
BETTER_AUTH_SECRET, TOKEN_ENCRYPTION_KEYopenssl rand -hex 32 each
POSTGRES_PASSWORDanything but the default
RESEND_API_KEY, EMAIL_FROMso sign-in codes reach inboxes

Then:

docker compose pull            # or `--build` to build from this checkout
docker compose up -d
docker compose run --rm core bun run doctor

doctor prints one line per thing it checked and exits non-zero while something still needs fixing. Core applies database migrations every time it starts, so the first up creates the schema.

Point your proxy at the three ports (3000 API, 3100 dashboard, 3200 portal), open https://app.example.com, sign in with your email — the code is in docker compose logs core until Resend is configured — and create the workspace with the slug you put in PORTAL_DEV_SLUG.

Reverse proxy example (Caddy)

api.example.com      { reverse_proxy localhost:3000 }
app.example.com      { reverse_proxy localhost:3100 }
feedback.example.com { reverse_proxy localhost:3200 }

Keep X-Forwarded-For on: the gateway's rate limits and vote integrity go by the real client IP.

Upgrading

Images are tagged by release (2026.9.16 style) and latest. Pin one in .env with HARKLY_VERSION=2026.9.16, then:

docker compose pull && docker compose up -d

Migrations run on core's start and are additive; the release notes at https://harkly.app/changelog call out anything that isn't.

Backup and restore

Everything lives in Postgres; Redis holds only rate-limit counters and can be lost. Back up nightly:

docker compose exec -T db pg_dump -U harkly -Fc harkly > harkly-$(date +%F).dump

Restore into a fresh install (stop the app services first so nothing writes):

docker compose stop core worker gateway dashboard portal
docker compose exec -T db pg_restore -U harkly -d harkly --clean --if-exists < harkly-2026-09-14.dump
docker compose start core worker gateway dashboard portal

The Postgres volume is harkly-pgdata; snapshotting it (with the container stopped) is an equally good backup.

License keys

Set HARKLY_LICENSE_KEY in .env and restart core. doctor shows who the key is for, which features it carries and when it expires. Keys are signed statements checked offline; nothing phones home.

Telemetry

Off unless HARKLY_TELEMETRY=1. When on, the worker sends one request a day to api.harkly.app/telemetry with an anonymous instance id (a hash of your auth secret), the version and row counts per table. No names, emails or content ever leave your install.

Features that need more than the box

featureneeds
Email support inboxEMAIL_INBOUND_DOMAIN with MX at Resend, RESEND_WEBHOOK_SECRET, Resend's inbound webhook at https://api.example.com/webhooks/resend
Google sign-ina Google OAuth client, GOOGLE_CLIENT_ID/SECRET, GOOGLE_SIGN_IN=1
AI answers and the agentANTHROPIC_API_KEY
Slack inbox and DMsa Slack app, SLACK_CLIENT_ID/SECRET/SIGNING_SECRET, events at https://api.example.com/webhooks/slack
Linear synca Linear OAuth app, LINEAR_CLIENT_ID/SECRET
Custom portal domains, several workspaces per installnot yet on self-host; use PORTAL_DEV_SLUG for one workspace

Running the dev stack instead

docker compose up db redis plus the bun run --cwd apps/* dev commands in the README run everything from source with hot reload.