Deployment
Pre-Deployment Checklist
Section titled “Pre-Deployment Checklist”Before deploying to production, verify these items:
- All secrets generated and set in
.env(ENCRYPTION_KEY,JWT_SECRET,COOKIE_SECRET_*, etc.) -
DEPLOYMENT_ENVIRONMENT=production -
DEPLOYMENT_URLset to your public HTTPS URL - Database configured and accessible (MongoDB or PostgreSQL for production)
- Redis configured (if using Redis for OIDC storage or sessions)
- JWKS keys generated (
yarn keys generate) - SMTP configured for email delivery
- Cookie
secureset totrue(requires HTTPS) -
USE_FILE_CONFIG=false(use database config in production) - Nginx or reverse proxy configured with SSL
Database Setup
Section titled “Database Setup”MongoDB
Section titled “MongoDB”# Install MongoDB (Ubuntu/Debian)sudo apt install -y mongodb-orgsudo systemctl enable mongodsudo systemctl start mongod
# Verify connectionmongosh "mongodb://localhost:27017/parako"Set in .env:
STORAGE_ADAPTER=mongodbSTORAGE_MONGODB_URI=mongodb://localhost:27017/parakoPostgreSQL
Section titled “PostgreSQL”# Install PostgreSQL (Ubuntu/Debian)sudo apt install -y postgresql postgresql-contribsudo systemctl enable postgresql
# Create database and usersudo -u postgres psql -c "CREATE USER parako WITH PASSWORD 'your_password';"sudo -u postgres psql -c "CREATE DATABASE parako OWNER parako;"Set in .env:
STORAGE_ADAPTER=postgresqlSTORAGE_POSTGRESQL_URL=postgresql://parako:your_password@localhost:5432/parakoRun migrations:
yarn db:generate:pgyarn db:migrate:deploy# Install Redis (Ubuntu/Debian)sudo apt install -y redis-serversudo systemctl enable redis-server
# Set password (recommended)sudo sed -i 's/# requirepass foobared/requirepass your_redis_password/' /etc/redis/redis.confsudo systemctl restart redis-serverSet in .env:
REDIS_HOST=localhostREDIS_PORT=6379REDIS_PASSWORD=your_redis_passwordPM2 Deployment
Section titled “PM2 Deployment”PM2 is the default process manager. Parako.ID includes a pre-configured ecosystem.config.cjs.
Build and Start
Section titled “Build and Start”# Build for productionyarn build
# Start with PM2yarn startThis starts two processes:
- parako-id — Main application (cluster mode, multiple instances)
- parako-id-worker — Background worker (fork mode, single instance)
PM2 Configuration Options
Section titled “PM2 Configuration Options”Customize PM2 behavior via environment variables:
| Variable | Default | Description |
|---|---|---|
APP_NAME | parako-id | PM2 process name |
PORT | 9007 | Server port |
PM2_INSTANCES | max | Number of instances (max = all CPUs) |
PM2_MAX_MEMORY | 1G | Max memory before restart (app) |
PM2_WORKER_MAX_MEMORY | 300M | Max memory before restart (worker) |
PM2_UID | — | Run as specific user (optional) |
PM2_GID | — | Run as specific group (optional) |
SQLite constraint: When using SQLite, you must set PM2_INSTANCES=1. SQLite does not support concurrent writes from multiple processes.
PM2 Commands
Section titled “PM2 Commands”yarn start # Start all processesyarn restart # Restart all processespm2 stop ecosystem.config.cjs # Stop all processespm2 logs # View all logspm2 logs parako-id # Application logs onlypm2 logs parako-id-worker # Worker logs onlypm2 monit # PM2 monitoring dashboardGraceful Restart
Section titled “Graceful Restart”PM2 is configured for zero-downtime restarts:
wait_ready: true— Waits for the process to signal readiness before routing trafficlisten_timeout: 30000— 30 seconds to become readykill_timeout: 10000— 10 seconds to gracefully shut downshutdown_with_message: true— Sends shutdown message to the process
Systemd Deployment
Section titled “Systemd Deployment”Use systemd as an alternative to PM2 for tighter OS integration.
Install Services
Section titled “Install Services”# Preview generated unit filesyarn systemd generate
# Install (requires sudo)sudo yarn systemd install
# Start servicessudo systemctl start parako-id
# Enable on bootsudo systemctl enable parako-idManage Services
Section titled “Manage Services”# Statusyarn systemd status
# Logs (tail both services live; Ctrl-C to stop)yarn systemd logs
# Worker only / time-windowedyarn systemd logs --workeryarn systemd logs --since "1 hour ago"
# Restart both services (main + worker)sudo yarn systemd restart
# Uninstallsudo yarn systemd uninstallCustomizing Resource Limits
Section titled “Customizing Resource Limits”Override the default memory caps when generating or installing:
sudo yarn systemd install \ --memory-app 2G \ --memory-worker 512MDefaults are 1G for the main app and 300M for the worker.
Safe Re-installs
Section titled “Safe Re-installs”yarn systemd install validates that the service user, working directory, and environment file are present before writing unit files. If existing unit files differ from what would be written, it shows a diff and refuses to overwrite — pass --force to apply. Identical content is a safe no-op (no daemon-reload).
Security Hardening
Section titled “Security Hardening”Generated systemd units include:
NoNewPrivileges=yesProtectSystem=strictPrivateTmp=yes- Resource limits matching PM2 configuration
- Worker bound to main service via
BindsTo
See CLI Tools for all systemd command options.
Nginx Reverse Proxy
Section titled “Nginx Reverse Proxy”Place nginx in front of Parako.ID for SSL termination and static asset caching.
Basic Configuration
Section titled “Basic Configuration”upstream parako { server 127.0.0.1:9007; keepalive 64;}
server { listen 80; server_name auth.example.com; return 301 https://$host$request_uri;}
server { listen 443 ssl http2; server_name auth.example.com;
ssl_certificate /etc/letsencrypt/live/auth.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/auth.example.com/privkey.pem;
# Security headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Proxy settings location / { proxy_pass http://parako; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; proxy_read_timeout 90s; }
# Static assets location /css/ { proxy_pass http://parako; expires 1y; add_header Cache-Control "public, immutable"; }
location /js/ { proxy_pass http://parako; expires 1y; add_header Cache-Control "public, immutable"; }}Set deployment.server.proxy: true in your Parako.ID configuration to trust proxy headers.
SSL/TLS
Section titled “SSL/TLS”Let’s Encrypt with Certbot
Section titled “Let’s Encrypt with Certbot”# Install Certbotsudo apt install -y certbot python3-certbot-nginx
# Issue certificatesudo certbot --nginx -d auth.example.com
# Auto-renewal (Certbot adds a cron job automatically)sudo certbot renew --dry-runSecure Cookie Configuration
Section titled “Secure Cookie Configuration”Once HTTPS is configured, enable secure cookies:
{ "deployment": { "cookies": { "defaults": { "secure": true, "sameSite": "lax", }, }, },}Multi-Tenancy Infrastructure
Section titled “Multi-Tenancy Infrastructure”When running Parako.ID in multi-tenant mode, you need wildcard DNS, a wildcard SSL certificate, and an nginx configuration that routes all tenant subdomains to the same upstream.
Domain Architecture
Section titled “Domain Architecture”Multi-tenancy uses a three-tier subdomain model under your base domain:
| Subdomain | Tenant | Purpose |
|---|---|---|
_ops.auth.example.com | _ops | Health probes, metrics, social OAuth callback relay |
_platforms.auth.example.com | _platforms | Master tenant — login, admin panel, cross-tenant management |
*.auth.example.com | Per-tenant | Tenant-specific OIDC endpoints (e.g., acme.auth.example.com) |
Parako.ID resolves the tenant internally by extracting the subdomain from the Host header. Nginx does not need per-tenant server blocks — a single block handles all traffic.
DNS Setup
Section titled “DNS Setup”Create three DNS records pointing to your VPS:
_ops.auth.example.com A <VPS_IP>_platforms.auth.example.com A <VPS_IP>*.auth.example.com A <VPS_IP>The wildcard record (*) covers all tenant subdomains. The explicit _ops and _platforms records are required because most DNS providers do not match wildcard records against explicitly defined subdomains.
SSL Certificates
Section titled “SSL Certificates”A single wildcard certificate covers all three tiers:
# Using DNS challenge (required for wildcard certs)sudo certbot certonly --dns-<plugin> \ -d "auth.example.com" \ -d "*.auth.example.com"Replace <plugin> with your DNS provider’s Certbot plugin (e.g., cloudflare, route53, digitalocean). The HTTP challenge cannot issue wildcard certificates.
Nginx Configuration
Section titled “Nginx Configuration”A single server block handles all tenant subdomains, _ops, and _platforms:
upstream parako { server 127.0.0.1:9007; keepalive 64;}
server { listen 80; server_name *.auth.example.com _ops.auth.example.com _platforms.auth.example.com; return 301 https://$host$request_uri;}
server { listen 443 ssl http2; server_name *.auth.example.com _ops.auth.example.com _platforms.auth.example.com;
ssl_certificate /etc/letsencrypt/live/auth.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/auth.example.com/privkey.pem;
# Security headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Proxy settings location / { proxy_pass http://parako; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; proxy_read_timeout 90s; }
# Static assets location /css/ { proxy_pass http://parako; expires 1y; add_header Cache-Control "public, immutable"; }
location /js/ { proxy_pass http://parako; expires 1y; add_header Cache-Control "public, immutable"; }}This replaces the single-tenant nginx block from the Nginx Reverse Proxy section. The key differences are the wildcard server_name and the wildcard SSL certificate paths.
Set deployment.server.proxy: true in your Parako.ID configuration to trust proxy headers.
For application-level multi-tenancy configuration (extraction strategies, provider pool, per-tenant overrides), see Multi-Tenancy.
Bootstrap Admin
Section titled “Bootstrap Admin”On first startup with multi-tenancy enabled, create the initial platform admin using shell-scoped exports (never in .env for production):
export PARAKO_BOOTSTRAP_ADMIN_EMAIL=admin@example.comexport PARAKO_BOOTSTRAP_ADMIN_PASSWORD=your-secure-passwordyarn startAfter logging in at _platforms.<domain>, create a permanent admin account, then close the shell session. See Multi-Tenancy — Special Tenants for details.
Environment-Specific Settings
Section titled “Environment-Specific Settings”Production Checklist
Section titled “Production Checklist”# .env (production)DEPLOYMENT_ENVIRONMENT=productionDEPLOYMENT_URL=https://auth.example.comDEPLOYMENT_SERVER_PORT=9007
STORAGE_ADAPTER=postgresqlSTORAGE_POSTGRESQL_URL=postgresql://parako:password@localhost:5432/parako
OIDC_STORAGE_ADAPTER=redisREDIS_HOST=localhostREDIS_PORT=6379REDIS_PASSWORD=your_redis_password
ENCRYPTION_KEY=<64-char-hex>JWT_SECRET=<64-char-hex>COOKIE_SECRET_1=<64-char-hex>COOKIE_SECRET_2=<64-char-hex>HMAC_SECRET=<64-char-hex>PAIRWISE_SALT=<32-char-hex>
USE_FILE_CONFIG=false
SECURITY_LOGGING_LEVEL=infoSECURITY_LOGGING_PRETTY_PRINT=falseSECURITY_LOGGING_FILE_LOGGING_ENABLED=true