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 is git 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.target
systemctl daemon-reload && systemctl enable --now app
systemctl status app
  • StartLimitIntervalSec and StartLimitBurst belong in [Unit]. They stop a crash loop after five failures in 60 seconds; clear the counter with systemctl reset-failed app after fixing the cause.
  • Restart=on-failure restarts only after a non-zero exit or signal; use Restart=always for workers that must never stay down.
  • Put secrets in EnvironmentFile, not the unit (units show up in systemctl show and git). Create it with chmod 600 and chown root:root; systemd reads it as root before dropping to deploy.
  • 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_exporter scraped 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.