The JekCMS production configuration we recommend for a VPS running Nginx, PHP 8.3, and MariaDB 11 involves specific values for worker_processes, fastcgi_cache, opcache settings, and innodb_buffer_pool_size. These settings are validated on production servers handling 50,000 to 500,000 monthly page views.
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 = Offremoves theX-Powered-Byheaderdisplay_errors = Offprevents error messages from leaking to visitorslog_errors = Onsends errors to the log file insteadsession.cookie_httponly = 1prevents JavaScript from reading session cookiessession.cookie_secure = 1sends session cookies only over HTTPSupload_max_filesize = 50Mmatches the Nginxclient_max_body_sizemax_execution_time = 30prevents 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.preloadto 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
mysqldumpdaily at 03:00, compressed with gzip, stored locally and synced to an S3-compatible bucket. Retention: 30 daily backups, 12 monthly backups. - Uploads directory:
rsyncto 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.