ffshmon monitors a WireGuard connection and attempts to recover it when the VPN check fails. It checks the configured FastD service, tests the WireGuard interface through Mullvad, and regenerates the WireGuard configuration once after a failure. Email alerts are optional and disabled by default.
It also provides a Prometheus endpoint with the latest up/down status.
- Linux with
systemdandsystemctl - Python 3.10 or newer
curl- A WireGuard interface named
exitby default - A FastD service named
fastd@ffsh.serviceby default /opt/wg-conf-gen/wg-conf-gen.pyfor automatic configuration recovery- SMTP access to the configured mail host if email alerts are enabled
Create a virtual environment and install the Python dependencies:
python3 -m venv .venv
.venv/bin/python -m pip install -r requirements.txtRun a one-shot check without configuring email:
sudo .venv/bin/python wireguard.py check \
--log /var/log/ffshmon.log \
--loglevel INFOThe command exits after one health cycle. If the FastD service is down, the connection probe is skipped and the status is considered down. If the probe fails, ffshmon regenerates the WireGuard configuration and retries once. A second failure stops FastD and WireGuard. Email is only sent when explicitly enabled.
Set both FFSHMON_MAIL_USER and FFSHMON_MAIL_PASSWORD in the process environment and add --email-alerts to check or serve. Credentials are only read when the flag is enabled; missing or partial credentials produce a configuration error. SMTP delivery failures are logged and do not interrupt monitoring.
For a systemd service, store these variables in a protected environment file and add EnvironmentFile=/etc/ffshmon/mail.env to the unit. Restrict access to that file because it contains a password. Do not put credentials in command-line arguments or ExecStart.
Start the long-running monitor with:
sudo .venv/bin/python wireguard.py serve \
--log /var/log/ffshmon.log \
--loglevel INFOBy default, the process:
- Runs an immediate health check, then repeats every 60 seconds.
- Listens on
127.0.0.1:8000. - Exposes the latest completed result at
/metrics. - Does not run a new health check when Prometheus scrapes the endpoint.
Example request:
curl http://127.0.0.1:8000/metricsThe relevant metric is:
wireguard_up{interface="exit"} 1.0
A value of 1 means the latest check succeeded. A value of 0 means the FastD service or WireGuard connectivity check is down.
The log level can be set with --loglevel to DEBUG, INFO, WARNING, ERROR, or CRITICAL. The listener and polling interval can be changed with --host, --port, and --interval:
sudo .venv/bin/python wireguard.py serve \
--log /var/log/ffshmon.log \
--loglevel INFO \
--host 127.0.0.1 \
--port 8000 \
--interval 60Add a scrape job for the host running ffshmon:
scrape_configs:
- job_name: ffshmon
static_configs:
- targets: ["127.0.0.1:8000"]If Prometheus runs on another host, bind serve to an appropriate reachable address and protect the endpoint with firewall rules or a reverse proxy. The endpoint has no built-in authentication.
The connectivity probe runs as the service user and does not require sudo. The service-management and configuration-recovery commands use sudo and must be allowed for the service user without an interactive password prompt.
Run serve as a supervised systemd service so the endpoint remains available. A minimal unit could look like this:
[Unit]
Description=WireGuard connectivity monitor
After=network-online.target
[Service]
Type=simple
WorkingDirectory=/opt/ffshmon
EnvironmentFile=-/etc/ffshmon/mail.env
ExecStart=/opt/ffshmon/.venv/bin/python /opt/ffshmon/wireguard.py serve --log /var/log/ffshmon.log
Restart=on-failure
[Install]
WantedBy=multi-user.targetTo enable alerts for this service, add --email-alerts to ExecStart and configure the mail environment variables using a protected environment file or another systemd credential mechanism.
Run the focused tests with:
.venv/bin/python -m unittest -v test_wireguard.pyRun Pylint with:
.venv/bin/python -m pylint wireguard.py test_wireguard.py