Deployment
Supported deployment shapes
Section titled “Supported deployment shapes”- Local development — Node.js + Docker for PostgreSQL and Redis
- Self-hosted — Docker Compose on any Linux host
- Split deployment — Static frontend (Vercel) + containerized API
Docker Compose (self-hosted)
Section titled “Docker Compose (self-hosted)”The repository ships two Compose files:
| File | Purpose |
|---|---|
docker-compose.yml |
Full local stack (PostgreSQL, Redis, API, web) |
docker-compose.prod.yaml |
Production-oriented — bring your own DB and secrets |
Production deploy
Section titled “Production deploy”# Copy and fill in your production secretscp apps/api/.env.example apps/api/.env
docker compose -f docker-compose.prod.yaml --env-file apps/api/.env up --build -dEnvironment variables
Section titled “Environment variables”API (required)
Section titled “API (required)”# Generate with: openssl rand -base64 64JWT_SECRET=ACTIVITY_HMAC_SECRET=
# DatabaseDB_HOST=DB_PORT=5432DB_NAME=DB_USER=DB_PASSWORD=
# RedisREDIS_HOST=REDIS_PORT=6379
# OriginsFRONTEND_URL=https://handoverkey.appCORS_ORIGINS=https://handoverkey.appAPI (optional)
Section titled “API (optional)”API_PORT=3001REDIS_PASSWORD=JWT_EXPIRES_IN=1hJWT_REFRESH_EXPIRES_IN=7dCOOKIE_DOMAIN=ADMIN_EMAILS=admin@example.comGRACE_PERIOD_HOURS=48TWO_FACTOR_ISSUER=HandoverKeySMTP is strongly recommended. Without it, email verification, inactivity reminders, handover notifications, and password resets won’t work.
SMTP_HOST=smtp.gmail.comSMTP_PORT=587SMTP_SECURE=falseSMTP_USER=your@email.comSMTP_PASS=your-app-passwordSMTP_FROM=noreply@handoverkey.appStripe (optional billing)
Section titled “Stripe (optional billing)”# Leave blank to disable paid plansSTRIPE_SECRET_KEY=STRIPE_WEBHOOK_SECRET=STRIPE_PRO_PRICE_ID=STRIPE_FAMILY_PRICE_ID=Register your webhook at https://yourdomain.com/api/v1/billing/webhook and subscribe to:
checkout.session.completed, customer.subscription.updated, customer.subscription.deleted, invoice.payment_failed
Web (build-time)
Section titled “Web (build-time)”VITE_API_URL=https://api.handoverkey.app/api/v1VITE_WS_URL=wss://api.handoverkey.app/wsLeave both empty in local development — the Vite proxy handles it.
Frontend hosting
Section titled “Frontend hosting”The web app is a Vite SPA. Any host must rewrite all non-asset routes to index.html.
A Vercel config is included at apps/web/vercel.json.
Reverse proxy notes
Section titled “Reverse proxy notes”- Forward WebSocket upgrade requests for
/ws - Enable TLS for both origins in production
- Set
FRONTEND_URLandCORS_ORIGINSto match your actual web origin
Health and monitoring
Section titled “Health and monitoring”| Endpoint | Purpose |
|---|---|
GET /health |
Service health (DB, Redis, queues, realtime) |
GET /metrics |
Prometheus-compatible metrics |
Production checklist
Section titled “Production checklist”- TLS on both web and API origins
- Strong
JWT_SECRETandACTIVITY_HMAC_SECRET(64+ bytes) - PostgreSQL backups and restore tested
- Redis persistence or managed Redis configured
- SMTP credentials configured and tested
-
FRONTEND_URLandCORS_ORIGINSverified -
VITE_API_URLandVITE_WS_URLset -
/healthendpoint wired into monitoring - SPA rewrites confirmed on web host