Troubleshooting
Common issues and fixes for SaaS Starter setup and deployment.
Troubleshooting
Quick fixes for the most common SaaS Starter issues.
First step: Run pnpm doctor from the monorepo root. It checks Node version, pnpm, PostgreSQL, Redis, and environment variables.
Database connection failed
Symptom: ECONNREFUSED on port 5432 or role "saas_starter" does not exist.
Fix:
- Ensure PostgreSQL is running:
# macOS brew services start postgresql@16 # Linux sudo systemctl start postgresql - Create the database and user:
psql -U postgres -c "CREATE DATABASE saas_starter;" psql -U postgres -c "CREATE USER saas_starter WITH PASSWORD 'saas_starter_local';" psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE saas_starter TO saas_starter;" - Verify
DATABASE_URLin your.envmatches the connection string. - Test the connection:
PGPASSWORD=saas_starter_local psql -U saas_starter -h localhost -d saas_starter -c "SELECT 1"
Redis connection failed
Symptom: Worker jobs not running, ECONNREFUSED on port 6379.
Fix:
- Ensure Redis is running:
# macOS brew services start redis # Linux sudo systemctl start redis-server - Test the connection:
redis-cli ping # Expected: PONG - Check
REDIS_URLandREDIS_QUEUE_URLin your.env.
Build fails with Out of Memory
Symptom: FATAL ERROR: ... heap out of memory during pnpm install or Docker build step pnpm generate:active-modules.
Cause (Docker): On hosts under ~6GB RAM, BuildKit runs Node with a small heap. Invoking pnpm generate:active-modules triggers a full dependency sync and can OOM. SaaS Starter setup auto-enables a low-memory path that builds Web and API on the host volume instead.
Secrets in setup output: The configuration summary never prints API keys or passwords. If you see a key appended to a line like AI: groq (key set)gsk_..., upgrade to the latest setup scripts (a bash ${VAR:-} bug caused that leak in older versions). Pipe setup to a log file only when needed — one-time admin passwords are shown on the interactive terminal (stderr), not in stdout logs.
Fix:
- Re-run setup with low-memory profile (recommended on 2–4GB VPS):
Or opt in to script-managed swap:bash setup.sh --low-memoryNOVASAAS_CREATE_SWAP=1 bash setup.sh - If Web built but API/worker failed, recover without full setup:
bash scripts/recover-docker-api-low-mem.sh - For PM2 / host builds, use the low-memory web path:
pnpm run build:web:low-mem - Manual swap (4GB recommended on 4GB VPS):
sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile
CSS/JS missing after PM2 restart
Symptom: App loads but looks broken, no styles or interactive elements.
Fix:
This is a known Next.js standalone issue — static assets aren't copied automatically.
# Use the PM2 web restart script — it copies static assets automatically
bash scripts/pm2-start-web.sh
# Or manually (if you need to restart without a full rebuild):
cp -rf apps/web/.next/static apps/web/.next/standalone/.next/static
cp -rf apps/web/public apps/web/.next/standalone/public
pm2 restart app-web
See PM2 Static Assets for the full explanation.
Migrations fail on first run
Symptom: relation "users" does not exist or similar schema errors.
Fix:
- Ensure database exists and is empty:
psql -U postgres -c "DROP DATABASE IF EXISTS saas_starter;" psql -U postgres -c "CREATE DATABASE saas_starter;" - Run migrations:
pnpm migrate - If using Docker:
docker compose exec api pnpm migrate
2FA not working
Symptom: Can't scan QR code or TOTP codes are rejected.
Fix:
- Verify
APP_URLmatches your browser URL (including protocol and port). - Ensure
BETTER_AUTH_URLis set correctly in your.env. - Clear browser cookies and try again.
- Check system clock — TOTP is time-sensitive.
Magic link email not received
Symptom: Email doesn't arrive, or link says "invalid token".
Fix:
- Check
EMAIL_FROMis a valid email address. - Verify email provider credentials (SMTP, Resend, SendGrid).
- Check spam/junk folder.
- For development, use
mailto:link output in console by settingEMAIL_PROVIDER=log(if available) or check server logs for email content. - Verify
APP_URLmatches the link in the email.
Webhook verification failed
Symptom: Stripe/Paddle/Lemon Squeezy webhooks rejected with "signature mismatch".
Fix:
- Ensure
STRIPE_WEBHOOK_SECRET(or equivalent) is set correctly in.env. - For local testing, use Stripe CLI:
stripe listen --forward-to localhost:8000/webhooks/stripe - Verify the webhook secret matches exactly (no extra whitespace or quotes).
Admin dashboard shows no data
Symptom: Charts empty, user list shows "No data yet".
Fix:
- Verify admin user exists:
# In monorepo root pnpm exec tsx scripts/seed-initial-config.ts - Or create admin via environment variables:
ADMIN_EMAIL=your@email.com ADMIN_PASSWORD=yourpassword - Restart the API server after setting admin variables.
Next.js App Router 404 on refresh
Symptom: Direct navigation to /dashboard or /admin/* returns 404.
Fix:
This usually means the Next.js standalone server is routing incorrectly.
- Verify
output: 'standalone'is set inapps/web/next.config.ts. - Ensure PM2 is running the correct entry point:
pm2 logs app-web --lines 20 - Check that reverse proxy (Caddy, Nginx) is configured to pass all routes to Next.js.
See Production Deployment for proxy configuration.
Health check failing
Symptom: curl http://localhost:8000/health returns non-200.
Fix:
# Check API logs
pm2 logs app-api --lines 50
# Common causes:
# - Missing AUTH_SECRET or ENCRYPTION_KEY
# - DATABASE_URL not set or invalid
# - PostgreSQL/Redis not running
Getting more help
If pnpm doctor passes and you're still stuck:
- Run
pnpm doctor:opsfor infrastructure diagnostics. - Check the environment matrix for correct variable names.
- Email support@example.com with:
- Node and pnpm versions (
node -v,pnpm -v) - OS and whether using Docker or native
- Last 30 lines of the failing command
- Output of
curl http://localhost:8000/health - Output of
pnpm doctor
- Node and pnpm versions (