Statping-ng is a self-hosted uptime monitor and status page written in Go. It checks your websites and services at a fixed interval, records response times and failures, publishes a public status page and sends alerts when a check fails. In this tutorial you will install the Statping-ng binary on Ubuntu 24.04, run it as an unprivileged systemd service with SQLite, publish it over HTTPS through Nginx, and create checks both from the dashboard and through the REST API.

Prerequisites

To follow this tutorial you need:

  • A server running Ubuntu 24.04 LTS with at least 512 MB of RAM, for example a CubePath VPS. Run the monitor outside the infrastructure it watches, so it can still report when that infrastructure is down.
  • A non-root user with sudo privileges and UFW enabled with OpenSSH allowed.
  • A domain name with an A record for a subdomain such as status.your_domain pointing to the server's IP address.
  • A Slack incoming webhook URL or SMTP credentials if you want alerts (optional).

Replace status.your_domain with your own hostname throughout the guide.

Step 1 - Installing the Statping-ng binary

Statping-ng is distributed as a single static binary on its GitHub releases page. This guide uses version v0.93.0; check the releases page for a newer one and adjust the version in the commands.

Download the archive for your architecture (amd64 for most servers, arm64 for ARM machines) and unpack it in a temporary directory:

cd /tmp
curl -fsSLO https://github.com/statping-ng/statping-ng/releases/download/v0.93.0/statping-linux-amd64.tar.gz
tar -xzf statping-linux-amd64.tar.gz

Install the binary into /usr/local/bin:

sudo install -m 755 /tmp/statping /usr/local/bin/statping

Check that it runs:

statping version
0.93.0 (a1b2c3d)

Step 2 - Creating a service user and the configuration

Statping-ng does not need root privileges, so run it as a dedicated system user without a login shell:

sudo useradd --system --home-dir /var/lib/statping --shell /usr/sbin/nologin statping

Statping-ng reads its settings from environment variables. When DB_CONN is set, it configures itself on first start instead of showing the web setup wizard. Generate a random API secret and a strong admin password first:

openssl rand -hex 32
openssl rand -base64 18

Create the environment file:

sudo mkdir -p /etc/statping
sudo nano /etc/statping/statping.env
# Storage: SQLite file inside STATPING_DIR
DB_CONN=sqlite
STATPING_DIR=/var/lib/statping

# Status page
NAME=Example Status
DESCRIPTION=Live status of Example services
DOMAIN=https://status.your_domain

# First admin account, created on first start
ADMIN_USER=admin
ADMIN_PASSWORD=your_strong_password
ADMIN_EMAIL=you@your_domain

# Token for the REST API (Authorization: Bearer ...)
API_SECRET=your_api_secret

# Start with an empty dashboard and do not send error reports upstream
SAMPLE_DATA=false
ALLOW_REPORTS=false

Replace your_strong_password and your_api_secret with the values you generated. Because the file holds secrets, make it readable only by root and the statping group:

sudo chown root:statping /etc/statping/statping.env
sudo chmod 640 /etc/statping/statping.env

Step 3 - Running Statping-ng with systemd

Create a unit file:

sudo nano /etc/systemd/system/statping.service
[Unit]
Description=Statping-ng status page and uptime monitor
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=statping
Group=statping
EnvironmentFile=/etc/statping/statping.env
StateDirectory=statping
WorkingDirectory=/var/lib/statping
ExecStart=/usr/local/bin/statping --ip 127.0.0.1 --port 8080
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

StateDirectory=statping makes systemd create /var/lib/statping owned by the service user, and --ip 127.0.0.1 keeps the web interface off the public network until Nginx is in front of it.

Load the unit, then enable and start the service:

sudo systemctl daemon-reload
sudo systemctl enable --now statping

Check its state:

sudo systemctl status statping --no-pager
● statping.service - Statping-ng status page and uptime monitor
     Loaded: loaded (/etc/systemd/system/statping.service; enabled; preset: enabled)
     Active: active (running) since Thu 2026-09-24 10:12:03 UTC; 6s ago
   Main PID: 2154 (statping)

Confirm it listens on localhost and that setup completed. The /health endpoint reports whether the instance is configured:

sudo ss -tlnp | grep 8080
curl -s http://127.0.0.1:8080/health
LISTEN 0      4096       127.0.0.1:8080       0.0.0.0:*    users:(("statping",pid=2154,fd=9))
{"online":true,"services":0,"setup":true}

On first start, Statping-ng writes a config.yml and the SQLite database into /var/lib/statping. If anything goes wrong, read the log with sudo journalctl -u statping -n 50.

Step 4 - Publishing the status page with Nginx and HTTPS

Install Nginx and Certbot:

sudo apt update
sudo apt install nginx certbot python3-certbot-nginx

Create a server block that proxies to Statping-ng:

