kuma-operator
A Kubernetes operator that syncs Uptime Kuma monitors, notification providers, and tags as Kubernetes custom resources. Define your monitoring setup as code alongside your services — the operator creates, updates, and deletes everything in Uptime Kuma automatically.
Overview
The operator manages three custom resource types:
| Resource |
Short name |
Purpose |
UptimeKumaMonitor |
ukm |
A monitor (HTTP, ping, TCP, DNS, …) |
UptimeKumaNotification |
— |
A notification provider (Discord, Slack, webhook, …) |
UptimeKumaTag |
— |
A tag for labelling monitors |
Monitors reference notification providers and tags by name, so you can declare everything in the same namespace as your service.
Installation
helm install kumaste oci://harbor.wuastbude.de/kumaste/kumaste \
--namespace monitoring \
--create-namespace \
--set uptimeKuma.url=http://uptimekuma.monitoring.svc:3001 \
--set uptimeKuma.username=admin \
--set uptimeKuma.password=secret
Or with an existing Secret:
helm install kumaste oci://harbor.wuastbude.de/kumaste/kumaste \
--namespace monitoring \
--create-namespace \
--set uptimeKuma.existingSecret=my-uptimekuma-creds
The Secret must contain the keys UPTIME_KUMA_URL, UPTIME_KUMA_USERNAME, and UPTIME_KUMA_PASSWORD.
Helm values
| Value |
Default |
Description |
uptimeKuma.url |
"" |
Required. URL of the Uptime Kuma instance |
uptimeKuma.username |
"" |
Admin username |
uptimeKuma.password |
"" |
Admin password |
uptimeKuma.existingSecret |
"" |
Use a pre-existing Secret instead |
syncInterval |
60s |
How often to re-reconcile for drift detection |
watchNamespaces |
[] |
Namespaces to watch; empty = cluster-wide |
replicaCount |
1 |
Number of operator replicas |
resources |
{} |
CPU/memory requests and limits |
UptimeKumaMonitor
Manages a single monitor in Uptime Kuma.
apiVersion: monitoring.wuastbude.de/v1alpha1
kind: UptimeKumaMonitor
metadata:
name: myapp
namespace: myapp-ns
spec:
type: http
url: "https://myapp.example.com"
interval: 60
Common fields
| Field |
Default |
Description |
name |
metadata.name |
Display name in Uptime Kuma |
description |
"" |
Optional description |
type |
http |
Monitor type (see below) |
active |
true |
Whether the monitor runs |
upsideDown |
false |
Invert status logic |
interval |
60 |
Check interval in seconds (min 20) |
retryInterval |
60 |
Retry interval when down (min 20) |
maxRetries |
1 |
Retries before marking down |
resendInterval |
0 |
Re-notify every N failed checks (0 = off) |
timeout |
null |
Request timeout in seconds |
parentName |
"" |
Display name of a group monitor to nest under |
notifications |
[] |
Notification provider names to attach |
tags |
[] |
Tags to attach (name + optional value) |
Monitor types
http — HTTP/HTTPS endpoint
spec:
type: http
url: "https://example.com"
method: GET # GET POST PUT PATCH DELETE HEAD OPTIONS
acceptedStatuscodes: ["200-299"]
maxRedirects: 10
ignoreTls: false
expiryNotification: false # alert on TLS cert expiry
domainExpiryNotification: false
cacheBust: false
saveResponse: false
saveErrorResponse: false
ipFamily: 0 # 0=auto 4=IPv4 6=IPv6
# HTTP Basic Auth
basicAuthUser: ""
basicAuthPass: ""
# Bearer token auth
authMethod: bearer
bearerToken: ""
# Custom request body
httpBody: '{"key":"value"}'
httpBodyEncoding: json # json xml x-www-form-urlencoded form-data text
# Custom headers (map)
httpHeaders:
X-API-Key: secret
Accept: application/json
keyword — HTTP + keyword matching
All http fields apply, plus:
spec:
type: keyword
url: "https://example.com"
keyword: "OK"
invertKeyword: false # true = alert when keyword IS found
json-query — HTTP + JSONPath assertion
All http fields apply, plus:
spec:
type: json-query
url: "https://api.example.com/health"
jsonPath: "$.status"
jsonPathOperator: "=="
expectedValue: "ok"
ping — ICMP ping
spec:
type: ping
hostname: "1.1.1.1"
packetSize: 56
tcp — TCP port check
spec:
type: tcp
hostname: "db.example.com"
port: 5432
dns — DNS record check
spec:
type: dns
hostname: "example.com"
dnsResolveType: A # A AAAA MX NS CNAME PTR TXT SOA CAA
dnsResolveServer: "1.1.1.1"
expectedValue: "93.184.216.34"
push — Push monitor (heartbeat)
The operator creates the monitor; your service pings its push URL periodically.
spec:
type: push
interval: 60
docker — Docker container state
spec:
type: docker
dockerContainer: "my-container-name"
dockerHostID: 1 # Docker host configured in Uptime Kuma
mqtt — MQTT topic
spec:
type: mqtt
hostname: "mqtt.example.com"
port: 1883
mqttTopic: "home/sensor/temperature"
mqttSuccessMessage: "ok"
mqttUsername: "user"
mqttPassword: "pass"
smtp — SMTP connectivity
spec:
type: smtp
hostname: "smtp.example.com"
port: 587
smtpSecurity: STARTTLS # STARTTLS TLS NONE
grpc-keyword — gRPC method response keyword
spec:
type: grpc-keyword
grpcUrl: "grpc.example.com:443"
grpcProtobuf: |
syntax = "proto3";
service Health { rpc Check(Request) returns (Response); }
message Request {}
message Response { string status = 1; }
grpcServiceName: Health
grpcMethod: Check
grpcBody: "{}"
grpcEnableTls: true
keyword: "SERVING"
sqlserver / postgres / mysql / mongodb / redis — Database monitors
spec:
type: postgres
databaseConnectionString: "postgres://user:pass@db.example.com:5432/mydb"
databaseQuery: "SELECT 1" # optional query to execute
kafka-producer — Kafka producer test
spec:
type: kafka-producer
kafkaProducerTopic: "health-check"
kafkaProducerBrokers: ["kafka.example.com:9092"]
kafkaProducerMessage: "ping"
kafkaProducerSsl: false
kafkaProducerAllowAutoTopicCreation: false
kafkaProducerSaslOptions:
mechanism: plain
username: user
password: pass
rabbit-mq — RabbitMQ management API
spec:
type: rabbit-mq
rabbitmqNodes: ["https://rabbitmq.example.com:15672"]
rabbitmqUsername: admin
rabbitmqPassword: secret
snmp — SNMP OID poll
spec:
type: snmp
hostname: "switch.example.com"
snmpVersion: "2c"
snmpOid: "1.3.6.1.2.1.1.1.0"
expectedValue: "Linux"
radius — RADIUS authentication test
spec:
type: radius
hostname: "radius.example.com"
port: 1812
radiusUsername: testuser
radiusPassword: testpass
radiusSecret: sharedsecret
radiusCalledStationId: "00-11-22-33-44-55"
radiusCallingStationId: "66-77-88-99-AA-BB"
gamedig — Game server query
spec:
type: gamedig
hostname: "gameserver.example.com"
port: 27015
game: tf2
gamedigToken: ""
real-browser — Headless browser check
spec:
type: real-browser
url: "https://example.com"
remoteBrowser: "my-remote-browser-id"
group — Monitor group
Groups do not perform checks; they serve as containers for other monitors.
spec:
type: group
name: "Production Services"
Nest monitors inside a group via parentName:
spec:
type: http
url: "https://api.example.com"
parentName: "Production Services"
manual — Manually controlled status
spec:
type: manual
manualStatus: 1 # 1=up 0=down
Tags
Attach tags by name with an optional value string:
spec:
tags:
- name: environment
value: production
- name: team
The tag must exist (created via UptimeKumaTag) before a monitor can reference it.
Notifications
Attach notification providers by name:
spec:
notifications:
- discord-alerts
- pagerduty-critical
The providers must exist (created via UptimeKumaNotification) before the monitor is reconciled.
Status
status:
monitorID: 42
conditions:
- type: Synced
status: "True"
reason: MonitorSynced
message: "Monitor successfully synced to Uptime Kuma"
lastTransitionTime: "2025-01-01T00:00:00Z"
UptimeKumaNotification
Manages a notification provider in Uptime Kuma.
apiVersion: monitoring.wuastbude.de/v1alpha1
kind: UptimeKumaNotification
metadata:
name: discord-alerts
namespace: monitoring
spec:
type: discord
active: true
config:
discordWebhookUrl: "https://discord.com/api/webhooks/123/abc"
discordUsername: "Uptime Kuma"
Fields
| Field |
Description |
name |
Display name in Uptime Kuma (defaults to metadata.name) |
type |
Provider type (see below) |
active |
Whether this provider is enabled |
isDefault |
Auto-attach to newly created monitors |
config |
Provider-specific key-value pairs (flat map) |
Provider types and config keys
All config values are sent flat to the Uptime Kuma API at the same level as name and type. The keys must match Uptime Kuma's internal field names exactly.
discord
| Key |
Description |
discordWebhookUrl |
Discord webhook URL |
discordUsername |
Bot display name |
discordPrefixMessage |
Message prefix text |
slack
| Key |
Description |
slackwebhookURL |
Slack incoming webhook URL |
slackusername |
Bot username |
slackchannel |
Channel (e.g. #alerts) |
slackiconemo |
Emoji icon (e.g. :white_check_mark:) |
telegram
| Key |
Description |
telegramBotToken |
Bot API token |
telegramChatID |
Chat or group ID |
telegramSendSilently |
"true" to suppress notification sound |
telegramProtectContent |
"true" to protect from forwarding |
webhook
| Key |
Description |
webhookURL |
Target URL |
webhookContentType |
json or form |
webhookAdditionalHeaders |
JSON string of extra headers |
smtp
| Key |
Description |
smtpHost |
SMTP server hostname |
smtpPort |
SMTP port |
smtpSecure |
"true" for TLS |
smtpUsername |
Auth username |
smtpPassword |
Auth password |
smtpTo |
Recipient address(es) |
smtpFrom |
Sender address |
smtpFromName |
Sender display name |
gotify
| Key |
Description |
gotifyserverurl |
Gotify server URL |
gotifyapplicationToken |
Application token |
gotifyPriority |
Message priority (integer string) |
ntfy
| Key |
Description |
ntfyserverurl |
ntfy server URL |
ntfytopic |
Topic name |
ntfyPriority |
Priority (1–5) |
ntfyAuthenticationMethod |
none, username, or accesstoken |
ntfyusername |
Username (if method=username) |
ntfypassword |
Password (if method=username) |
ntfyaccesstoken |
Access token (if method=accesstoken) |
pushover
| Key |
Description |
pushoveruserkey |
Pushover user key |
pushoverapikey |
Application API key |
pushoversounds |
Notification sound |
pushoverpriority |
Priority (-2 to 2) |
matrix
| Key |
Description |
matrixHomeserverUrl |
Matrix homeserver URL |
matrixAccessToken |
Access token |
matrixInternalRoomId |
Room ID |
mattermost
| Key |
Description |
mattermostwebhookUrl |
Incoming webhook URL |
mattermostusername |
Bot username |
mattermostchannel |
Channel name |
| Key |
Description |
pagerdutyIntegrationKey |
Integration (routing) key |
pagerdutyAutoResolve |
"true" to auto-resolve incidents |
opsgenie
| Key |
Description |
opsgenieApiKey |
OpsGenie API key |
opsgenieRegion |
us or eu |
opsgeniePriority |
Priority (P1–P5) |
Examples
# Webhook
apiVersion: monitoring.wuastbude.de/v1alpha1
kind: UptimeKumaNotification
metadata:
name: generic-webhook
spec:
type: webhook
active: true
config:
webhookURL: "https://hooks.example.com/alert"
webhookContentType: json
---
# Telegram
apiVersion: monitoring.wuastbude.de/v1alpha1
kind: UptimeKumaNotification
metadata:
name: telegram-ops
spec:
type: telegram
active: true
isDefault: true
config:
telegramBotToken: "1234567890:AAFakeToken"
telegramChatID: "-1001234567890"
Status
status:
notificationID: 3
conditions:
- type: Synced
status: "True"
UptimeKumaTag
Manages a tag in Uptime Kuma. Tags can be attached to monitors with an optional value.
apiVersion: monitoring.wuastbude.de/v1alpha1
kind: UptimeKumaTag
metadata:
name: environment
namespace: monitoring
spec:
color: "#3498DB"
Fields
| Field |
Description |
name |
Tag name in Uptime Kuma (defaults to metadata.name) |
color |
Hex colour string, e.g. #FF5733 |
Status
status:
tagID: 1
conditions:
- type: Synced
status: "True"
Full example
# 1. Create a tag
apiVersion: monitoring.wuastbude.de/v1alpha1
kind: UptimeKumaTag
metadata:
name: environment
namespace: monitoring
spec:
color: "#27AE60"
---
# 2. Create a notification provider
apiVersion: monitoring.wuastbude.de/v1alpha1
kind: UptimeKumaNotification
metadata:
name: discord-ops
namespace: monitoring
spec:
type: discord
active: true
config:
discordWebhookUrl: "https://discord.com/api/webhooks/123/abc"
discordUsername: "Uptime Kuma"
---
# 3. Create a monitor group
apiVersion: monitoring.wuastbude.de/v1alpha1
kind: UptimeKumaMonitor
metadata:
name: production-group
namespace: monitoring
spec:
type: group
name: "Production"
---
# 4. Create a monitor inside the group with tag and notification
apiVersion: monitoring.wuastbude.de/v1alpha1
kind: UptimeKumaMonitor
metadata:
name: myapp
namespace: myapp-ns
spec:
type: http
url: "https://myapp.example.com"
interval: 60
parentName: "Production"
notifications:
- discord-ops
tags:
- name: environment
value: production
Development
# Run unit tests
just test
# Run e2e tests (requires Docker)
just test-e2e
# Lint
just lint
# Regenerate CRD manifests and deepcopy after editing API types
just generate
# Build binary
just build
# Build Docker image
just docker-build
# Render Helm templates
just helm-template
E2E test environment variables
| Variable |
Default |
Description |
E2E_UPTIME_KUMA_URL |
— |
Uptime Kuma instance URL (required to run e2e tests) |
E2E_UPTIME_KUMA_USERNAME |
admin |
Admin username |
E2E_UPTIME_KUMA_PASSWORD |
testpassword123 |
Admin password |