Skip to main content
Trinity

Single-Server Deployment

Run Trinity on a Linux VPS or dedicated server with a stable public URL. This guide uses docker-compose.prod.yml, which disables hot-reload, adds health checks and restart policies to every service, and keeps Redis off the public network.

Prerequisites

  • Linux server (Ubuntu 22.04 LTS or later recommended), minimum 8 GB RAM
  • Docker Engine 24+ and Docker Compose plugin (docker compose — no hyphen)
  • A domain or subdomain pointing to your server's IP (e.g., trinity.your-domain.com)
  • openssl on the server for secret generation
  • Outbound HTTPS access from the server (for Docker image pulls and Anthropic API calls)

1. Clone the Repository

git clone https://github.com/abilityai/trinity.git
cd trinity

2. Configure .env

cp .env.example .env

Security-critical (must be set before first boot)

VariableHow to generateNotes
SECRET_KEYopenssl rand -hex 32JWT signing key. Never reuse across instances.
ADMIN_PASSWORDChoose a strong passwordMinimum 12 characters. Drives both admin login and the MCP server's legacy auth path. Requireddocker-compose.prod.yml refuses to render if unset (issue #692).
CREDENTIAL_ENCRYPTION_KEYopenssl rand -hex 32Encrypts OAuth tokens and credentials. If lost, all encrypted credentials become unrecoverable.
INTERNAL_API_SECRETopenssl rand -hex 32Authenticates scheduler-to-backend calls.
REDIS_PASSWORDopenssl rand -hex 24Admin/default ACL user. For recovery and ad-hoc ops.
REDIS_BACKEND_PASSWORDopenssl rand -hex 24Runtime ACL user for backend and scheduler. Required — compose refuses to render without it.

Generate all six at once:

echo "SECRET_KEY=$(openssl rand -hex 32)"
echo "CREDENTIAL_ENCRYPTION_KEY=$(openssl rand -hex 32)"
echo "INTERNAL_API_SECRET=$(openssl rand -hex 32)"
echo "REDIS_PASSWORD=$(openssl rand -hex 24)"
echo "REDIS_BACKEND_PASSWORD=$(openssl rand -hex 24)"

Redis security note: Trinity uses two separate Redis passwords by design. REDIS_BACKEND_PASSWORD is the runtime credential embedded in REDIS_URL for the backend and scheduler containers. Even if a platform container were compromised and this password leaked, it does not grant access to destructive Redis commands (FLUSHALL, CONFIG, SHUTDOWN) — those require REDIS_PASSWORD.

Required for agent functionality

VariableNotes
ANTHROPIC_API_KEYRequired for agents to run Claude. Can be left blank and configured in Settings after login.
GITHUB_PATRequired to clone private GitHub template repos.

Required for production access

VariableNotes
FRONTEND_URLYour public-facing domain (e.g., https://trinity.your-domain.com). Used for OAuth redirect callbacks and email verification links.
PUBLIC_CHAT_URLThe externally reachable URL for public chat links and webhooks. Often the same as FRONTEND_URL. Leave blank if all users access via VPN.

Email authentication

VariableNotes
EMAIL_PROVIDERresend (recommended), sendgrid, smtp, or console (dev only)
RESEND_API_KEYRequired when EMAIL_PROVIDER=resend.
SENDGRID_API_KEYRequired when EMAIL_PROVIDER=sendgrid.
SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASSWORD / SMTP_FROMRequired when EMAIL_PROVIDER=smtp.

Data path

The prod compose uses a bind-mount directory for trinity.db instead of a named Docker volume. Use an absolute path on a server for clarity:

TRINITY_DATA_PATH=/srv/trinity-data

Create the directory before starting:

mkdir -p /srv/trinity-data

Database backend

Trinity stores all platform state in SQLite by default, in trinity.db under your TRINITY_DATA_PATH bind mount (/data/trinity.dbinside the container) — the zero-config default for local dev and evaluation.

On every backend boot a versioned, crash-safe, concurrency-safe migration runner brings the schema up to date (the bespoke SQLite runner, or Alembic for PostgreSQL): a cross-process lock serialises it so workers and the scheduler cannot race each other, and table rebuilds run in a transaction that rolls back cleanly on a mid-migration crash. If a migration is pending or has failed, /health returns 503 with a migrations block (applied, expected, first_pending) naming the stuck migration — so a 503 from curl http://localhost:8000/health during an upgrade is actionable.

PostgreSQL is now the recommended backend for production, and SQLite support ends 2026-09-01 (after that date it stops receiving schema migrations and fixes). To run on PostgreSQL, set one variable in .env:

DATABASE_URL=postgresql://trinity:your-postgres-password@your-db-host:5432/trinity

Both the backend and the scheduler pick it up. Notes for the prod compose:

  • docker-compose.prod.yml ships no bundled PostgreSQL service — point DATABASE_URL at an operator-managed instance (a managed cloud database or your own PostgreSQL server). The bundled --profile postgres container exists only in the dev compose.
  • Selection is non-sticky and non-destructive: comment DATABASE_URL out and the next restart is back on SQLite.
  • A fresh PostgreSQL database is initialized automatically on first boot (Alembic-managed migrations).
  • Migrating an existing SQLite instance? Use the Trinity Ops Agent's /migrate-to-postgres skill (ops-agent guide) — a validate-then-cutover flow that never writes to your SQLite file, so rollback is one line. New-instance setup details: docs/POSTGRESQL_SETUP.md in the repo.
  • On PostgreSQL, back up with pg_dump instead of copying trinity.db — see Backup and Restore.

3. Build the Base Agent Image

./scripts/deploy/build-base-image.sh

Required before you can create any agents. Takes 5–10 minutes on first build.

4. Build and Start Platform Services

docker compose -f docker-compose.prod.yml build
docker compose -f docker-compose.prod.yml up -d

This starts: backend, frontend, redis, mcp-server, scheduler, vector, and otel-collector.

The cloudflared tunnel service is not started by default — it requires an explicit --profile tunnel flag. See the Public Access guide.

5. First Login

Open your domain in a browser. Log in with:

  • Username: admin
  • Password: the ADMIN_PASSWORD you set in .env

After login, go to Settings → Email Whitelist to allow team members to log in via email verification.

6. Connect from Claude Code

Create an MCP API key first:

1

Log in to the web UI

2

Go to Keys in the top navigation

3

Create a new key and copy it

Then connect from your Claude Code session:

/trinity:connect
# URL: http://your-server:8080/mcp  (or https://trinity.your-domain.com/mcp if behind a reverse proxy)
# API Key: (your MCP API key)

Restart vs. Down

Use docker compose restart, not down/up. docker compose down removes the trinity-agent-network, which orphans every running agent container — they keep running but lose their network and have to be removed and recreated. restart preserves both the agents and the network.

# Correct way to restart platform services
docker compose -f docker-compose.prod.yml restart backend frontend mcp-server scheduler

# Full stop (agents will need to be restarted/recreated)
docker compose -f docker-compose.prod.yml down

Verify Service Health

# Backend
curl -s http://localhost:8000/health

# Scheduler
curl -s http://localhost:8001/health

# Frontend
curl -s -o /dev/null -w '%{http_code}' http://localhost

# Redis
docker exec trinity-redis redis-cli ping

# MCP Server
curl -s http://localhost:8080/health

# Vector
docker exec trinity-vector wget -q -O - http://localhost:8686/health

See the Monitoring guide for the full health check reference.