Production VPS Monitoring: Logs, CPU, RAM, Disk, and Health Checks
Monitor a production VPS with journald, Nginx logs, PM2, Docker health checks, CPU, RAM, disk, uptime, and actionable alerts.
Article by Phạm Minh Thiện
Front-end developer working directly with Next.js, NestJS, Docker, Nginx, and Ubuntu VPS deployments.
Reviewed on 7/21/2026.
Table of contents
Monitoring is not a single dashboard. A useful production baseline tells you whether the service is available, whether requests are failing, and whether the VPS is running out of CPU, memory, disk, inodes, or network capacity.
Start with four signals
- Availability: can users reach the public endpoint?
- Errors: are HTTP 5xx responses or application exceptions increasing?
- Latency: are responses getting slower?
- Saturation: is CPU, RAM, disk, an inode table, a connection pool, or a queue exhausted?
An alert should include a threshold, duration, owner, and first diagnostic command. “CPU is high” is less useful than “CPU above 90% for 10 minutes on the API VPS.”
Inspect the VPS
uptime
top
free -h
df -h
df -i
Check processes, open files, and network listeners when a service is unavailable:
ps aux --sort=-%mem | head
sudo ss -ltnp
Disk fullness and inode exhaustion can both prevent an application from writing files or logs.
Read system and Nginx logs
journalctl -p warning -b
journalctl -u nginx --since "30 minutes ago"
sudo tail -n 200 /var/log/nginx/error.log
sudo tail -n 200 /var/log/nginx/access.log
Use timestamps and request IDs to connect a public error to an application log. Do not put passwords, tokens, or full authorization headers in logs.
PM2 and Node.js
pm2 status
pm2 monit
pm2 logs myapp --lines 200
Monitor restart counts and memory limits, not only process status. A process that repeatedly restarts may look online between failures.
Docker health checks and logs
docker compose ps
docker inspect --format '{{json .State.Health}}' myapp-api
docker compose logs --since 30m --tail=200 api
A health check should be cheap and meaningful. A liveness check asks whether the process can respond; a readiness check may also verify that the database or required dependency is available.
Monitor the database and backups
Track PostgreSQL connection usage, slow queries, locks, disk growth, and failed backups. A successful backup job is not enough: schedule a restore test and record how long recovery takes.
Keep database data, uploaded files, and backups on a plan with known retention and off-host copies.
Make alerts actionable
Good first alerts include:
- public health check failure from more than one location;
- sustained HTTP 5xx increase;
- certificate expiry within a defined window;
- disk or inode usage above the team threshold;
- low available memory or repeated OOM kills;
- service restart loops;
- backup failure or stale backup age.
Avoid paging on a single short CPU spike unless it predicts actual user impact.
Keep a small incident runbook
curl -I https://example.com
sudo systemctl status nginx
pm2 status
docker compose ps
sudo journalctl -p err -b --no-pager
df -h
Write down who owns the service, how to stop a bad release, where backups live, and which changes require approval. Monitoring becomes valuable when it shortens the path from symptom to safe action.
Continue reading
Related articles
Point a Domain to a VPS with DNS, Cloudflare, and Nginx
Point a domain to an Ubuntu VPS with A, AAAA, and CNAME records, verify DNS propagation, configure Cloudflare and Nginx, and fix www issues.
Read article →Deploy Node.js to a VPS with PM2 and Nginx from A to Z
A practical Node.js deployment guide for Ubuntu using PM2 and Nginx, including builds, environment variables, HTTPS, logs, health checks, and rollback.
Read article →Safely Fix SSH Permission Denied and Host Key Changed Errors
Check the correct SSH user, private key, authorized_keys, file permissions, and server fingerprint before repairing publickey or known_hosts errors.
Read article →