sudo nano /etc/nginx/sites-available/statping
server {
    listen 80;
    listen [::]:80;
    server_name status.your_domain;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Enable the site and check the configuration:

sudo ln -s /etc/nginx/sites-available/statping /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Open HTTP and HTTPS in the firewall, then get a certificate. Certbot adds the TLS configuration and an HTTP to HTTPS redirect for you:

sudo ufw allow 'Nginx Full'
sudo certbot --nginx -d status.your_domain

Open https://status.your_domain in your browser. You should see an empty status page with the name you set in NAME. Log in at https://status.your_domain/login with ADMIN_USER and ADMIN_PASSWORD.

Step 5 - Adding checks from the dashboard

Statping-ng supports these check types:

TypeWhat it does
httpRequests a URL and compares the status code (and optionally the body) with what you expect
tcp / udpOpens a connection to a host and port
icmpSends a ping to a host
grpcConnects to a gRPC endpoint, optionally with the standard health check
smtp / imapConnects to a mail server
staticA manual entry with no check, useful for components you update by hand

To add a check, open Services in the dashboard and click Create. For a website, fill in:

  • Service Type: HTTP Service.
  • Service Endpoint: https://your_domain.
  • Expected Status Code: 200.
  • Check Interval: 60 seconds. Very short intervals increase load on both sides without adding much value.
  • Request Timeout: 10 seconds.
  • Verify SSL: enabled, so an expired or invalid certificate counts as a failure.
  • Notify After Failures: 2, so a single dropped request does not wake anybody up.

Save the service. Within one interval, it turns green on the dashboard and on the public page. Use Groups in the dashboard to organize services under headings such as "Website" and "API".

ICMP checks work without root privileges because Ubuntu allows unprivileged ping sockets for all groups by default (net.ipv4.ping_group_range).

Step 6 - Managing checks through the API

Everything in the dashboard is also available through the REST API. Write endpoints require the API_SECRET from Step 2 as a bearer token. Export it together with the base URL:

export STATPING_URL="https://status.your_domain/api"
export STATPING_TOKEN="your_api_secret"

Create an HTTP check:

curl -s -X POST "$STATPING_URL/services" \
  -H "Authorization: Bearer $STATPING_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Website",
    "domain": "https://your_domain",
    "type": "http",
    "method": "GET",
    "expected_status": 200,
    "check_interval": 60,
    "timeout": 10,
    "verify_ssl": true,
    "notify_after": 2,
    "allow_notifications": true,
    "public": true
  }'
{"status":"success","type":"service","method":"create","id":1,"output":{...}}

Create a TCP check for a database port. For tcp checks, domain is the host name or IP address and port is set separately:

curl -s -X POST "$STATPING_URL/services" \
  -H "Authorization: Bearer $STATPING_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "PostgreSQL", "domain": "db.your_domain", "type": "tcp", "port": 5432, "check_interval": 60, "timeout": 5, "notify_after": 1, "public": false}'

List services with their current state and 24-hour uptime:

curl -s "$STATPING_URL/services" \
  -H "Authorization: Bearer $STATPING_TOKEN" \
  | python3 -c 'import json,sys; [print(s["id"], s["name"], "online" if s["online"] else "OFFLINE", s["online_24_hours"]) for s in json.load(sys.stdin)]'
1 Website online 100
2 PostgreSQL online 100

Without the token, the same endpoint returns only public services and hides fields such as the monitored address.

To publish an incident on a service, post to that service's incidents endpoint, then add updates to the incident as it evolves. Update types used by the dashboard are Investigating, Update, Unknown and Resolved:

curl -s -X POST "$STATPING_URL/services/1/incidents" \
  -H "Authorization: Bearer $STATPING_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title": "Slow page loads", "description": "Some visitors see slow page loads. We are investigating."}'

curl -s -X POST "$STATPING_URL/incidents/1/updates" \
  -H "Authorization: Bearer $STATPING_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type": "Resolved", "message": "A cache node was replaced and response times are back to normal."}'

Step 7 - Configuring notifications

Statping-ng ships with notifiers for email (SMTP), Slack, Discord, Telegram, Mattermost, Pushover, Gotify, Twilio SMS, Amazon SNS, generic webhooks and local commands. They are configured in the dashboard:

  1. Open Settings and select the notifier in the left-hand list, for example Slack.
  2. Paste your incoming webhook URL (for SMTP: host, port, username, password, sender and recipient addresses).
  3. Turn on the switch at the top of the notifier form to enable it and click Save.
  4. Click Test Failure and Test Success to send sample messages.

Notifications fire for every service that has Enable Notifications turned on, once the number of consecutive failures reaches its Notify After Failures value, and again when the service recovers.

To check the whole path end to end, create a test HTTP service pointing to a URL that returns 404 (for example https://your_domain/statping-test) with Notify After Failures set to 1. You should receive an alert within one interval. Delete the test service afterwards.

Step 8 - Backing up Statping-ng

All state lives in /var/lib/statping: config.yml and the SQLite database statping.db. The environment file in /etc/statping holds your secrets. To make a consistent copy, stop the service briefly and archive both directories:

sudo systemctl stop statping
sudo tar -czf /root/statping-$(date +%F).tar.gz /var/lib/statping /etc/statping
sudo systemctl start statping

Troubleshooting

The service restarts in a loop and the journal says the directory is not writable. Statping-ng must be able to write to STATPING_DIR. Check that the unit contains StateDirectory=statping and that /var/lib/statping is owned by statping.

The login page loads but the dashboard shows "Setup" again. The service started without DB_CONN and entered setup mode, usually because the environment file was not readable. Run sudo systemctl show statping -p EnvironmentFiles and check the permissions of /etc/statping/statping.env.

A check is red but the site works in your browser. Test from the server itself, because that is where the check runs: curl -sI https://your_domain or nc -zv db.your_domain 5432. Firewalls that allow only certain source IPs, IPv6-only records and certificate errors with Verify SSL enabled are the usual causes.

API calls return {"error":"user not authenticated"}. The Authorization header must be exactly Bearer followed by the value of API_SECRET. If you change API_SECRET in the environment file, restart the service.

Conclusion

You installed Statping-ng as a hardened systemd service on Ubuntu 24.04, published it over HTTPS with Nginx, created HTTP and TCP checks from the dashboard and the API, and wired up notifications. As next steps, scrape the built-in Prometheus /metrics endpoint (it accepts the same bearer token) if you already run Prometheus, add a check-in (heartbeat) for cron jobs that should report in on schedule, and move to PostgreSQL if you plan to run hundreds of checks.