Sellf Deployment - Mikrus VPS (Optimized)
Last Updated: 2026-01-15 (Performance Optimization Release)
This guide covers deployment of Sellf on Mikrus.us VPS with optimized configuration for high performance on resource-constrained environments.
Quick path: If you just want Sellf live on Mikrus, the StackPilot one-liner handles everything in this guide automatically — same DNS + Caddy + PM2 setup, ~5 minutes instead of ~30. See the landing demo for the click-by-click walkthrough. This document remains as the manual path if you want full control over every step or need to debug a broken install.
Stripe webhook: Whichever path you pick, register the webhook from the Sellf admin (
Settings → Payments → Register webhook) — no Stripe Dashboard hopping, noSTRIPE_WEBHOOK_SECRETenv var to manage. The env var entry below is kept for the env-config / CI deploy path.
📊 Performance Expectations
Section titled “📊 Performance Expectations”After optimization (ISR + PM2 cluster + optional Redis):
| Mikrus Plan | CPU | RAM | Expected Performance | Recommended Instances |
|---|---|---|---|---|
| Mikrus 2.0 | 1 core | 512MB | 30-50 req/sec | 1 instance |
| Mikrus 2.0+ | 1 core | 1GB | 50-80 req/sec | 1-2 instances |
| Mikrus 3.0 | 2 cores | 2GB | 100-200 req/sec | 2 instances (cluster) |
| Mikrus 3.0+ | 4+ cores | 4GB+ | 200-400+ req/sec | 4 instances (cluster) |
Baseline (before optimization): ~11-12 req/sec on any VPS (CPU bottleneck)
After optimization: 10-30x improvement depending on hardware
🎯 Prerequisites
Section titled “🎯 Prerequisites”- Mikrus VPS with Ubuntu 22.04+ or Debian 12+
- Domain pointed to your VPS IP
- Supabase Project (hosted or self-hosted)
- Stripe Account (for payments)
- SSH Access to your VPS
📦 Part 1: System Setup (All Mikrus Plans)
Section titled “📦 Part 1: System Setup (All Mikrus Plans)”Step 1: Connect to VPS
Section titled “Step 1: Connect to VPS”ssh root@your-mikrus-vps.mikr.usStep 2: Create Non-Root User
Section titled “Step 2: Create Non-Root User”# Create deploy useradduser sellfusermod -aG sudo sellf
# Switch to deploy usersu - sellfStep 3: Install Node.js 20.x LTS
Section titled “Step 3: Install Node.js 20.x LTS”# Install Node.jscurl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -sudo apt-get install -y nodejs
# Verify installationnode --version # Should be v20.xnpm --versionStep 4: Install PM2 Globally
Section titled “Step 4: Install PM2 Globally”sudo npm install -g pm2@latest
# Verify installationpm2 --versionStep 5: Install Git
Section titled “Step 5: Install Git”sudo apt-get updatesudo apt-get install -y git🚀 Part 2: Deploy Sellf
Section titled “🚀 Part 2: Deploy Sellf”Step 1: Clone Repository
Section titled “Step 1: Clone Repository”cd ~git clone https://github.com/yourusername/sellf.gitcd sellfStep 2: Install Dependencies
Section titled “Step 2: Install Dependencies”cd admin-panelnpm installcd ..Step 3: Configure Environment Variables
Section titled “Step 3: Configure Environment Variables”Copy and edit environment file:
cp .env.fullstack.example .env.fullstacknano .env.fullstackMinimum Required Variables:
# DatabasePOSTGRES_PASSWORD=your_secure_password_here
# JWT & AuthJWT_SECRET=generate_with_openssl_rand_base64_32REALTIME_SECRET_KEY_BASE=generate_with_openssl_rand_base64_32ANON_KEY=your_supabase_anon_keySERVICE_ROLE_KEY=your_supabase_service_role_key
# URLsAPI_EXTERNAL_URL=https://api.yourdomain.comNEXT_PUBLIC_SUPABASE_URL=https://api.yourdomain.comGOTRUE_SITE_URL=https://yourdomain.comNEXT_PUBLIC_SITE_URL=https://yourdomain.com
# Stripe — pk/sk required, webhook secret optional (auto-managed via# the admin's Register-webhook button by default — see top of this doc)NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_xxxSTRIPE_SECRET_KEY=sk_live_xxx# STRIPE_WEBHOOK_SECRET=whsec_xxx # env-config path only
# Cloudflare TurnstileNEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY=your_site_keyCLOUDFLARE_TURNSTILE_SECRET_KEY=your_secret_keyOptional - Upstash Redis (Recommended for better performance):
# Upstash Redis (Optional - but recommended)# Free tier: 10k req/day, 256MB storage# Benefits: <10ms latency, 50-70% reduced DB loadUPSTASH_REDIS_REST_URL=https://your-region.upstash.ioUPSTASH_REDIS_REST_TOKEN=AX...your-token...==See UPSTASH-REDIS.md for setup guide.
Step 4: Build Application
Section titled “Step 4: Build Application”cd admin-panelnpm run buildcd ..Expected output: ✓ Compiled successfully
If you see ℹ️ Upstash Redis not configured - using database fallback (this is OK) - that’s fine! App works without Redis.
⚙️ Part 3: PM2 Configuration (Optimized for Mikrus)
Section titled “⚙️ Part 3: PM2 Configuration (Optimized for Mikrus)”The repository includes a pre-configured ecosystem.config.js optimized for different Mikrus plans.
For Mikrus 2.0 (512MB - 1 core)
Section titled “For Mikrus 2.0 (512MB - 1 core)”Edit ecosystem.config.js:
module.exports = { apps: [ { name: 'sellf-admin', cwd: './admin-panel', script: 'node_modules/next/dist/bin/next', args: 'start',
// SINGLE INSTANCE MODE (1 core VPS) instances: 1, exec_mode: 'fork', // Use 'fork' for single instance
// MEMORY MANAGEMENT (critical for 512MB) max_memory_restart: '400M', // Restart if exceeds 400MB min_uptime: '10s', max_restarts: 10,
// GRACEFUL SHUTDOWN kill_timeout: 5000, listen_timeout: 10000,
// LOGGING error_file: './logs/pm2-error.log', out_file: './logs/pm2-out.log', log_date_format: 'YYYY-MM-DD HH:mm:ss Z', merge_logs: true,
// ENV env: { NODE_ENV: 'production', PORT: 3000, }, }, ],}Expected Performance: 30-50 req/sec, ~500ms latency
For Mikrus 3.0 (2GB+ - 2+ cores) - RECOMMENDED
Section titled “For Mikrus 3.0 (2GB+ - 2+ cores) - RECOMMENDED”Edit ecosystem.config.js:
module.exports = { apps: [ { name: 'sellf-admin', cwd: './admin-panel', script: 'node_modules/next/dist/bin/next', args: 'start',
// CLUSTER MODE - Use all CPU cores instances: 'max', // Auto-detect cores (2-4 instances) exec_mode: 'cluster',
// MEMORY MANAGEMENT max_memory_restart: '512M', // Restart if exceeds 512MB per instance min_uptime: '10s', max_restarts: 10,
// GRACEFUL SHUTDOWN kill_timeout: 5000, listen_timeout: 10000, wait_ready: true,
// LOGGING error_file: './logs/pm2-error.log', out_file: './logs/pm2-out.log', log_date_format: 'YYYY-MM-DD HH:mm:ss Z', merge_logs: true,
// ENV env: { NODE_ENV: 'production', PORT: 3000, }, }, ],}Expected Performance: 100-200+ req/sec, <200ms latency
🔥 Part 4: Start Application
Section titled “🔥 Part 4: Start Application”Step 1: Create Logs Directory
Section titled “Step 1: Create Logs Directory”mkdir -p admin-panel/logsStep 2: Start PM2
Section titled “Step 2: Start PM2”pm2 start ecosystem.config.jsOutput (Mikrus 2.0 - single instance):
┌────┬────────────────┬─────────┬─────────┬──────┬──────────┐│ id │ name │ mode │ status │ cpu │ memory │├────┼────────────────┼─────────┼─────────┼──────┼──────────┤│ 0 │ sellf-admin │ fork │ online │ 0% │ 180.0mb │└────┴────────────────┴─────────┴─────────┴──────┴──────────┘Output (Mikrus 3.0 - cluster mode):
┌────┬────────────────┬─────────┬─────────┬──────┬──────────┐│ id │ name │ mode │ status │ cpu │ memory │├────┼────────────────┼─────────┼─────────┼──────┼──────────┤│ 0 │ sellf-admin │ cluster │ online │ 0% │ 208.0mb ││ 1 │ sellf-admin │ cluster │ online │ 0% │ 207.8mb │└────┴────────────────┴─────────┴─────────┴──────┴──────────┘Step 3: Verify Application
Section titled “Step 3: Verify Application”# Check PM2 statuspm2 status
# View logspm2 logs sellf-admin --lines 20
# Monitor in real-timepm2 monitStep 4: Test Homepage
Section titled “Step 4: Test Homepage”curl http://localhost:3000Should return HTML content (not error).
Step 5: Save PM2 Process List
Section titled “Step 5: Save PM2 Process List”pm2 saveStep 6: Setup Auto-Start on Reboot
Section titled “Step 6: Setup Auto-Start on Reboot”pm2 startup
# Follow the instructions (will output a command like):# sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u sellf --hp /home/sellf
# Run the generated command, then:pm2 save🌐 Part 5: Nginx Reverse Proxy (Optional but Recommended)
Section titled “🌐 Part 5: Nginx Reverse Proxy (Optional but Recommended)”Step 1: Install Nginx
Section titled “Step 1: Install Nginx”sudo apt-get install -y nginxStep 2: Configure Nginx
Section titled “Step 2: Configure Nginx”sudo nano /etc/nginx/sites-available/sellfAdd configuration:
# Rate limiting (adjust based on your Mikrus plan)limit_req_zone $binary_remote_addr zone=app:10m rate=10r/s;
server { listen 80; server_name yourdomain.com www.yourdomain.com;
# Rate limiting location / { limit_req zone=app burst=20 nodelay;
proxy_pass http://localhost:3000; 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;
# Timeouts proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; }
# Health check endpoint location /api/health { proxy_pass http://localhost:3000; access_log off; }}Step 3: Enable Site
Section titled “Step 3: Enable Site”sudo ln -s /etc/nginx/sites-available/sellf /etc/nginx/sites-enabled/sudo nginx -tsudo systemctl restart nginxStep 4: Setup SSL with Let’s Encrypt
Section titled “Step 4: Setup SSL with Let’s Encrypt”sudo apt-get install -y certbot python3-certbot-nginxsudo certbot --nginx -d yourdomain.com -d www.yourdomain.com🔄 Part 6: Deployment & Updates
Section titled “🔄 Part 6: Deployment & Updates”Zero-Downtime Deployment Script
Section titled “Zero-Downtime Deployment Script”Use the included scripts/deploy.sh:
#!/bin/bashset -e
echo "🚀 Deploying Sellf..."
# Pull latest changesgit pull origin main
# Install dependenciescd admin-panelnpm ci
# Buildnpm run build
# Reload PM2 (zero-downtime)cd ..pm2 reload ecosystem.config.js
# Verifypm2 status
echo "✅ Deployment complete!"Usage:
chmod +x scripts/deploy.sh./scripts/deploy.shFor Mikrus 2.0 (single instance), use pm2 restart instead of reload:
pm2 restart ecosystem.config.js📊 Part 7: Monitoring & Performance
Section titled “📊 Part 7: Monitoring & Performance”Check PM2 Status
Section titled “Check PM2 Status”# List processespm2 list
# Real-time monitoringpm2 monit
# View logspm2 logs sellf-admin
# CPU & Memory usagepm2 describe sellf-adminRun Benchmark (Optional)
Section titled “Run Benchmark (Optional)”From your local machine:
# Default: 50 concurrent connectionsnode scripts/benchmark.js https://yourdomain.com
# Small VPS (1 vCPU / ≤1GB RAM): use 5 connections for realistic resultsnode scripts/benchmark.js https://yourdomain.com 5Why fewer connections for small VPS? 50 concurrent connections will saturate a single-core server and produce misleadingly high latency numbers. 5 connections better reflects real-world traffic on a small instance and gives you actionable latency metrics.
Expected Results (50 connections):
| Mikrus Plan | Req/sec | Latency |
|---|---|---|
| 2.0 (512MB) | 30-50 | ~500ms |
| 2.0+ (1GB) | 50-80 | ~300ms |
| 3.0 (2GB) | 100-200 | ~200ms |
| 3.0+ (4GB) | 200-400+ | <200ms |
Expected Results (5 connections — small VPS):
| Mikrus Plan | Req/sec | Latency |
|---|---|---|
| 2.0 (512MB) | 20-40 | ~150ms |
| 2.0+ (1GB) | 30-50 | ~120ms |
| 3.0 (2GB) | 50-100 | ~80ms |
Setup Upstash Redis (Optional - Performance Boost)
Section titled “Setup Upstash Redis (Optional - Performance Boost)”See UPSTASH-REDIS.md for detailed setup guide.
Quick Summary:
- Create free Upstash account: https://console.upstash.com
- Create Redis database (choose region closest to your VPS)
- Add credentials to
.env.fullstack:Terminal window UPSTASH_REDIS_REST_URL=https://your-region.upstash.ioUPSTASH_REDIS_REST_TOKEN=AX...your-token...== - Restart PM2:
Terminal window pm2 reload all - Verify in logs:
Terminal window pm2 logs | grep Redis# Should see: "✅ Upstash Redis connected - caching enabled"
Performance Impact:
- Shop config queries: 50-100ms → 5-10ms (10x faster)
- Database load: -50-70%
- Cache hit rate: 80%+
🛠️ Troubleshooting
Section titled “🛠️ Troubleshooting”High Memory Usage (Mikrus 2.0)
Section titled “High Memory Usage (Mikrus 2.0)”Symptom: App keeps restarting due to memory limit
Solution 1: Lower memory limit in ecosystem.config.js:
max_memory_restart: '350M', // Lower limit for 512MB VPSSolution 2: Disable PM2 cluster mode (use single instance):
instances: 1,exec_mode: 'fork',Solution 3: Add swap space:
# Create 1GB swap filesudo fallocate -l 1G /swapfilesudo chmod 600 /swapfilesudo mkswap /swapfilesudo swapon /swapfile
# Make permanentecho '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstabHigh CPU Usage
Section titled “High CPU Usage”Symptom: PM2 shows 100% CPU usage
Check logs:
pm2 logs sellf-admin --lines 100Common causes:
- No ISR cache - Make sure
export const revalidate = 60is in public pages - Too many PM2 instances - Reduce to 1-2 instances on Mikrus 2.0
- Heavy database queries - Enable Upstash Redis caching
Quick fix (reduce instances):
pm2 scale sellf-admin 1Port 3000 Already in Use
Section titled “Port 3000 Already in Use”# Find process using port 3000sudo lsof -i :3000
# Kill processsudo kill -9 <PID>
# Or restart PM2pm2 restart sellf-adminApplication Not Starting
Section titled “Application Not Starting”Check logs:
pm2 logs sellf-admin --err --lines 50Common errors:
-
“Your project’s URL and Key are required”
- Missing Supabase credentials in
.env.fullstack
- Missing Supabase credentials in
-
“listen EADDRINUSE: address already in use”
- Port 3000 already taken (see above)
-
“JavaScript heap out of memory”
- Lower
max_memory_restartor add swap space
- Lower
📚 Additional Resources
Section titled “📚 Additional Resources”- Performance Optimization Details: BACKLOG.md - Search for “Performance & Scalability”
- Upstash Redis Setup: UPSTASH-REDIS.md
- PM2 Advanced: PM2-VPS.md
- Benchmark Script:
scripts/benchmark.js
✅ Production Checklist
Section titled “✅ Production Checklist”Before going live:
- PM2 started with optimized
ecosystem.config.js - PM2 auto-start enabled (
pm2 startup+pm2 save) - Nginx reverse proxy configured
- SSL certificate installed (Let’s Encrypt)
- Environment variables properly set
- Upstash Redis configured (optional but recommended)
- Firewall configured (UFW)
- Monitoring enabled (
pm2 monit) - Backup strategy in place
- Benchmark test passed (>30 req/sec)
🎯 Summary
Section titled “🎯 Summary”Mikrus 2.0 (512MB - 1 core):
- Use single instance (
instances: 1,exec_mode: 'fork') - Memory limit: 400MB
- Expected: 30-50 req/sec
- Consider adding swap space
Mikrus 3.0 (2GB+ - 2+ cores):
- Use cluster mode (
instances: 'max',exec_mode: 'cluster') - Memory limit: 512MB per instance
- Expected: 100-200+ req/sec
- Highly recommended with Upstash Redis
Both plans:
- ISR enabled by default (60s cache)
- React cache() deduplication
- Optional Redis for extra performance
- Zero-downtime deployments with
pm2 reload
Last Updated: 2026-01-15 (Performance Optimization Release)