Skip to content

Deployment

Overview for deploying LaunchKit Pro to a VPS or cloud VM. Refer to launchkit-pro-infra samples for Nginx, systemd, and scripts — never copy private credentials from any internal runbook into buyer docs.

Architecture overview

Internet → Nginx (443) → app.yourdomain.com → Next.js :3000
                        → api.yourdomain.com  → NestJS  :4000
                        → docs.yourdomain.com → static VitePress build

API and frontend bind to localhost; Nginx is the public edge.

Production VPS layout

LaunchKit Labs reference paths (adjust for your domain):

PathPurpose
/var/www/launchkitlabs/apps/api/currentActive API release (symlink)
/var/www/launchkitlabs/shared/backend/.envShared API secrets
/var/www/launchkitlabs/logs/api-out.logAPI stdout log
/var/www/launchkitlabs/deploy-backend.shProduction deploy entrypoint

Full layout: VPS Deployment.

Domains: app.launchkitlabs.com, api.launchkitlabs.com, docs.launchkitlabs.com, launchkitlabs.com.

Backend deployment (robust script)

Use the production script (copy from launchkit-pro-infra/scripts/deploy-backend.sh):

bash
/var/www/launchkitlabs/deploy-backend.sh

Flow

  1. Clone new release to apps/api/releases/<timestamp>/
  2. Symlink shared env — never source .env (spaces in values break shell)
  3. npm ci --include=dev — required for nest build
  4. npx prisma generate + npx prisma migrate deploy
  5. npm run build
  6. npm prune --omit=devafter successful build only
  7. Preflight — start on port 4001, health check before promoting
  8. Switch current symlink → systemctl restart launchkit-api
  9. Production health — retry up to 60 seconds
  10. Rollback previous release only if health still fails

WARNING

Do not run npm ci --omit=dev before npm run build — causes nest: not found. See Deployment Gotchas.

systemd example:

  • WorkingDirectory=/var/www/launchkitlabs/apps/api/current
  • ExecStart=.../current/dist/main.js
  • EnvironmentFile=/var/www/launchkitlabs/shared/backend/.env

Health check:

bash
curl -sf http://127.0.0.1:4000/health
curl -sf https://api.launchkitlabs.com/health

Frontend deployment

  1. Clone launchkit-pro-frontend
  2. Set production .env.local or build-time env:
env
NEXT_PUBLIC_API_URL=https://api.yourdomain.com
NEXT_PUBLIC_APP_NAME=LaunchKit Pro
NEXT_PUBLIC_DOCS_URL=https://docs.yourdomain.com
NEXT_PUBLIC_SEARCH_SHORTCUT_ENABLED=true

Search uses the backend GET /search API. Docs links in search results use NEXT_PUBLIC_DOCS_URL. See Dashboard Search.

Security: set TRUST_PROXY=true behind Nginx, keep RATE_LIMIT_ENABLED=true, and list production origins in CORS_ALLOWED_ORIGINS. See Security Hardening.

  1. npm ci && npm run build && npm run start

Example systemd: launchkit-frontend.service → port 3000

Docs site deployment

Build static buyer docs:

bash
cd launchkit-pro-docs
npm ci
npm run build

Output: .vitepress/dist — serve as static files at docs.yourdomain.com.

nginx
root /var/www/yourapp/sites/docs;
location / {
  # VitePress emits guide/page.html for clean URLs like /guide/updates
  try_files $uri $uri.html $uri/ =404;
}
error_page 404 /404.html;

Nginx routing

HostUpstream
app.yourdomain.comhttp://127.0.0.1:3000
api.yourdomain.comhttp://127.0.0.1:4000
docs.yourdomain.comstatic files

Sample config structure in launchkit-pro-infra/samples/nginx/.

SSL

Use Let's Encrypt (Certbot):

bash
sudo certbot --nginx -d yourdomain.com -d app.yourdomain.com \
  -d api.yourdomain.com -d docs.yourdomain.com

Renewal dry run:

bash
sudo certbot renew --dry-run

Upload size (Nginx)

On API server block:

nginx
client_max_body_size 10m;

Match backend MAX_UPLOAD_SIZE_MB and frontend NEXT_PUBLIC_MAX_UPLOAD_SIZE_MB.

Uploads and avatars (production)

Set a persistent upload directory outside release folders:

env
STORAGE_PROVIDER=LOCAL
STORAGE_UPLOAD_DIR=/var/www/yourapp/shared/uploads
STORAGE_PUBLIC_BASE_URL=https://api.yourdomain.com/uploads
  • Avatars and workspace files are served at https://api.yourdomain.com/uploads/...
  • Frontend uses NEXT_PUBLIC_API_URL to resolve asset URLs
  • Include the upload directory in filesystem backups (not included in PostgreSQL dumps)
  • Do not bundle runtime uploads in buyer ZIP packages
  • For durable media storage in production, prefer S3, Cloudflare R2, or Cloudinary. Keep those provider secrets in backend env only.
  • Provider migration from local disk to cloud storage is future work; back up local uploads until migrated.

See File Storage & Avatars.

Systemd services

Infra samples include:

  • launchkit-api.service.sample — backend
  • launchkit-frontend.service.sample — frontend

Typical commands:

bash
sudo systemctl daemon-reload
sudo systemctl enable launchkit-api launchkit-frontend
sudo systemctl start launchkit-api launchkit-frontend
sudo systemctl status launchkit-api --no-pager

Deployment scripts

Sample scripts in launchkit-pro-infra/samples/scripts/:

  • deploy-backend.sh.sample
  • deploy-frontend.sh.sample
  • backup-postgres.sh.sample

Customize paths and users for your server. Do not embed secrets in scripts committed to git.

Backups and restore

LaunchKit Pro includes a backup and restore foundation for self-hosted deployments. The app-level backup script is designed for VPS layouts such as:

bash
/var/www/launchkitlabs/apps/api/current
/var/www/launchkitlabs/apps/frontend/current
/var/www/launchkitlabs/apps/docs/current
/var/www/launchkitlabs/shared/backend/.env
/var/www/launchkitlabs/shared/frontend/.env.production

Manual run:

bash
bash /var/www/launchkitlabs/infra/scripts/backup-launchkit.sh

Daily cron example:

cron
0 2 * * * /var/www/launchkitlabs/infra/scripts/backup-launchkit.sh >> /var/www/launchkitlabs/logs/backup.log 2>&1

Backups include PostgreSQL, uploads when present, shared env files, Nginx configs, deploy scripts, and release symlink metadata. The archive contains secrets if env files are included, so keep it private and never commit backups to Git.

Contabo/VPS automatic backups or snapshots are recommended for production. Local app-level backups are helpful for restores, but long-term production setups should also keep an offsite copy using S3/R2/Backblaze/Google Drive sync or rsync to another VPS.

See Backups & Restore.

Production environment notes

TopicRecommendation
SecretsInject via env manager; rotate JWT secrets periodically
DatabaseDedicated PostgreSQL; regular backups
BackupsEnable VPS/provider backups, run the app-level backup script daily, and keep an offsite copy
UploadsPersistent volume for LOCAL_UPLOAD_DIR; include it in backup/restore testing
API docsAPI_DOCS_ENABLED=false if public explorer is undesired
Demo modeOff unless running marketplace demo
CORSFRONTEND_URL must match real app origin; add staging/preview hosts via CORS_ALLOWED_ORIGINS
AI providerDefault AI_PROVIDER=MOCK; add buyer-owned OPENAI_API_KEY or GEMINI_API_KEY only when enabling real AI
Billing providerDefault BILLING_PROVIDER=MOCK; add Stripe or Lemon Squeezy secrets to backend .env only when enabling live billing
Billing webhooksWhen using Stripe/Lemon Squeezy, expose https://api.yourdomain.com/billing/webhooks/stripe and /billing/webhooks/lemon-squeezy over HTTPS
Outbound webhooksUse HTTPS endpoint URLs; queues are optional, and direct delivery works when WEBHOOK_QUEUE_ENABLED=false
API keysSet API_KEY_HASH_SECRET to a strong random value in production; never expose API keys in frontend env
Redis / queuesOptional; QUEUES_ENABLED=false by default — see Background Queues

Docker deployment (optional)

VPS + systemd + Nginx remains the recommended production path. Docker is an optional alternative for buyers who prefer containers.

  1. Copy docker-compose.example.yml to docker-compose.yml
  2. Fill env from .env.example — never commit secrets
  3. Validate: docker compose -f docker-compose.example.yml config
  4. Run: docker compose up --build

See Docker for service details, build args, and production notes.

LaunchKit Pro by LaunchKit Labs — buyer documentation. No secrets in this site.