Install with Docker Compose
Run the controller container against an external PostgreSQL and put nginx (or any TLS-terminating reverse proxy) in front of it. The compose file runs only the controller — you bring the database.
Requirements
Section titled “Requirements”- A host with Docker and Docker Compose.
- PostgreSQL 16+, reachable from the host (managed service or a separate server — for a
quick trial you can add a
postgres:16service to the same compose file). - A domain name and a TLS certificate (Let’s Encrypt below).
- Paid plans only: your per-organization license token and public key from the Tessera portal. Community needs no license — just skip the license lines below.
1. Keys and secrets
Section titled “1. Keys and secrets”Generate the encryption and JWT keys once and keep them in a .env file next to the compose
file (running Community? drop the license line):
cat > .env <<EOFTESSERA_ENC_KEY=$(openssl rand -hex 32)TESSERA_JWT_SECRET=$(openssl rand -hex 32)TESSERA_LICENSE=<license token>EOFchmod 600 .env2. docker-compose.yml
Section titled “2. docker-compose.yml”services:controller: image: registry.tessera.company/tessera/controller:v1.0.0 restart: unless-stopped ports: - "127.0.0.1:8443:8443" # nginx terminates public TLS and proxies here environment: # External PostgreSQL — the compose file does not run a database TESSERA_DATABASE_URL: "postgres://tessera:PASSWORD@db.example.com:5432/tessera?sslmode=require" TESSERA_PUBLIC_URL: "https://controller.example.com" TESSERA_HTTP_ADDR: ":8443" TESSERA_HOST_KEY: "/data/ssh_host_ed25519_key" # generated once, kept on the volume TESSERA_RECORDINGS_DIR: "/data/recordings" TESSERA_ENC_KEY: "${TESSERA_ENC_KEY}" TESSERA_JWT_SECRET: "${TESSERA_JWT_SECRET}" TESSERA_LICENSE: "${TESSERA_LICENSE}" volumes: - controller_data:/data # SSH host key + session recordings — must persist
volumes:controller_data:The image is pulled from Tessera’s public registry, so no login is needed. Pin the tag to a released version (see Installation options); the schema migrates automatically on start.
3. nginx reverse proxy
Section titled “3. nginx reverse proxy”The controller serves HTTPS on :8443 (self-signed internally), and every proxied protocol
rides WebSocket over that port — so nginx must forward the Upgrade header and use long
timeouts. Put the map in the http block once:
map $http_upgrade $connection_upgrade { default upgrade; '' close; }Then the server block (Let’s Encrypt terminating TLS):
server { listen 80; server_name controller.example.com; return 301 https://$host$request_uri; }
server { listen 443 ssl; server_name controller.example.com;
ssl_certificate /etc/letsencrypt/live/controller.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/controller.example.com/privkey.pem;
proxy_ssl_verify off; # controller uses a self-signed cert internally proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 3600s; # long-lived SSH / DB / RDP sessions proxy_send_timeout 3600s; proxy_buffering off; # stream interactive terminals / RDP client_max_body_size 0; # large transfers
location /metrics { deny all; } # keep metrics internal location / { proxy_pass https://127.0.0.1:8443; }}TESSERA_PUBLIC_URL must match the public name so OIDC redirects and notification links resolve.
4. Start and sign in
Section titled “4. Start and sign in”docker compose --env-file .env up -ddocker compose logs -f controller # watch it come up and migrateOpen https://controller.example.com. On a fresh database the controller bootstraps a default
administrator — sign in as admin / admin and change the password immediately. Then
follow the initial setup walkthrough.
Upgrading and backups
Section titled “Upgrading and backups”docker compose pull controllerdocker compose --env-file .env up -d controllerThe container restarts and active sessions drop — upgrade on purpose. Back up your
PostgreSQL database, the .env file (especially TESSERA_ENC_KEY), and the controller_data
volume (SSH host key) — see Backups & upgrades.
Configuration lists every setting and
Licensing & activation covers how the license is applied.
We would like to count visits with Google Analytics, which needs a cookie. Nothing is loaded and nothing is stored unless you accept. What this sets.