Production
This page covers what to change when taking a HappyView instance from local development to production. For setup instructions, see Deployment. This page assumes you already have a working deployment and focuses on hardening and operational concerns.
Session secret
Set SESSION_SECRET to a random string of at least 64 characters. This signs the session cookies issued during OAuth login; rotating it invalidates every existing session.
openssl rand -base64 48
Never commit the secret to source control. Store it in your platform's secret manager (Railway variables, Docker secrets, Kubernetes secrets, etc.).
Token encryption key
If you use plugins that require secrets (API keys, OAuth credentials), set TOKEN_ENCRYPTION_KEY to a base64-encoded 32-byte key. This encrypts plugin secrets at rest using AES-256-GCM:
openssl rand -base64 32
Without this variable, the dashboard's plugin secret fields are disabled and plugins can only read secrets from environment variables.
TLS and PUBLIC_URL
HappyView does not terminate TLS. Put it behind a reverse proxy (nginx, Caddy, Cloudflare Tunnel, a platform-managed load balancer) and set PUBLIC_URL to the public HTTPS URL:
PUBLIC_URL=https://happyview.example.com
PUBLIC_URL is used to construct OAuth redirect URIs, so it must exactly match the URL users hit — including scheme. A mismatch breaks OAuth login.
Database
SQLite is fine for small to medium instances and is the default. Switch to Postgres if you need:
- Multiple HappyView replicas sharing one database
- Larger-than-memory working sets
- External tools that need direct read access to the records table
See the database setup guide for configuration details and Postgres → SQLite migration if you're moving the other direction. Migrations run automatically on startup regardless of backend.
Rate limits
HappyView has a per-client token-bucket rate limiter for XRPC endpoints. The defaults (set via DEFAULT_RATE_LIMIT_CAPACITY and DEFAULT_RATE_LIMIT_REFILL_RATE) apply to any API client that doesn't have per-client overrides. Raise the defaults cautiously — they exist so one misbehaving integrator can't saturate the server.
Per-client overrides are set at client creation or via PUT /admin/api-clients/{id} (see Admin API — API Clients).
Logging
The default RUST_LOG setting (happyview=debug,tower_http=debug) is noisy. For production, drop the verbosity:
RUST_LOG=happyview=info,tower_http=info
Structured logs go to stdout, so any platform that captures container stdout (Railway, Fly, ECS, Kubernetes) will ingest them without further configuration. For retention and querying, ship stdout to your usual log aggregator.
Event log retention
The admin event log is stored in the same database as records. EVENT_LOG_RETENTION_DAYS (default 30) controls automatic cleanup. Set to 0 to keep events indefinitely — useful for compliance-sensitive deployments, but plan for database growth.
Health checks
GET /health returns 200 ok when HappyView can bind its HTTP listener. Use it as the readiness/liveness probe for your platform.
For a deeper check, hit GET /xrpc/com.atproto.server.describeServer — this exercises the database and lexicon registry, and only returns 200 if HappyView can actually serve requests.
Backups
- SQLite: back up the database file (e.g.
data/happyview.db) plus its-waland-shmsidecar files. Usesqlite3 happyview.db ".backup '/path/backup.db'"for a consistent snapshot while HappyView is running. - Postgres: standard
pg_dump/ managed-Postgres snapshots.
Most of what HappyView stores is derivable from the network — lost records can be re-indexed via backfill. You can't recover from the network: user accounts and permissions, API keys, API clients, plugin secrets, and the Jetstream cursor. Prioritize those in your backup plan.
Next steps
- Configuration — full environment variable reference
- Permissions — lock down admin access before exposing the dashboard publicly
- Troubleshooting — diagnose issues with a running instance