Skip to content

Documentation

Production Deployment & Optimization

Complete guide for deploying SveltyCMS in production with Nginx, Redis, and enterprise-grade optimization strategies.

6/14/2026
16 min read Edit on GitHub

This comprehensive guide covers deploying SveltyCMS in production with Nginx reverse proxy, optional Redis caching, and enterprise-grade optimizations for maximum performance and security.

Tip

New: See Enterprise Scaling Layers for the complete composable scaling story — PgBouncer connection pooling, optional Redis, pre-compressed cache HITs (Brotli/zstd), CDN purge, and deployment diagrams. Start simple (single-node SQLite) and add layers as you grow.


Architecture Overview

The recommended production architecture is composable — start minimal and add layers:

  • Nginx / Caddy — Reverse proxy, TLS termination, static asset serving, optional rate limiting
  • SveltyCMS — Bun runtime (3-4× faster than Node.js), Turbo pipeline, LocalCMS zero-tax SDK
  • Redis (optional) — Distributed L2 cache, cross-node invalidation, session sharing
  • PgBouncer (optional) — Connection pooling for Postgres at scale (see scaling-layers.mdx)
  • Database — SQLite (single-node), PostgreSQL, MariaDB, or MongoDB with connection pooling
Internet → Nginx (443) → PM2 Cluster → SveltyCMS → Redis → Database

           Static Assets

Prerequisites

Before starting, ensure you have:

  • Linux server (Ubuntu 20.04+ or similar)
  • Root or sudo access
  • Domain name with DNS configured
  • SSL certificate (Let’s Encrypt recommended)
  • Node.js 24+ installed
  • Redis 6+ installed
  • Nginx installed
  • ImageMagick & Ghostscript (Optional, for PDF thumbnails)

1. Nginx Configuration

Static Asset Optimization

Configure Nginx to serve static assets directly, bypassing Node.js entirely for maximum performance.

File: /etc/nginx/sites-available/sveltycms.conf

# Upstream Node.js application
upstream sveltycms_backend {
    server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
    keepalive 32;
}

# Rate limiting zones
limit_req_zone $binary_remote_addr zone=general:10m rate=100r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=50r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name your-domain.com;

    # SSL Configuration
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # Security Headers (defense in depth)
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    # Root directory
    root /var/www/sveltycms/build/client;

    # Logging
    access_log /var/log/nginx/sveltycms.access.log;
    error_log /var/log/nginx/sveltycms.error.log warn;

