---
title: "Hostinger VPS: Deploying with systemd"
slug: "hostinger-vps-deploy-systemd"
category: "ops"
tags: ["ops", "hostinger", "vps", "systemd", "linux", "deploy", "journalctl"]
status: "stable"
last_updated: 2026-05-14
summary: "Write a systemd service unit, configure restart policy and environment files, and query logs with journalctl on a Hostinger VPS."
related: ["[[ops/hostinger-vps]]", "[[ops/hostinger-vps-hardening]]", "[[ops/hostinger-vps-reverse-proxy-caddy]]", "[[ops/hostinger-vps-backups]]", "[[ops/github-pages]]"]
---

## Overview

systemd ships on every Ubuntu and Debian box that Hostinger provisions. It supervises processes, restarts them on failure, logs to the journal, and starts them at boot. Use it instead of pm2, supervisor, or screen. This page covers writing a unit file, setting up environment files, operating the service, and reading logs. See [[ops/hostinger-vps-hardening]] for the prerequisites that should be done before any app is deployed.

## Write the service unit file

Place unit files in `/etc/systemd/system/`. The filename determines the service name.

```ini
# /etc/systemd/system/app.service
[Unit]
Description=My App
Documentation=https://github.com/yourorg/yourapp
After=network.target

[Service]
Type=simple
User=deploy
Group=deploy
WorkingDirectory=/srv/app
EnvironmentFile=/etc/app/env
ExecStart=/usr/bin/node /srv/app/server.js
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=app

[Install]
WantedBy=multi-user.target
```

After writing or editing the file, reload the daemon before starting the service:

```bash
systemctl daemon-reload
systemctl enable --now app
```

`enable --now` activates the service for current boot and registers it for future boots in one command.

## Configure restart policy to match the failure mode

`Restart=on-failure` restarts only on non-zero exit codes. Use it for apps that may exit cleanly when signaled.

`Restart=always` restarts on any exit, including clean exits and signals. Use it for services that should never stop, such as a queue worker.

`RestartSec=5` waits five seconds before restarting. Pair it with a `StartLimitIntervalSec` and `StartLimitBurst` to avoid tight crash loops:

```ini
[Service]
Restart=on-failure
RestartSec=5
StartLimitIntervalSec=60
StartLimitBurst=5
```

This allows five restarts in sixty seconds before systemd gives up. Without limits, a misconfigured app burns CPU in a tight restart loop. After `StartLimitBurst` failures, use `systemctl reset-failed app` to clear the counter and try again after fixing the root cause.

## Store secrets in EnvironmentFile, not in the unit

Hard-coding secrets in the unit file exposes them in `systemctl show` output and in git history if the file is tracked.

Create the env file with restrictive permissions:

```bash
mkdir -p /etc/app
touch /etc/app/env
chmod 600 /etc/app/env
chown root:root /etc/app/env
```

Populate it with `KEY=value` pairs, one per line:

```
DATABASE_URL=postgres://user:pass@localhost/mydb
SECRET_KEY=replace_with_random_64_chars
REDIS_URL=redis://localhost:6379
```

Reference it in the unit with `EnvironmentFile=/etc/app/env`. systemd injects these into the process environment before exec. The deploy user does not need read access; the service runs as `deploy` but the env file is owned by root. If `deploy` must write to it during deploy, adjust ownership to `deploy:deploy` and `chmod 600`.

## Manage the service lifecycle

Common commands for the deploy workflow:

```bash
# Start, stop, restart
systemctl start app
systemctl stop app
systemctl restart app

# Reload without full restart (if the app supports SIGHUP)
systemctl reload app

# Check current state
systemctl status app

# Enable/disable at boot
systemctl enable app
systemctl disable app

# After editing the unit file
systemctl daemon-reload && systemctl restart app
```

Keep a deploy script that does `git fetch --tags && git checkout <tag> && systemctl restart app` rather than manual steps. Tags make rollback deterministic.

## Read logs with journalctl

All `StandardOutput` and `StandardError` from the service flow to the systemd journal. Query with `journalctl`:

```bash
# Follow live output
journalctl -u app -f

# Last 100 lines
journalctl -u app -n 100

# Since a specific time
journalctl -u app --since "2026-05-14 00:00" --until "2026-05-14 06:00"

# Filter by priority (err and above)
journalctl -u app -p err

# Output as JSON for log shipping
journalctl -u app -o json | head -5
```

Persist the journal across reboots by setting `Storage=persistent` in `/etc/systemd/journald.conf`. By default, Debian and Ubuntu store the journal in memory only and drop it on reboot.

## Related

- [[ops/hostinger-vps]]
- [[ops/hostinger-vps-hardening]]
- [[ops/hostinger-vps-reverse-proxy-caddy]]
- [[ops/hostinger-vps-backups]]
- [[ops/github-pages]]
