Nginx configuration for a 2-vCPU VPS: worker_processes auto (matches CPU core count), worker_connections 1024, keepalive_timeout 65, client_max_body_size 50m (for media uploads). PHP-FPM pool: pm = dynamic, pm.max_children = 20, pm.start_servers = 5, pm.min_spare_servers = 5, pm.max_spare_servers = 10. These values assume a 4GB RAM VPS; adjust pm.max_children proportionally for different RAM sizes.

fastcgi_cache: Zero-PHP Traffic Absorption

Enable fastcgi_cache in Nginx for full-page caching at the web server layer. Configure a 1GB cache zone in nginx.conf: fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=jekcms:10m max_size=1g. Set a 10-minute TTL for GET requests returning 200. Skip the cache for logged-in users by checking for the session cookie with a fastcgi_cache_bypass directive. This layer absorbs traffic spikes without invoking PHP at all.

OPcache for Production

OPcache settings in php.ini: opcache.memory_consumption=256, opcache.max_accelerated_files=20000, opcache.validate_timestamps=0 in production (set to 1 during development to pick up file jekcms.alfadizayn.com/blog/v1-4-0-migration-guide-breaking-changes-and-how-to-adapt" class="auto-link" target="_blank" rel="noopener">changes). The validate_timestamps=0 setting eliminates stat() calls on every request. On NVMe storage this is a marginal improvement; on network-attached or spinning-disk storage it can reduce request latency by 5–15ms per request.

MariaDB: Buffer Pool and Slow Query Log

MariaDB: set innodb_buffer_pool_size to 70% of available RAM, rounded to the nearest 512MB. On a 4GB server, employ 2560M. Enable the slow query log from day one: slow_query_log=1, long_query_time=1, log_queries_not_using_indexes=1. Slow queries are far easier to diagnose when you have two weeks of historical data rather than when you are already in a production incident.

SSL and Security Headers

Configure SSL with Let's Encrypt using Certbot. The Nginx SSL block should include:

server {
 listen 443 ssl http2;
 server_name yoursite.com;

 ssl_protocols TLSv1.2 TLSv1.3;
 ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
 ssl_prefer_server_ciphers off;
 ssl_session_timeout 1d;
 ssl_session_cache shared:SSL:10m;

 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 Strict-Transport-Security "max-age=63072000" always;
}

Test your SSL configuration at ssllabs.com/ssltest and target an A+ rating. The HSTS header with a 2-year max-age tells browsers to always use HTTPS; do not enable it until you have confirmed HTTPS works correctly for all resources.

PHP Security Settings

Harden PHP in production with these php.ini directives:

  • expose_php = Off removes the X-Powered-By header
  • display_errors = Off prevents error messages from leaking to visitors
  • log_errors = On sends errors to the log file instead
  • session.cookie_httponly = 1 prevents JavaScript from reading session cookies
  • session.cookie_secure = 1 sends session cookies only over HTTPS
  • upload_max_filesize = 50M matches the Nginx client_max_body_size
  • max_execution_time = 30 prevents runaway scripts from consuming resources

Monitoring and Automated Restarts

Systemd handles auto-restart for Nginx and PHP-FPM on Ubuntu/Debian. For application-level monitoring, configure JekCMS's health check endpoint at /api/health which checks database connectivity, file system writability, and cache layer status. Set up an external monitoring service (UptimeRobot, Pingdom, or Better Stack) to hit this endpoint every 60 seconds and alert on failure.

Log Rotation

Configure logrotate for all log files to prevent disk space exhaustion:

  • Nginx access and error logs: rotate daily, keep 14 days, compress after 1 day
  • PHP-FPM slow log and error log: rotate weekly, keep 4 weeks
  • MariaDB slow query log: rotate weekly, keep 8 weeks for query analysis
  • JekCMS application logs: rotate daily, keep 30 days

Scaling Beyond 500,000 Monthly Page Views

When traffic exceeds 500,000 monthly page views on a single VPS, consider these optimizations before upgrading hardware:

  • Increase fastcgi_cache TTL from 10 minutes to 1 hour for pages that change infrequently. This alone can absorb a 3-5x traffic spike.
  • Add a CDN layer like Cloudflare (free tier sufficient) or Bunny.net ($1/month per TB). Cache static assets and HTML at the edge.
  • Move MariaDB to a separate server if the database is the bottleneck. Separating them lets each service use the full buffer pool.
  • Enable PHP preloading with opcache.preload to compile all JekCMS core files at PHP-FPM startup, eliminating the first-request compilation penalty.

Backup Strategy

Implement a 3-2-1 backup strategy for production VPS deployments: three copies of data, on two different storage types, with one copy off-site. For JekCMS:

  • Database: automated mysqldump daily at 03:00, compressed with gzip, stored locally and synced to an S3-compatible bucket. Retention: 30 daily backups, 12 monthly backups.
  • Uploads directory: rsync to off-site storage nightly. Only new and changed files are transferred, keeping bandwidth usage minimal.
  • Full server snapshot: weekly VPS snapshot through your hosting provider's API. This captures the entire system state including Nginx, PHP, and MariaDB configurations.

Test your restore process quarterly. A backup you have never restored is a backup you cannot trust. Document the restore procedure so any team member can execute it during an incident, not just the person who set up the backups.