Self-hosting Opentra
Opentra runs on your own VM or server. You get the full PostgreSQL database and complete control of your data.
Requirements
Section titled “Requirements”- 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.
Deploy
Section titled “Deploy”# on your hostgit clone https://github.com/Opentra/psa.git opentra && cd opentra
# create your env file and fill in secretscp .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 redisRun 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):
pnpm install
# apply all Prisma migrations to the bundled databasepnpm --filter @opentra/database db:migrate:deploy
# seed the 4 default project templates (idempotent — safe to re-run)pnpm --filter @opentra/database db:seedStart the app
Section titled “Start the app”# build and start the web app + background worker from sourcedocker compose up -d --build web workerThe 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:
docker compose psdocker compose logs -f web workerConfiguration
Section titled “Configuration”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.
Backups & upgrades
Section titled “Backups & upgrades”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-formatpg_dumpof the databasecmt_psa_config_<timestamp>.tar.gz.gpg— a GPG-encrypted bundle of your.envfiles anddocker-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:
git pullpnpm installpnpm --filter @opentra/database db:migrate:deploydocker compose up -d --buildManaged deployment (we run it for you, with an SLA) is available on the Pro + Managed plan — see pricing.