Skip to content

Self-hosting Opentra

Opentra runs on your own VM or server. You get the full PostgreSQL database and complete control of your data.

  • A Linux host — ~2 vCPU / 4 GB RAM is plenty for ~10 techs / ~100 clients
  • Docker + Docker Compose (v2)
  • Git, Node.js 22 and pnpm on the host — used to install dependencies and run the database migrations, and read access to the private repo
  • A domain/subdomain for the app (e.g. psa.yourshop.io) with TLS terminated at a reverse proxy in front of the app
  • Outbound HTTPS to the integrations you use (Microsoft Graph, Xero, HubSpot, the Anthropic API, etc.)

The Compose file bundles everything the app depends on — PostgreSQL 17 and Redis 7 (Redis backs the BullMQ job queue) — so you don’t need to provision those separately unless you want to.

Terminal window
# on your host
git clone https://github.com/Opentra/psa.git opentra && cd opentra
# create your env file and fill in secrets
cp .env.example .env
# at minimum set POSTGRES_PASSWORD, NEXTAUTH_SECRET and NEXTAUTH_URL.
# generate a secret with: openssl rand -base64 32
# bring up the data services first (Postgres + Redis)
docker compose up -d postgres redis

Run migrations (required — not automatic)

Section titled “Run migrations (required — not automatic)”

The app containers do not run database migrations on startup, so apply the schema before starting them. Run this from the host repo — it installs dependencies and migrates against the Postgres that Compose exposes on 127.0.0.1:5432 (the DATABASE_URL in .env.example already points there):

Terminal window
pnpm install
# apply all Prisma migrations to the bundled database
pnpm --filter @opentra/database db:migrate:deploy
# seed the 4 default project templates (idempotent — safe to re-run)
pnpm --filter @opentra/database db:seed
Terminal window
# build and start the web app + background worker from source
docker compose up -d --build web worker

The images build from source in the repo (there is no pre-published image), so the first up takes a few minutes.

The web app listens on port 4000. Point your reverse proxy (Caddy, nginx, Cloudflare Tunnel, etc.) at http://127.0.0.1:4000 and terminate TLS there. Check it’s healthy:

Terminal window
docker compose ps
docker compose logs -f web worker

Core settings live in .env; per-integration credentials are entered in Settings → Integrations inside the app. Key .env variables:

Variable Purpose
POSTGRES_PASSWORD Password for the bundled Postgres (cmt_psa database + user)
NEXTAUTH_URL Public URL of the app, e.g. https://psa.yourshop.io
NEXTAUTH_SECRET Session secret — openssl rand -base64 32
AZURE_AD_CLIENT_ID / _SECRET / _TENANT_ID Microsoft Entra ID sign-in (primary auth)
GOOGLE_CLIENT_ID / _SECRET Google Workspace sign-in (secondary auth)
MS_GRAPH_CLIENT_ID / _SECRET / _TENANT_ID / MS_GRAPH_MAILBOX Microsoft Graph — turns inbound mail into tickets
ANTHROPIC_API_KEY AI agent features
XERO_*, HUBSPOT_API_KEY, TWILIO_* Integrations, filled in as you connect each

See Integrations for the full list and setup per provider.

Because you host it, back up on your normal schedule. The repo ships scripts/db-backup.sh, which each run writes two matched artifacts to /opt/backup:

  • cmt_psa_<timestamp>.dump — a custom-format pg_dump of the database
  • cmt_psa_config_<timestamp>.tar.gz.gpg — a GPG-encrypted bundle of your .env files and docker-compose.yml

You need both to recover: the .env files are gitignored and hold NEXTAUTH_SECRET (which couples to stored integration-token encryption) plus every OAuth/API credential — a database dump alone can’t decrypt them. Run it from cron (4×/day is the shipped cadence) and enable the off-box step at the bottom of the script so a copy lives off the machine. See RECOVERY.md in the repo for the full restore procedure.

To upgrade, pull the latest code, apply any new migrations from the host, then rebuild:

Terminal window
git pull
pnpm install
pnpm --filter @opentra/database db:migrate:deploy
docker compose up -d --build

Managed deployment (we run it for you, with an SLA) is available on the Pro + Managed plan — see pricing.