# ===== STATIC ASSETS (Nginx serves directly) =====

    # 🚀 IMPORTANT: SveltyCMS handles compression internally for dynamic paths.
    # Turbo GET + API cache HITs already ship pre-compressed brotli/gzip/zstd
    # with Content-Encoding set. Do NOT enable gzip/brotli on dynamic locations
    # (proxy_pass) — this would double-compress and waste CPU.
    # Static assets below can use Nginx's gzip_static/brotli_static safely.

    # SvelteKit immutable assets
    location ~ ^/_app/immutable/ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        add_header X-Served-By "nginx-static";
        access_log off;
        try_files $uri =404;
        gzip_static on;
        brotli_static on;
    }

    # SvelteKit version files
    location ~ ^/_app/version\.json$ {
        expires 1h;
        add_header Cache-Control "public, max-age=3600";
        try_files $uri =404;
    }

    # Static folder
    location ~ ^/static/ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        add_header X-Served-By "nginx-static";
        access_log off;
        try_files $uri =404;
        gzip_static on;
        brotli_static on;
    }

    # Common static files
    location ~ ^/(favicon\.ico|robots\.txt|sitemap\.xml|manifest\.webmanifest)$ {
        expires 1y;
        add_header Cache-Control "public, max-age=31536000";
        access_log off;
        try_files $uri =404;
    }

    # Media assets by extension
    location ~ \.(svg|png|jpg|jpeg|gif|webp|avif|ico)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
        try_files $uri =404;
    }

    # Fonts
    location ~ \.(woff|woff2|ttf|eot|otf)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        add_header Access-Control-Allow-Origin "*";
        access_log off;
        try_files $uri =404;
    }

    # JavaScript and CSS
    location ~ \.(js|css|map)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
        try_files $uri =404;
        gzip_static on;
        brotli_static on;
    }

    # ===== SECURITY BLOCKS (Early rejection) =====

    # Block hidden files
    location ~ /\. {
        deny all;
        return 404;
    }

    # Block sensitive file extensions
    location ~ \.(env|git|sql|bak|config|ini|log)$ {
        deny all;
        return 404;
    }

    # Block SQL injection patterns
    location ~ (union.*select|concat.*\(|benchmark\(|sleep\(|load_file) {
        deny all;
        return 403;
    }

    # Block path traversal
    location ~ (\.\./|\.\.\\|%2e%2e|%252e%252e) {
        deny all;
        return 403;
    }

    # ===== API ENDPOINTS (Rate limited, proxied to Node.js) =====

    # Login endpoint (strict rate limiting)
    location = /api/user/login {
        limit_req zone=login burst=3 nodelay;
        limit_req_status 429;

        proxy_pass http://sveltycms_backend;
        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 10s;
        proxy_send_timeout 30s;
        proxy_read_timeout 30s;
    }

    # API endpoints (moderate rate limiting)
    location ~ ^/api/ {
        limit_req zone=api burst=20 nodelay;
        limit_req_status 429;

        proxy_pass http://sveltycms_backend;
        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 for API
        proxy_connect_timeout 10s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        # Buffer settings
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
    }

    # ===== ALL OTHER REQUESTS (Proxied to Node.js) =====

    location / {
        limit_req zone=general burst=50 nodelay;

        # Try static files first, then proxy
        try_files $uri @proxy;
    }

    location @proxy {
        proxy_pass http://sveltycms_backend;
        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;

        # Buffer settings
        proxy_buffering on;
        proxy_buffer_size 8k;
        proxy_buffers 16 8k;
        proxy_busy_buffers_size 16k;
    }
}

# HTTP to HTTPS redirect
server {
    listen 80;
    listen [::]:80;
    server_name your-domain.com;
    return 301 https://$server_name$request_uri;
}

Enable Configuration

# Create symbolic link
sudo ln -s /etc/nginx/sites-available/sveltycms.conf /etc/nginx/sites-enabled/

# Test configuration
sudo nginx -t

# Reload Nginx
sudo systemctl reload nginx

Performance Impact

Metric Before Nginx Optimization After Improvement
Static asset latency 15-20ms <1ms 95% faster
Static asset CPU Node.js process None 100% CPU reduction
Malicious requests Reach Node.js Blocked at Nginx 80-90% blocked early

2. Redis Configuration

Redis Setup

File: /etc/redis/redis.conf

# Network
bind 127.0.0.1
port 6379
protected-mode yes
tcp-backlog 511

# Security
requirepass your_strong_redis_password_here

# Memory Management
maxmemory 512mb
maxmemory-policy allkeys-lru  # Evict least recently used keys

# Persistence
save 900 1      # Save if 1 key changed in 15 minutes
save 300 10     # Save if 10 keys changed in 5 minutes
save 60 10000   # Save if 10000 keys changed in 1 minute

# Append-only file (AOF) for durability
appendonly yes
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# Performance
timeout 300
tcp-keepalive 60
databases 16

# Logging
loglevel notice
logfile /var/log/redis/redis-server.log

# Snapshotting
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /var/lib/redis

Restart Redis

sudo systemctl restart redis-server
sudo systemctl enable redis-server

Update Environment Variables

Add to your .env file:

REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=your_strong_redis_password_here

3. Application Configuration

PM2 Process Manager

PM2 provides cluster mode, auto-restart, and zero-downtime deployments.

File: ecosystem.config.js (in project root)

module.exports = {
  apps: [
    {
      name: "sveltycms",
      script: "build/index.js",

      // Cluster mode (uses all CPU cores)
      instances: "max",
      exec_mode: "cluster",

      // Environment variables
      env: {
        NODE_ENV: "production",
        PORT: 3000,
        HOST: "127.0.0.1",
      },

      // Memory management
      max_memory_restart: "500M",

      // Logging
      error_file: "logs/error.log",
      out_file: "logs/out.log",
      merge_logs: true,
      log_date_format: "YYYY-MM-DD HH:mm:ss Z",

      // Restart behavior
      autorestart: true,
      max_restarts: 10,
      min_uptime: "10s",
      restart_delay: 4000,

      // Graceful shutdown
      kill_timeout: 5000,
      listen_timeout: 3000,
      shutdown_with_message: true,

      // Watch options (disabled in production)
      watch: false,
      ignore_watch: ["node_modules", "logs", ".git"],

      // Environment-specific overrides
      env_development: {
        NODE_ENV: "development",
        watch: true,
      },
    },
  ],

  deploy: {
    production: {
      user: "deploy",
      host: "your-server.com",
      ref: "origin/main",
      repo: "git@github.com:yourorg/sveltycms.git",
      path: "/var/www/sveltycms",
      "post-deploy":
        "bun install && bun run build && pm2 reload ecosystem.config.js --env production",
    },
  },
};

Install and Start PM2

# Install PM2 globally
npm install -g pm2

# Start application
pm2 start ecosystem.config.js --env production

# Save PM2 process list
pm2 save

# Enable auto-start on system boot
pm2 startup

# Monitor processes
pm2 monit

PM2 Commands

# View logs
pm2 logs sveltycms

# Restart application
pm2 restart sveltycms

# Reload (zero-downtime)
pm2 reload sveltycms

# Stop application
pm2 stop sveltycms

# View process info
pm2 info sveltycms

# View process list
pm2 list

4. Middleware Optimization

Remove Redundant Static Middleware

Since Nginx now handles static assets, you can optionally remove handleStaticAssetCaching from your middleware sequence.

File: src/hooks.server.ts

// Before (with static middleware)
export const handle: Handle = sequence(
  handleStaticAssetCaching, // ← Remove this (Nginx handles it)
  handleSystemState,
  handleRateLimit,
  handleFirewall,
  // ... rest of middleware
);

// After (optimized for Nginx)
export const handle: Handle = sequence(
  handleSystemState,
  handleRateLimit,
  handleFirewall,
  handleSetup,
  handleLocale,
  handleTheme,
  handleAuthentication,
  handleAuthorization,
  addSecurityHeaders,
);

Benefits:

  • Faster application startup
  • Lower memory usage
  • Reduced CPU overhead
  • Simplified middleware stack

Session Cache TTL Optimization

For high-traffic production, increase session cache TTL to reduce database queries.

File: src/hooks/handleAuthentication.ts

// Current (5 minutes)
const SESSION_CACHE_TTL_MS = 5 * 60 * 1000;

// Recommended for production (15 minutes)
const SESSION_CACHE_TTL_MS = 15 * 60 * 1000;

Impact: Reduces Redis/database queries by 66% while maintaining security through automatic session rotation.


5. Database Optimization

Connection Pooling

MongoDB {#pooling-mongodb}

import { MongoClient } from "mongodb";

const client = new MongoClient(process.env.MONGODB_URI, {
  maxPoolSize: 50, // Maximum connections
  minPoolSize: 10, // Minimum connections
  maxIdleTimeMS: 60000, // Close idle connections after 60s
  serverSelectionTimeoutMS: 5000,
  socketTimeoutMS: 45000,
  retryWrites: true,
  w: "majority",
});

await client.connect();

PostgreSQL {#pooling-postgresql}

import { Pool } from "pg";

const pool = new Pool({
  max: 50, // Maximum connections
  min: 10, // Minimum connections
  idleTimeoutMillis: 60000, // Close idle connections
  connectionTimeoutMillis: 5000,
  allowExitOnIdle: false,
});

Database Indexes

Create indexes for frequently queried fields to improve performance.

MongoDB {#mongodb-2}

// User collection
db.users.createIndex({ email: 1 }, { unique: true });
db.users.createIndex({ tenantId: 1, role: 1 });
db.users.createIndex({ createdAt: -1 });

// Session collection
db.sessions.createIndex({ sessionId: 1 }, { unique: true });
db.sessions.createIndex({ userId: 1 });
db.sessions.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 }); // TTL index

// Collections
db.collections.createIndex({ tenantId: 1, name: 1 });
db.collections.createIndex({ status: 1 });

// Media
db.media.createIndex({ tenantId: 1, type: 1 });
db.media.createIndex({ uploadedAt: -1 });

PostgreSQL {#postgresql-2}

-- Users table
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_tenant_role ON users(tenant_id, role);
CREATE INDEX idx_users_created ON users(created_at DESC);

-- Sessions table
CREATE UNIQUE INDEX idx_sessions_id ON sessions(session_id);
CREATE INDEX idx_sessions_user ON sessions(user_id);
CREATE INDEX idx_sessions_expires ON sessions(expires_at);

-- Collections table
CREATE INDEX idx_collections_tenant_name ON collections(tenant_id, name);
CREATE INDEX idx_collections_status ON collections(status);

-- Media table
CREATE INDEX idx_media_tenant_type ON media(tenant_id, type);
CREATE INDEX idx_media_uploaded ON media(uploaded_at DESC);

6. Monitoring & Observability

Health Check Endpoint

File: src/routes/api/health/+server.ts

import { json } from "@sveltejs/kit";
import type { RequestHandler } from "./$types";
import { getSystemState } from "@stores/system";

export const GET: RequestHandler = async () => {
  const systemState = getSystemState();
  const isHealthy = systemState.overallState === "READY";

  const health = {
    status: isHealthy ? "ok" : "degraded",
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    memory: {
      used: Math.round(process.memoryUsage().heapUsed / 1024 / 1024),
      total: Math.round(process.memoryUsage().heapTotal / 1024 / 1024),
      external: Math.round(process.memoryUsage().external / 1024 / 1024),
    },
    system: {
      state: systemState.overallState,
      setup: systemState.setupComplete,
      database: systemState.databaseConnected,
    },
  };

  return json(health, {
    status: isHealthy ? 200 : 503,
    headers: {
      "Cache-Control": "no-cache, no-store, must-revalidate",
    },
  });
};

Prometheus Metrics Endpoint

File: src/routes/api/metrics/+server.ts

import { metricsService } from "@src/services/metrics-service";
import type { RequestHandler } from "./$types";

export const GET: RequestHandler = async () => {
  const metrics = metricsService.getReport();

  // Convert to Prometheus format
  const prometheus = [
    `# HELP sveltycms_requests_total Total HTTP requests`,
    `# TYPE sveltycms_requests_total counter`,
    `sveltycms_requests_total ${metrics.requests.total}`,
    ``,
    `# HELP sveltycms_errors_total Total errors`,
    `# TYPE sveltycms_errors_total counter`,
    `sveltycms_errors_total ${metrics.requests.errors}`,
    ``,
    `# HELP sveltycms_cache_hits_total Cache hit count`,
    `# TYPE sveltycms_cache_hits_total counter`,
    `sveltycms_cache_hits_total ${metrics.cache.hits}`,
    ``,
    `# HELP sveltycms_cache_misses_total Cache miss count`,
    `# TYPE sveltycms_cache_misses_total counter`,
    `sveltycms_cache_misses_total ${metrics.cache.misses}`,
    ``,
    `# HELP sveltycms_security_violations_total Security violation count by type`,
    `# TYPE sveltycms_security_violations_total counter`,
    `sveltycms_security_violations_total{type="rate_limit"} ${metrics.security.rateLimitViolations}`,
    `sveltycms_security_violations_total{type="firewall"} ${metrics.security.firewallBlocks}`,
    `sveltycms_security_violations_total{type="auth_failure"} ${metrics.security.authFailures}`,
  ].join("\n");

  return new Response(prometheus, {
    headers: {
      "Content-Type": "text/plain; version=0.0.4",
      "Cache-Control": "no-cache",
    },
  });
};

Nginx Monitoring

# Add to server block
location /nginx_status {
    stub_status on;
    access_log off;
    allow 127.0.0.1;
    deny all;
}

Log Analysis

# Real-time request monitoring
tail -f /var/log/nginx/sveltycms.access.log

# Top requesting IPs
awk '{print $1}' /var/log/nginx/sveltycms.access.log | sort | uniq -c | sort -rn | head -20

# Response time analysis
awk '{print $NF}' /var/log/nginx/sveltycms.access.log | sort -n | tail -100

# 4xx/5xx errors
grep -E ' (4[0-9]{2}|5[0-9]{2}) ' /var/log/nginx/sveltycms.access.log | tail -50

7. Security Hardening

SSL/TLS Best Practices

# Generate strong DH parameters
sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048

# Add to Nginx SSL configuration
ssl_dhparam /etc/nginx/dhparam.pem;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

Firewall Configuration (UFW)

# Enable UFW
sudo ufw enable

# Allow SSH
sudo ufw allow 22/tcp

# Allow HTTP/HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Check status
sudo ufw status verbose

Fail2Ban for Brute Force Protection

# Install Fail2Ban
sudo apt install fail2ban

# Create Nginx jail
sudo nano /etc/fail2ban/jail.local

File: /etc/fail2ban/jail.local

[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/sveltycms.error.log

[nginx-noscript]
enabled = true
port = http,https
logpath = /var/log/nginx/sveltycms.access.log

[nginx-badbots]
enabled = true
port = http,https
logpath = /var/log/nginx/sveltycms.access.log
maxretry = 2

[nginx-noproxy]
enabled = true
port = http,https
logpath = /var/log/nginx/sveltycms.access.log
maxretry = 2
# Restart Fail2Ban
sudo systemctl restart fail2ban

# Check status
sudo fail2ban-client status

8. Backup Strategy

Automated Backups

File: /usr/local/bin/backup-sveltycms.sh

#!/bin/bash

# Configuration
BACKUP_DIR="/var/backups/sveltycms"
RETENTION_DAYS=30
DATE=$(date +%Y%m%d_%H%M%S)

# Create backup directory
mkdir -p "$BACKUP_DIR"

# Backup application files
tar -czf "$BACKUP_DIR/app_$DATE.tar.gz" -C /var/www sveltycms

# Backup database (MongoDB example)
mongodump --uri="mongodb://localhost:27017/sveltycms" --out="$BACKUP_DIR/db_$DATE"
tar -czf "$BACKUP_DIR/db_$DATE.tar.gz" -C "$BACKUP_DIR" "db_$DATE"
rm -rf "$BACKUP_DIR/db_$DATE"

# Backup Redis data
cp /var/lib/redis/dump.rdb "$BACKUP_DIR/redis_$DATE.rdb"

# Remove old backups
find "$BACKUP_DIR" -type f -mtime +$RETENTION_DAYS -delete

echo "Backup completed: $DATE"
# Make executable
sudo chmod +x /usr/local/bin/backup-sveltycms.sh

# Add to crontab (daily at 2 AM)
sudo crontab -e

Add line:

0 2 * * * /usr/local/bin/backup-sveltycms.sh >> /var/log/sveltycms-backup.log 2>&1

9. Performance Benchmarking

Apache Bench

# Test homepage
ab -n 1000 -c 100 https://your-domain.com/

# Test API endpoint
ab -n 1000 -c 50 https://your-domain.com/api/collections

K6 Load Testing

File: load-test.js

import http from "k6/http";
import { check, sleep } from "k6";

export const options = {
  stages: [
    { duration: "2m", target: 100 }, // Ramp up to 100 users
    { duration: "5m", target: 100 }, // Stay at 100 users
    { duration: "2m", target: 0 }, // Ramp down
  ],
  thresholds: {
    http_req_duration: ["p(95)<500"], // 95% of requests under 500ms
  },
};

export default function () {
  const res = http.get("https://your-domain.com");
  check(res, {
    "status is 200": (r) => r.status === 200,
    "response time < 500ms": (r) => r.timings.duration < 500,
  });
  sleep(1);
}
# Install K6
curl https://github.com/grafana/k6/releases/download/v0.46.0/k6-v0.46.0-linux-amd64.tar.gz -L | tar xvz
sudo mv k6-v0.46.0-linux-amd64/k6 /usr/local/bin/

# Run test
k6 run load-test.js

10. Deployment Checklist

Pre-Deployment

  • Environment variables configured in .env
  • Database indexes created
  • Redis configured with authentication
  • SSL certificates installed and valid
  • Nginx configuration tested (nginx -t)
  • Application built (bun run build)
  • PM2 ecosystem file configured
  • Backup strategy implemented
  • Monitoring endpoints accessible
  • Firewall rules configured

Deployment

  • Start Redis: sudo systemctl start redis-server
  • Start Nginx: sudo systemctl start nginx
  • Start application: pm2 start ecosystem.config.js --env production
  • Save PM2 state: pm2 save
  • Enable PM2 startup: pm2 startup
  • Test health endpoint: curl https://your-domain.com/api/health
  • Test metrics endpoint: curl https://your-domain.com/api/metrics
  • Verify static assets load (check DevTools Network tab)
  • Test login functionality
  • Monitor logs: pm2 logs sveltycms

Post-Deployment

  • Monitor for 24 hours
  • Check error logs: tail -f /var/log/nginx/sveltycms.error.log
  • Verify cache hit rates
  • Test automatic session rotation
  • Verify rate limiting works
  • Run load tests
  • Test backup script
  • Document any custom configurations

Expected Performance Improvements

Metric Before Optimization After Optimization Improvement
Static asset latency 15-20ms <1ms 95% faster
API response (cached) 50ms 5-10ms 80-90% faster
Memory usage (per worker) 500MB 250-350MB 30-50% reduction
Concurrent connections 1,000 5,000+ 5x capacity
CPU usage (static assets) 20-30% 0-2% 90%+ reduction
Malicious requests blocked Node.js level Nginx level 80-90% early rejection
Session cache hit rate 75% 85-90% 10-15% improvement

Troubleshooting

Application Won’t Start

# Check PM2 logs
pm2 logs sveltycms --lines 100

# Check application port
sudo netstat -tlnp | grep 3000

# Test application directly
NODE_ENV=production node build/index.js

Static Assets Not Loading

# Verify Nginx serves static files
curl -I https://your-domain.com/_app/version.json

# Check file permissions
ls -la /var/www/sveltycms/build/client/_app/

# Verify Nginx root path
sudo nginx -T | grep root

High Memory Usage

# Check PM2 memory per process
pm2 list

# Reduce max_memory_restart in ecosystem.config.js
# Reduce number of instances
pm2 scale sveltycms 2  # Run only 2 instances

Redis Connection Errors

# Test Redis connection
redis-cli -a your_password ping

# Check Redis logs
sudo tail -f /var/log/redis/redis-server.log

# Restart Redis
sudo systemctl restart redis-server

Nginx 502 Bad Gateway

# Check if Node.js is running
pm2 status

# Check Nginx error logs
sudo tail -f /var/log/nginx/sveltycms.error.log

# Verify upstream connection
curl http://127.0.0.1:3000/api/health

Related Documentation


Summary

This production deployment guide covers:

Nginx - Reverse proxy with static asset optimization and rate limiting ✅ Redis - Distributed caching configuration ✅ PM2 - Cluster mode for multi-core utilization ✅ Security - TLS, firewall, Fail2Ban, and early threat blocking ✅ Monitoring - Health checks, metrics, and log analysis ✅ Optimization - Database indexes, connection pooling, caching ✅ Backups - Automated backup strategy ✅ Performance - 5x capacity increase, 95% faster static assets

Follow this guide to achieve enterprise-grade performance, security, and reliability for your SveltyCMS production deployment.

productiondeploymentnginxredisoptimizationperformancesecurity
Was this page helpful?