Gatus
A self-hosted uptime and health monitoring dashboard.
Gatus is an open-source health dashboard that periodically checks HTTP, TCP, and DNS endpoints and displays uptime status. It stores historical data in PostgreSQL and supports alerting via various channels. Self-hosting means no usage limits and full control over which internal services are monitored.
Alternatives considered
Cloud Hosted
| Tool | Open Source | Free Tier | Monthly Cost |
|---|---|---|---|
| UptimeRobot | No | Limited | From $7/mo |
| Pingdom | No | No | From $15/mo |
Self Hosted
| Tool | Open Source | Full Features | Notes |
|---|---|---|---|
| Uptime Kuma | Yes | Yes | Simpler UI, less config flexibility |
Installation
Architecture
- Deployment: Single
gatusdeployment in thegatusnamespace; NetBird sidecar for VPN connectivity - Images:
ghcr.io/twin/gatus,netbirdio/netbird, both digest-pinned via Renovate - Database: CNPG PostgreSQL cluster for uptime history
- Config: Multiple ConfigMaps with endpoint definitions (arr services, internal tools, public web services)
- Networking: HTTPRoute via public gateway
Security
- Gatus container:
readOnlyRootFilesystem: true,allowPrivilegeEscalation: false, capabilities dropped - NetBird sidecar adds
NET_ADMINandNET_RAWon top of drop-all (VPN tunnel; NetBird 0.75+ opens a raw socket when bringing up the interface). It is not privileged runAsNonRoot: falseon the pod level (namespace PSS set to privileged)
Updates
Managed by Renovate. Both images are digest-pinned.
Data Management
- Database: CNPG PostgreSQL cluster (Longhorn-encrypted PVCs) for uptime history
- Backups: k8up
Schedules to Hetzner and Backblaze via restic (workload-talos-gatus-f4b5c644), covering the database dump from thebackups-cnpgPreBackupPod. Retention: last 5, 7 daily, 4 weekly, 12 monthly, 5 yearly.
User Management
OIDC configured — OPENID_CLIENT_ID and OPENID_CLIENT_SECRET injected from SOPS secret. Issuer and redirect URL set in the Gatus config ConfigMap.
Configuration Management
- Endpoint definitions from multiple ConfigMaps (arr services, internal tools, public web)
- OIDC credentials from SOPS-encrypted secret
- Database connection string from CNPG-generated secret
Usage
View the uptime dashboard to see current status of all monitored endpoints grouped by category (Arr, Int, Web, Talos). Configure new endpoints by editing the ConfigMap YAML files. Alerts can be triggered via the configured channels when checks fail.
Cluster-specific deviations from the above live in the per-cluster README — see k8s/apps/gatus/talos/README.md.
Cluster Deployment
Gatus — Talos cluster
- Image:
ghcr.io/twin/gatus:v5.37.0@sha256:094eb186e55235db367e90e9d56140e5897b7044c41de23cde4cc18e6da1242e - Image:
netbirdio/netbird:0.81.2@sha256:6242a1fbaf6b611c00c26a914cbbea512c30c18a45de0646ce7b0e5d93a21e67
Rendered manifests (kustomize build)
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kustomize.toolkit.fluxcd.io/force: enabled
labels:
app.kubernetes.io/name: gatus
name: gatus
namespace: gatus
spec:
replicas: 1
revisionHistoryLimit: 2
selector:
matchLabels:
app.kubernetes.io/name: gatus
strategy:
type: Recreate
template:
metadata:
labels:
app.kubernetes.io/name: gatus
spec:
automountServiceAccountToken: false
containers:
- env:
- name: GATUS_CONFIG_PATH
value: /config
- name: POSTGRES_PATH
valueFrom:
secretKeyRef:
key: uri
name: cnpg-app
- name: SMTP_HOST
valueFrom:
secretKeyRef:
key: host
name: allinkl-smtp-credentials
- name: SMTP_PORT
valueFrom:
secretKeyRef:
key: port
name: allinkl-smtp-credentials
- name: SMTP_USERNAME
valueFrom:
secretKeyRef:
key: username
name: allinkl-smtp-credentials
- name: SMTP_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: allinkl-smtp-credentials
- name: SMTP_FROM
valueFrom:
secretKeyRef:
key: from
name: allinkl-smtp-credentials
envFrom:
- secretRef:
name: gatus
image: ghcr.io/twin/gatus:v5.37.0@sha256:094eb186e55235db367e90e9d56140e5897b7044c41de23cde4cc18e6da1242e
livenessProbe:
failureThreshold: 5
httpGet:
path: /
port: 8080
initialDelaySeconds: 15
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 5
name: gatus
ports:
- containerPort: 8080
name: web
protocol: TCP
readinessProbe:
failureThreshold: 5
httpGet:
path: /
port: 8080
initialDelaySeconds: 15
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 5
resources:
limits:
memory: 128Mi
requests:
cpu: 25m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
startupProbe:
failureThreshold: 5
httpGet:
path: /
port: 8080
initialDelaySeconds: 15
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 5
volumeMounts:
- mountPath: /config
name: gatus-config
readOnly: true
initContainers:
- env:
- name: NB_SETUP_KEY
valueFrom:
secretKeyRef:
key: NB_SETUP_KEY
name: gatus-netbird
image: netbirdio/netbird:0.81.2@sha256:6242a1fbaf6b611c00c26a914cbbea512c30c18a45de0646ce7b0e5d93a21e67
name: sidecar-netbird
resources:
limits:
memory: 128Mi
requests:
cpu: 25m
memory: 64Mi
restartPolicy: Always
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_ADMIN
- NET_RAW
drop:
- ALL
startupProbe:
exec:
command:
- sh
- -c
- netbird status | grep -q Connected
failureThreshold: 30
periodSeconds: 2
priorityClassName: tier-2
volumes:
- configMap:
name: gatus-config-594c4k9588
name: gatus-config