ProjectsStatus monitor
Status monitor
A self-hosted uptime and status monitor written in Go. It checks websites, TCP ports and FiveM servers, shows availability and response times publicly and sends a mail when a service goes down and when it is back.

Key facts
As of
- Targets
- 4 on the public status page
- Availability since monitoring began on 17 September
- Website 99.77 %, job finder 100.00 %, job finder API 99.98 %, source chat 100.00 %
- Tests
- 142 test functions in 31 files
One hiccup is not an outage
An incident only starts after several failed checks in a row. The number can be set per target, the default is three. A single timeout stays a data point and raises no alert.
When the incident starts, a mail goes out. Once the target responds again, the incident ends, and a second mail says how long the outage lasted.
One goroutine per target
Every target runs in its own goroutine with its own interval. A random offset keeps all checks from starting at the same moment. No more than eight run at once, the rest wait for a free slot.
HTTP(S) checks whether the status code is in the allowed range and optionally a keyword in the response body. TCP only checks whether the port accepts a connection.
For FiveM servers the monitor queries info.json and players.json of the FXServer. It stores the player count, the maximum and the version, never player names.
- Goroutine waits for its interval, with a random offset
- Slot opens up, at most eight checks run at once
- Check over HTTP(S), TCP or FiveM
- SQLite stores the result and response time
Status page and admin
For every target the status page shows availability over 24 hours, 7 days and 30 days, a bar of the last 90 hours and the response time history. Addresses and error messages never appear there. The page is available in German and English.
The charts are rendered as SVG on the server. Raw data is kept for 30 days and rolled up into hourly values.
Targets and incidents are managed in a separate admin interface behind a login.
Security
The monitor requests addresses entered in the admin interface. To keep that from reaching the internal network, what counts is the IP after name resolution, not the hostname.
- Passwords hashed with argon2id
- Session cookie with the __Host- prefix
- CSRF protection through an Origin check and a masked token
- Rate limit on login attempts
- SSRF block on the resolved IP, including DNS rebinding
- Strict Content Security Policy without inline code
Technology
- Language
- Go with net/http, html/template and embed from the standard library
- Data
- SQLite without CGO through modernc.org/sqlite
- Charts
- SVG, rendered on the server
- Operations
- a single binary in a distroless Docker image, read-only file system
- CI
- GitHub Actions with go test -race, golangci-lint and govulncheck
Live
- Address status.dmn-software.com
- Access Public, admin behind login