Website Health Monitor
A Go service that checks around the clock that my projects are up and actually working — with a web UI, an API and history in PostgreSQL.
- Role
- Requirements, architecture, Go development with Claude Code
- Period
- 2026.01 → …
- Status
- In progress
- Stack
- Go · PostgreSQL · Docker · GitHub Actions · Grafana
Interface
01 / 05Problem
Links to my pet projects go to employers, and a link must never open a dead service. The projects live on a VPS and free hosting — they fall asleep, respond after a cold start, and a free Supabase database pauses after a week without requests. Learning about an outage from a recruiter is too late.
Solution
The monitor checks every target on a schedule — not just the response code but also a keyword on the page — writes the result to PostgreSQL and shows an "all green / something is down" summary with 24-hour uptime. Targets are added, changed and disabled in the web UI or via the API and are picked up without a restart.
I wrote the first version in January as a Go learning project. In October I came back to it with a new goal — watching the projects in this portfolio — ran an audit, found 10 issues and drew up a 7-stage plan. The code is written with Claude Code along that plan with a decision log; three stages are done.
Goals
- 01Learn about an outage before an employer does
- 02Check that a project works, not just that it responds
- 03Add and disable projects without rebuilding
- 04Keep free-tier services awake
Features
Key requirements
| ID | Requirement | Priority |
|---|---|---|
| FR-01 | A target is up if the response has the expected code (any 2xx after redirects by default) and, if set, contains the keyword. | Must |
| FR-02 | Adding, changing and disabling a target apply without restarting the service. | Must |
| FR-03 | A target is never checked in parallel with itself. | Must |
| FR-04 | Interval at least 30 s; timeout at most 120 s and less than the interval. | Must |
| FR-05 | Telegram alerts on outage and recovery — without spam. | Should |
| NFR-01 | Stopping or deploying the service produces no false outages. | Must |
| NFR-02 | UI and API require a password; /healthz is open for external monitoring. | Must |
| NFR-03 | The DB schema changes only through new migrations; old data is carried over. | Must |
Scenarios
- 01A check does not find the keyword: the page returned 200 but it is an error stub
- 02The result with the reason is written to health_checks
- 03In the UI the target moves to the top in red; the summary says "1 down"
- 04After stage 5 — a Telegram message, and another on recovery
- 01Adds a target in the form: URL, keyword, a timeout for cold starts
- 02The server validates the settings and saves the target
- 03The scheduler starts checking right away — no restart
Timeline
- 2026.01First versionChecks from a list in code, PostgreSQL, Docker, CI with tests, Grafana output.
- 2026.10Stage 0: audit and planA new goal — monitoring the portfolio; 10 issues found and a 7-stage plan.
- 2026.10Stages 1–2: core and targets in the DBStatus logic, timeouts, migrations, pgx, graceful shutdown; a targets table, keywords, headers, live reload.
- 2026.10Stage 3: UI and APIA web UI without JS frameworks, a JSON API, validation, Basic Auth, CSRF protection, /healthz.
- NextStages 4–7Grafana as code, Telegram alerts, quality and CI, deployment to the VPS with external monitoring of the checker itself.
Challenges
Outcome
Three of seven stages are done — about 3,200 lines of Go including 34 tests, integration ones on PostgreSQL among them. The service checks targets, applies changes on the fly and shows a summary with uptime.
Next — a Grafana dashboard from the repository, Telegram alerts and deployment to the VPS next to the projects it watches.