DeployEasy
ProductionIntermediate

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.

· 3 min read· 489 words
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