This guide covers installing nuntius on a Linux server with `nginx`, `systemd`, and `rsync` already in place — the same setup used by [petrbalvin.org](https://www.petrbalvin.org). The same `rsync -avz` workflow that ships the static website works for nuntius with no changes.
## Architecture in Production
```mermaid
graph LR
Browser[Visitor's browser]:::accent6
Internet((Internet)):::accent2
Nginx[nginx :443]:::accent0
Nuntius[nuntius :8080]:::accent1
Systemd[systemd unit]:::accent7
SMTP[(SMTP provider)]:::accent2
Disk[(/var/lib/nuntius/data/*.jsonl)]:::accent2
Browser --> Internet
Internet --> Nginx
Nginx -->|proxy_pass /api/nuntius/| Nuntius
Systemd -.->|manages| Nuntius
Nuntius -->|SMTP PLAIN| SMTP
Nuntius -->|append JSONL| Disk
```
nuntius listens on `127.0.0.1:8080` (default). nginx terminates TLS on `:443` and proxies `/api/nuntius/*` to the upstream. systemd restarts the process on failure.
> `install.py` lives in the repository root. The systemd unit is included inline in Option A below (you do not need to rsync it from the repo). `.env.example` is a template for the secrets file. Adjust the `rsync` source list to match what you keep checked in.
sudo $EDITOR /etc/nuntius/.env # set NUNTIUS_SMTP_PASSWORD
# Edit the auto-generated config to your real SMTP + allowed origins
sudo $EDITOR /etc/nuntius/config.toml
# Restart and watch logs
sudo systemctl restart nuntius
sudo journalctl -u nuntius -f
```
The service file expects the binary at `/usr/local/bin/nuntius`, the env file at `/etc/nuntius/.env`, and the data dir at `/var/lib/nuntius` (the config's `data_dir: "./data"` resolves to `/var/lib/nuntius/data` because the unit sets `WorkingDirectory=/var/lib/nuntius`). If you'd rather use `/opt/nuntius/...` or any other layout, edit the systemd unit block above before installing.
Before running the script, copy the systemd unit from the block in Option A above into `/tmp/nuntius/nuntius.service` (the script reads it from the current working directory):
```bash
ssh user@your-server
cd /tmp/nuntius
# Paste the [Unit]…[Install] block from Option A into this file:
Without `X-Forwarded-For`, the rate limiter will see every request as coming from `127.0.0.1` and effectively become useless once the form is live. The `Host` header also matters for vhost-aware upstreams; the default is fine for a single nuntius instance.
## 4. Wire It Up to Your Web
If you proxy `/api/nuntius/*` through nginx (recommended, step 3 above), `VITE_NUNTIUS_URL` can stay empty and the form will post to the same origin as the page. Otherwise, set it to the bare upstream URL:
```bash
# .env (in the web project, only needed if NOT proxying through nginx)
VITE_NUNTIUS_URL=https://your-server:8080
```
For a feedback or newsletter endpoint on the same site, just call the matching path:
For schema changes, also ship the new `config.toml` and a migration note in your changelog.
## 6. Updating the Config
The service does **not** auto-reload `config.toml`. After editing it, restart:
```bash
sudo $EDITOR /etc/nuntius/config.toml
sudo systemctl restart nuntius
sudo journalctl -u nuntius -f
```
The process exits 1 on a broken config (unknown field, unknown form type, duplicate path, missing SMTP host, …) and systemd will not keep it up. Fix the reported line and retry.