Production VPS Setup: Nginx, PHP, MySQL, Redis, Backups, and Monitoring

Infrastructure Jun 14, 2026 · OTPZap Team

A healthy production VPS does not have to be expensive, but it has to be organized. Many small applications fail not because traffic is huge, but because the server has no clear structure: backups are untested, cron jobs run twice, services die silently, or logs fill the disk.

This article approaches VPS setup practically. The goal is not the most advanced configuration. The goal is a server that is maintainable, auditable, and less likely to create panic at two in the morning.

Folders and users

Start with a consistent folder structure. Put the application in a clear directory, separate public files from configuration files, and keep permissions tight. Do not let `.env` files, SQL backups, or internal scripts be reachable from the web. The web server should only read files that are meant for the browser.

Use service users properly. PHP-FPM, workers, and cron jobs do not always need to run as root. The smaller the permissions of a process, the smaller the damage if that process is abused.

Nginx and PHP-FPM

Nginx receives requests, serves static files, and forwards PHP requests to PHP-FPM. A good configuration blocks sensitive files, enforces HTTPS, sets cache headers for static assets, and has clear error pages.

PHP-FPM should match server capacity. Too few workers create queues. Too many workers exhaust memory. Watch memory per process, then set `pm.max_children` realistically. For transaction systems, stability matters more than a benchmark number that only survives for a few minutes.

MySQL and indexes

MySQL is often the first bottleneck. Start with indexes that match query patterns. Order tables usually need indexes on user, status, created_at, and reference fields. Notification tables need indexes on user and time. Without indexes, a small server can feel slow even before traffic becomes large.

Database backups should be automated and occasionally restored in a test environment. A backup that is never tested is only an assumption. Store backups separately, define retention, and record the last successful backup time.

Redis, cache, and rate limits

Redis is useful for cache, sessions, lightweight queues, and rate limits. But do not make Redis the only place for critical state unless the system is designed for it. For balances, orders, deposits, and refunds, the database should remain the source of truth.

Cron and systemd

Cron is good for simple scheduled tasks. Systemd is better for workers that must keep running. Avoid running the same process from two places by accident. Many production bugs happen because an old cron job remains active while a new worker also processes the same data.

Every important cron should have logs. Every important service should have a restart policy. After deployment, check `systemctl status`, recent logs, and whether the service is actually running the latest binary or script.

Firewall and admin access

Close ports that are not needed. SSH should use keys instead of passwords. Server admin panels should be restricted with IP rules, VPN, or at least strong passwords and MFA. The database does not need to be public if the application runs on the same server.

Monitoring that is enough

Early monitoring does not need to be fancy. Watch CPU, RAM, disk, load average, service status, error logs, and response time. Add alerts for nearly full disks, dead services, failed backups, and rising error rates. A few relevant alerts are better than dozens that everyone ignores.

Example use case: verification platforms

A platform like OTPZap needs consistent infrastructure because users may arrive from web, Telegram, and API. Orders, balances, notifications, and code status must stay synchronized. Database backups, healthy workers, and audit logs are not extras. They are operational foundations.

Need a real transaction-flow reference?

OTPZap is a useful example of how dashboards, bots, APIs, workers, and notifications should read from the same source of truth.

Open OTPZap