Overview
A Hostinger VPS is an ordinary Ubuntu or Debian server (or another Linux template) with root SSH, so the standard Linux playbook applies; nothing about the vendor changes it. Work in this order: harden, deploy, proxy, back up, monitor. Front it with cloudflare for edge TLS and caching.
Harden before installing anything
Create a non-root user, disable root and password SSH, enable a firewall, fail2ban, and unattended upgrades. Steps: hostinger-vps-hardening. Hostinger’s dashboard also offers a managed firewall; if you run it alongside UFW, keep the two rule sets identical.
Deploy a tagged release, not a floating branch
- Git-based: the deploy script runs
git fetch --tags && git checkout <tag>, builds, and restarts the service. Rollback isgit checkout <previous-tag>and a restart. - Artifact-based: CI builds, uploads to the box, and a systemd unit restarts the service. Use it when the build is too heavy for the VPS.
The deploy user owns the app directory and is the only user allowed to restart the service.
Supervise the app with systemd, not pm2
systemd is already on the box: it restarts on failure, journals output, and starts at boot. Write /etc/systemd/system/app.service:
[Unit]
Description=App
After=network.target
StartLimitIntervalSec=60
StartLimitBurst=5
[Service]
User=deploy
WorkingDirectory=/srv/app
EnvironmentFile=/etc/app/env
ExecStart=/usr/bin/node /srv/app/server.js
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
[Install]
WantedBy=multi-user.targetsystemctl daemon-reload && systemctl enable --now app
systemctl status appStartLimitIntervalSecandStartLimitBurstbelong in[Unit]. They stop a crash loop after five failures in 60 seconds; clear the counter withsystemctl reset-failed appafter fixing the cause.Restart=on-failurerestarts only after a non-zero exit or signal; useRestart=alwaysfor workers that must never stay down.- Put secrets in
EnvironmentFile, not the unit (units show up insystemctl showand git). Create it withchmod 600andchown root:root; systemd reads it as root before dropping todeploy. - Edit a unit, then
systemctl daemon-reload && systemctl restart app.
Read logs with journalctl and persist the journal
journalctl -u app -f # follow
journalctl -u app -n 100 -p err # last 100 lines, errors and above
journalctl -u app --since "2026-10-01 00:00" --until "2026-10-01 06:00"Storage=auto keeps the journal on disk only if /var/log/journal exists. Set Storage=persistent in /etc/systemd/journald.conf so logs survive reboots.
Treat Hostinger’s backups as a convenience, not your backup
Hostinger takes weekly automated backups (daily if you buy it), keeps up to four, and offers one manual snapshot that expires after a day. Restoring either overwrites everything on the server. Keep your own encrypted off-box copies and test restores: hostinger-vps-backups. For Postgres, see postgres for pg_dump.
Put TLS in front of the app
Use Caddy for a single app, or nginx for complex routing: hostinger-vps-reverse-proxy-caddy.
Monitor outside and inside
node_exporterscraped by a hosted or self-hosted Prometheus: CPU, memory, disk, and load.- An external uptime check (UptimeRobot, BetterStack, Cronitor) on a public health endpoint every minute; internal metrics cannot report that the box is down.
- Alert on disk above 80 percent; a full disk is the most common preventable outage on a small VPS.