Deployment guide
TwistCue targets a single-box deployment for v2 (both API and admin on the same host as meacrypto, using systemd + Apache + Let’s Encrypt). Multi-node horizontal scaling is phase 2.
| Role | Hostname (planned) | Purpose |
|---|---|---|
| App box | YOUR_APP_HOST | API + admin + crons |
| DB | YOUR_DB_HOST | Postgres 15 (managed) |
| Redis | YOUR_REDIS_HOST | Cache / rate-limit |
| DNS | Registrar | twistcue.com and admin.twistcue.com |
Domains
Section titled “Domains”| Domain | Points to | Notes |
|---|---|---|
admin.twistcue.com | YOUR_SERVER_IP (app box) | The admin UI |
api.twistcue.connectorme.com | YOUR_SERVER_IP | The API |
docs.twistcue.com (planned) | Static CDN | This docs site |
systemd services
Section titled “systemd services”Two units on the app box:
/etc/systemd/system/twistcue-api.service:
[Unit]Description=TwistCue APIAfter=network.target postgresql.service redis.service
[Service]Type=simpleUser=twistcueWorkingDirectory=/opt/twistcue/app/services/apiEnvironmentFile=/etc/twistcue-api.envExecStart=/usr/bin/node dist/main.jsRestart=alwaysRestartSec=5
[Install]WantedBy=multi-user.target/etc/systemd/system/twistcue-admin.service:
[Unit]Description=TwistCue Admin (Next.js)After=network.target twistcue-api.service
[Service]Type=simpleUser=twistcueWorkingDirectory=/opt/twistcue/app/apps/adminEnvironmentFile=/etc/twistcue-admin.envExecStart=/usr/bin/node .next/standalone/server.jsEnvironment=PORT=3100 HOSTNAME=127.0.0.1Restart=alwaysRestartSec=5
[Install]WantedBy=multi-user.targetEnv files (mode 0600)
Section titled “Env files (mode 0600)”/etc/twistcue-api.env:
NODE_ENV=productionDATABASE_URL=postgres://twistcue:REDACTED@YOUR_DB_HOST:5432/twistcueREDIS_URL=redis://YOUR_REDIS_HOST:6379JWT_SECRET=REDACTED_LONG_RANDOM_STRINGADMIN_JWT_SECRET=REDACTEDCF_STREAM_ACCOUNT_ID=REDACTEDCF_STREAM_API_TOKEN=REDACTEDCF_STREAM_WEBHOOK_SECRET=REDACTED# ...RevenueCat, dLocal, pawaPay, card MID, etc./etc/twistcue-admin.env:
NODE_ENV=productionNEXT_PUBLIC_API_BASE=https://api.twistcue.connectorme.com/v1ADMIN_INTERNAL_API_BASE=http://127.0.0.1:3000/v1Both files must be chown twistcue:twistcue and chmod 0600.
Apache vhosts
Section titled “Apache vhosts”Admin vhost (basic-auth guard until WebAuthn ships):
<VirtualHost *:443> ServerName admin.twistcue.com
SSLEngine on SSLCertificateFile /etc/letsencrypt/live/admin.twistcue.com/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/admin.twistcue.com/privkey.pem
# Optional: IP allowlist for pre-launch # <RequireAll> # Require ip YOUR_OFFICE_IP_CIDR # </RequireAll>
# Pre-launch: basic-auth layer above the app login # <Location /> # AuthType Basic # AuthName "TwistCue Admin" # AuthUserFile /etc/apache2/twistcue.htpasswd # Require valid-user # </Location>
ProxyPass / http://127.0.0.1:3100/ ProxyPassReverse / http://127.0.0.1:3100/ ProxyPreserveHost On</VirtualHost>API vhost:
<VirtualHost *:443> ServerName api.twistcue.connectorme.com SSLEngine on SSLCertificateFile /etc/letsencrypt/live/api.twistcue.connectorme.com/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/api.twistcue.connectorme.com/privkey.pem ProxyPass / http://127.0.0.1:3000/ ProxyPassReverse / http://127.0.0.1:3000/ ProxyPreserveHost On</VirtualHost>Deployment steps (manual)
Section titled “Deployment steps (manual)”From a checkout on the app box:
cd /opt/twistcue/appgit fetch --all && git checkout main && git pullnpm cicd services/api && npm run build && npx prisma migrate deploycd ../../apps/admin && npm run buildsudo systemctl restart twistcue-api twistcue-adminZero-downtime notes
Section titled “Zero-downtime notes”- v2 is single-box, so “zero-downtime” is best-effort. Restarts drop ~2s of active requests.
- If a migration is destructive, run it during a maintenance window.
- For real HA later: front with an LB, run 2+ nodes, sticky-none, rolling restart.
Docs deployment
Section titled “Docs deployment”The docs app builds to static:
cd apps/docsnpm run build # outputs dist/ with Pagefind indexServe dist/ from any CDN or static host (Cloudflare Pages, Netlify, S3+CloudFront, or nginx on the app box). No runtime needed.
Backup + DR
Section titled “Backup + DR”Daily Postgres dumps to off-box storage (managed provider handles this if using a managed DB). Retain 30 days minimum. Test restore quarterly.
Do not
Section titled “Do not”- ❌ Commit any
.envfile. - ❌ Log JWTs or webhook payloads with PII.
- ❌ Run
prisma migrate resetin prod. - ❌ Expose port 3000 (API) or 3100 (admin) to the internet directly; always go through Apache with TLS.