Uptime Kuma on GKE Autopilot — Lab Guide
Overview
Estimated time: 45–90 minutes
Uptime Kuma is a self-hosted uptime monitoring tool for websites, APIs, TCP ports, and DNS records, with status pages and 90+ notification channels. This lab takes you through the full operational lifecycle of the Uptime Kuma on GKE Autopilot module on Google Cloud: deploy it, access and verify it, run it day-to-day, observe it, diagnose common problems, and tear it down.
The lab focuses on operating the GKE module and the Google Cloud platform, not on Uptime Kuma product features. For the complete list of provisioned services and every configuration input (organised by group), see the Configuration Guide — this lab deliberately does not duplicate that detail so it stays accurate over time.
Objectives
By the end of this lab you will be able to:
- Deploy the module from the RAD platform and locate the resources it provisions.
- Connect to the GKE cluster, find the Uptime Kuma workload, and verify it is healthy.
- Explain why this module has no Cloud SQL instance and no Secret Manager entries, and why all durable state lives on a single Filestore (NFS) share.
- Perform day-2 operations — inspect the workload, understand why it must stay at a single replica, update the version, and check the NFS-backed data.
- Observe the workload with Cloud Logging and Cloud Monitoring.
- Diagnose and resolve the most common deployment and runtime issues, including the known SQLite-over-NFS limitation.
- Tear the deployment down cleanly.
Prerequisites
- Services_GCP deployed in the target project (provides the VPC, GKE Autopilot cluster, Filestore/NFS networking, Artifact Registry, and shared service accounts this module depends on).
- A Google Cloud project with billing enabled.
- gcloud CLI and kubectl installed;
gcloud auth loginandgcloud auth application-default logincompleted. - Project Owner (or equivalent) IAM on the project.
- RAD platform access with permission to deploy modules into the project.
Set these shell variables once; every task below reuses them:
export PROJECT="<your-gcp-project-id>"
export REGION="us-central1" # the region you deploy into
Task 1 — Deploy the module [Automated]
-
Click Deploy in the RAD platform top navigation, open UptimeKuma (GKE) from the Platform Modules list to start configuration, set
project_id, and review the inputs. Configure only what you need — the Configuration Guide documents every input by group, with defaults. Review the estimated cost (if credits are enabled) and click Deploy, which opens the deployment status page with real-time logs. -
The platform schedules the workload onto the GKE Autopilot cluster, provisions no Cloud SQL instance (
database_type = "NONE"), creates no Secret Manager entries, and runs no database-initialisation job — Uptime Kuma stores everything in an embedded SQLite database that it creates itself on first boot. The platform mounts a Filestore (NFS) share at the fixed container path/app/dataand mirrors the officiallouislam/uptime-kumaimage into Artifact Registry (enable_image_mirroring = true). Because there is no database to provision, this deploy is dominated by Autopilot node scheduling and the NFS mount rather than by Cloud SQL creation — expect it to complete noticeably faster than database-backed GKE modules. -
Connect to the cluster and discover the namespace with name-agnostic filters:
CLUSTER=$(gcloud container clusters list --project="$PROJECT" --format="value(name)" --limit=1)
gcloud container clusters get-credentials "$CLUSTER" --region="$REGION" --project="$PROJECT"
NS=$(kubectl get ns -o name | grep uptimekuma | head -1 | cut -d/ -f2)
echo "Cluster: $CLUSTER Namespace: $NS"
kubectl get all -n "$NS"
Task 2 — Access & verify [Manual]
-
Confirm the workload is running and find its external address:
kubectl get pods,svc -n "$NS"
EXTERNAL_IP=$(kubectl get svc -n "$NS" \
-o jsonpath='{.items[?(@.spec.type=="LoadBalancer")].status.loadBalancer.ingress[0].ip}')
echo "External IP: $EXTERNAL_IP" -
Confirm the service is healthy. Both the startup and liveness probes are a plain HTTP
GET /on port3001— Uptime Kuma's native port:curl -s -o /dev/null -w "%{http_code}\n" "http://${EXTERNAL_IP}/" # expect 200The startup probe is deliberately generous (30 s initial delay, 10 s period, 30 failure threshold — up to several minutes), which covers first-boot SQLite schema creation on a fresh NFS volume.
-
Open
http://${EXTERNAL_IP}in a browser. On first access Uptime Kuma serves its setup wizard — create the admin account immediately. There is no default or auto-generated credential in Secret Manager to retrieve:gcloud secrets list --project="$PROJECT" --filter="name~uptimekuma" # expect emptyUntil the admin account exists, the setup page is reachable by anyone who finds the external IP — do not leave a freshly deployed instance unconfigured for long.
-
Add a first monitor (e.g. an HTTPS check against a site you own) and watch it turn green.
Task 3 — Operate & keep it running (Day-2) [Manual]
-
Inspect the workload — deployment, pods, and the NFS-backed PVC:
kubectl get deploy,pods,pvc -n "$NS"
kubectl describe deploy -n "$NS" -
Do not scale beyond one replica. Both
min_instance_countandmax_instance_countdefault to1and should stay there — Uptime Kuma's embedded SQLite database is single-writer, and every pod would otherwise share the same NFS-mounted database file. Unlike modules that scale via a Redis-backed queue, there is no supported way to run more than one Uptime Kuma replica against the same data volume. -
Update the application version by changing the version input in the RAD platform and applying it via Update. Because this module is NFS-backed, the Foundation sets the Deployment's update strategy to
Recreaterather than the defaultRollingUpdate— the running pod is fully terminated before the replacement starts, rather than surging a second pod that would otherwise write the same SQLite file concurrently. Expect a short gap in monitoring coverage during the update, not a seamless rolling replace. -
Confirm there are no secrets or init jobs to manage — this module creates neither:
kubectl get secrets -n "$NS"
gcloud secrets list --project="$PROJECT" --filter="name~uptimekuma" # expect empty
kubectl get jobs -n "$NS" # expect none by default -
Verify the persistent state lives on Filestore (NFS), not ephemeral disk, and back it up:
gcloud filestore instances list --project="$PROJECT"
kubectl exec -n "$NS" deploy/"$(kubectl get deploy -n "$NS" -o jsonpath='{.items[0].metadata.name}')" -- ls -la /app/dataExport a backup from the Uptime Kuma UI (Settings → Backup) periodically, in addition to any Filestore snapshot policy.
Known limitation — SQLite over NFS. Uptime Kuma's embedded SQLite database defaults to WAL (write-ahead log) journal mode, which relies on shared memory-mapped file locking. NFS does not reliably support that locking model, so
SQLITE_CORRUPTerrors can surface even when the Filestore/NFS mount itself is correctly configured — this is a limitation of running SQLite's WAL mode over NFS, not a sign of NFS misconfiguration, and it is not fully resolved by keeping the replica count at 1. If you rely on long-term monitor history, treat the UI export/backup step above as load-bearing rather than optional. The practical workarounds if you hit corruption are to switch Uptime Kuma toDELETEjournal mode (trades some write performance for NFS-safe locking) or to restore from the most recent backup / reset the database file and let Uptime Kuma recreate its schema (losing monitor history since the last backup).
Task 4 — Observe: Logging & Monitoring [Manual]
-
Logs — from
kubectlor the Logs Explorer:kubectl logs -n "$NS" deploy/"$(kubectl get deploy -n "$NS" -o jsonpath='{.items[0].metadata.name}')" --tail=50Logs Explorer filter:
resource.type="k8s_container" AND resource.labels.namespace_name="<namespace>". -
Monitoring — open the GKE / Kubernetes dashboards and review pod CPU and memory utilisation and restart counts (a restarting pod is the fastest signal that monitoring coverage has gaps). The module can optionally provision a Cloud Monitoring uptime check against Uptime Kuma's own
GET /endpoint (uptime_check_config.enabled = falseby default) — an outside-in signal that your monitoring system is itself up. This is separate from the monitors you configure inside the Uptime Kuma UI.
Task 5 — Troubleshoot & debug [Manual]
Durable techniques for the failure modes you are most likely to hit. These are platform-level diagnostics and do not change with Uptime Kuma releases.
- Pod not Ready / CrashLoopBackOff: inspect events and logs. The liveness
probe is HTTP
GET /on port3001; a workload that never opens that port will fail Ready.kubectl describe pod -n "$NS" <pod> # Events section shows scheduling/probe/mount errors
kubectl logs -n "$NS" <pod> --previous # logs from the crashed container - Pending pod / NFS not mounting: confirm the Filestore instance is
READYand the PVC isBoundbefore assuming an application bug:gcloud filestore instances list --project="$PROJECT"
kubectl get pvc -n "$NS"
kubectl describe pod -n "$NS" <pod> # look for mount errors in Events - "database is corrupted" / SQLITE_CORRUPT in logs: see the Task 3 note above
— this is a known limitation of SQLite's default WAL journal mode running over
NFS, and can occur independently of whether the NFS mount is correctly
configured. It is not necessarily fixed by re-checking the Filestore setup.
Restore from the most recent Uptime Kuma backup, or delete the SQLite file
under
/app/dataand let Uptime Kuma recreate its schema on next boot (monitor history is lost back to the last backup). Switching toDELETEjournal mode inside the container reduces the risk going forward. - Monitors show gaps in history: confirm the pod has not been restarting
(
kubectl get pods -n "$NS"restart count) and that only one replica is running —max_instance_countabove1risks lock contention on the shared SQLite file, which can itself present as gaps or corruption. - No external IP: check
kubectl describe podevents for resource or quota issues, and confirm the LoadBalancer Service has an assigned IP (kubectl get svc -n "$NS"). - Image pull errors: this module deploys the prebuilt, mirrored
louislam/uptime-kumaimage — confirm the mirrored copy exists in Artifact Registry and the node service account can pull it; there is no Cloud Build step to debug since the image is not custom-built. - Update seems to cause downtime: expected — see the
Recreatestrategy note in Task 3.3. This is a deliberate trade-off to avoid two pods writing the SQLite file at once, not a stuck rollout.
See the Configuration Guide's Configuration Pitfalls section for
setting-specific gotchas (including why enable_nfs, nfs_mount_path, and
max_instance_count are marked Critical/High risk).
Task 6 — Tear down [Automated]
On the Deployments page, open the deployment and click the Trash icon (Delete). Delete runs terraform destroy and is irreversible (the deployment record is retained for history). If a deployment is stuck and the RAD platform can no longer manage it (for example after manual changes that conflict with the Terraform state), use Purge instead — it removes the deployment from RAD's records without destroying the cloud resources (it makes RAD forget the project). This removes everything the module created — the Kubernetes workload
and namespace, and the Filestore NFS share (including the SQLite database with all
monitors and history — export a backup first if you want to keep them). There is no
Cloud SQL instance, Secret Manager secret, or GCS bucket to clean up by default.
Resources owned by Services_GCP (the VPC, GKE cluster, shared Filestore
networking, registry) are managed separately and are not removed here.
Summary
| Task | Type | Outcome |
|---|---|---|
| 1 — Deploy | Automated | Module deploys the GKE workload with a Filestore NFS mount at /app/data and mirrors the prebuilt image — no Cloud SQL, secrets, or init jobs |
| 2 — Access & verify | Manual | Connect to the cluster; health check passes; create the initial admin account on the first-run setup page |
| 3 — Operate | Manual | Inspect workload, keep replica count at 1, update version (Recreate strategy), verify and back up NFS-backed SQLite state |
| 4 — Observe | Manual | Query Cloud Logging; review pod restarts and CPU/memory; optionally monitor the monitor |
| 5 — Troubleshoot | Manual | Diagnose pod, NFS-mount, SQLite-over-NFS corruption, scheduling, and image-pull issues |
| 6 — Tear down | Automated | Delete (Trash) removes all module resources |