A lightweight Kubernetes controller that automatically manages BetterStack Uptime monitors by watching Ingress resources. Annotate an Ingress and the controller will create, update, or delete the corresponding monitors — no manual configuration required.
The controller uses kopf to watch networking.k8s.io/v1 Ingress events. When an Ingress carries the betterstack.io/monitor: "true" annotation the controller reconciles BetterStack monitors for every hostname defined in spec.rules.
| Event | Action |
|---|---|
| Ingress created | Creates one monitor per host |
| Ingress updated | Updates existing monitors; removes monitors for deleted hosts |
| Ingress deleted | Deletes all monitors associated with the Ingress |
Monitor IDs are persisted back to the Ingress as betterstack.io/monitor-ids (a JSON map of host → monitor_id) so reconciliation is idempotent across controller restarts.
| Annotation | Required | Default | Description |
|---|---|---|---|
betterstack.io/monitor |
Yes | — | Set to "true" to enable monitoring |
betterstack.io/path |
No | /api/health |
HTTP path checked by the monitor |
betterstack.io/monitor-ids |
Auto-managed | {} |
JSON map of host → monitor ID written back by the controller |
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
betterstack.io/monitor: "true"
betterstack.io/path: "/healthz"
spec:
rules:
- host: my-app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 80
# Add the Helm repository (served via GitHub Pages)
helm repo add betterstack-monitor https://luong-komorebi.github.io/betterstack-monitor-controller
helm repo update
# Install
helm upgrade --install betterstack-monitor \
betterstack-monitor/betterstack-monitor-controller \
--namespace monitoring \
--create-namespace \
--set apiToken=<YOUR_TOKEN>
# Pin a specific chart/app version
helm upgrade --install betterstack-monitor \
betterstack-monitor/betterstack-monitor-controller \
--namespace monitoring \
--create-namespace \
--set apiToken=<YOUR_TOKEN> \
--version 0.1.0
Or install directly from the local chart:
helm upgrade --install betterstack-monitor \
./helm/betterstack-monitor-controller \
--namespace monitoring \
--create-namespace \
--set apiToken=<YOUR_TOKEN>
# Pin a specific image version
helm upgrade --install betterstack-monitor \
./helm/betterstack-monitor-controller \
--namespace monitoring \
--create-namespace \
--set apiToken=<YOUR_TOKEN> \
--set image.tag=1.2.3
# Reference an existing Secret instead of letting the chart create one
helm upgrade --install betterstack-monitor \
./helm/betterstack-monitor-controller \
--namespace monitoring \
--create-namespace \
--set existingSecret.name=my-betterstack-secret \
--set existingSecret.key=api-token
See helm/betterstack-monitor-controller/values.yaml for all available options.
# Create the secret
kubectl create namespace monitoring
kubectl create secret generic betterstack-monitor \
--namespace monitoring \
--from-literal=api-token=<YOUR_TOKEN>
# Deploy
kubectl apply -f k8s/deploy.yaml
This project uses uv for dependency management.
# Install uv (if not already installed)
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install all dependencies including dev tools
uv sync --group dev
# Run tests
uv run pytest
# Run tests with coverage
uv run pytest --cov --cov-report=term-missing
GitHub Actions runs on every push and pull request to main:
ghcr.io/luong-komorebi/betterstack-monitor-controller on pushes to main and version tags (v*.*.*)helm lint on every change to helm/**Chart.yaml version is bumped on mainThe Docker image is published automatically; no extra secrets are needed beyond the default GITHUB_TOKEN.
This project follows Semantic Versioning (MAJOR.MINOR.PATCH) and derives the package version from Git tags.
vX.Y.Z (example: v1.2.3)pyproject.toml)Chart.yaml → version): bumped independently from the app version when chart templates or values changemain.vX.Y.Z, for example v1.2.3).helm/betterstack-monitor-controller/Chart.yaml — bump version (chart changes) and/or appVersion (app version the chart installs by default).main.The
gh-pagesbranch must exist and GitHub Pages must be enabled (Settings → Pages → Branch:gh-pages) before the first chart release.