Kavita on GKE Autopilot — Lab Guide
Overview
Estimated time: 45–90 minutes
Kavita is a fast, self-hosted digital library and reading server for comics, manga, and e-books — a web reading UI, OPDS feeds, collections, reading lists, and full-text search, built on .NET with an internal SQLite database. This lab takes you through the full operational lifecycle of the Kavita 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 Kavita 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, access the running workload, and complete the first-run setup wizard.
- Perform day-2 operations — inspect the StatefulSet and PVC, update, and manage storage.
- Observe the workload with Cloud Logging and Cloud Monitoring.
- Diagnose and resolve the most common deployment and runtime issues.
- Tear the deployment down cleanly.
Prerequisites
- Services_GCP deployed in the target project (provides the VPC, GKE Autopilot cluster, Artifact Registry, and shared service accounts this module depends on — Kavita itself needs no Cloud SQL).
- 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 Kavita (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 deploys Kavita as a StatefulSet (auto-selected because
stateful_pvc_enabled = true) into the GKE Autopilot cluster with a per-pod block PVC mounted at/kavita/config, builds the custom container image (a thin wrapper overjvmilazz0/kavita), and exposes it through the Gateway API with a reserved static IP. There is no database to provision and no init job to wait on — Kavita manages its own internal SQLite database. First deploys typically finish in 10–15 minutes. -
Connect to the cluster and discover the namespace with a name-agnostic filter:
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 kavita | head -1 | cut -d/ -f2)
echo "Cluster: $CLUSTER Namespace: $NS"
kubectl get statefulset,pvc,pods,svc -n "$NS"
Task 2 — Access & verify [Manual]
-
Confirm the pod is running and find the external address:
kubectl get pods,svc,gateway,httproute -n "$NS"
EXTERNAL_IP=$(kubectl get gateway -n "$NS" \
-o jsonpath='{.items[0].status.addresses[0].value}' 2>/dev/null)
echo "External IP: $EXTERNAL_IP" -
Confirm the service is healthy. Kavita exposes a public, unauthenticated health endpoint that returns
200once the server is serving:kubectl port-forward -n "$NS" svc/<service-name> 5000:5000 &
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:5000/api/health # expect 200Or, once the Gateway has an address, hit it directly:
curl -s -o /dev/null -w "%{http_code}\n" "http://${EXTERNAL_IP}/api/health". -
Open the service URL in a browser. On first visit Kavita's first-run setup wizard walks through creating the initial administrator account and adding your first library — there is no pre-seeded admin credential in Secret Manager. Complete this promptly: until the wizard runs, the service is reachable but unclaimed.
-
This module only persists Kavita's state directory (
/kavita/config— settings, the SQLite database, covers) on the PVC. It does not provision the actual library content. To read anything, add your owngcs_volumes(or enable NFS) pointing at your comics/manga/e-book files and register that path as a library inside the Kavita UI.
Task 3 — Operate & keep it running (Day-2) [Manual]
-
Inspect the workload and its storage:
kubectl get statefulset,pvc,pods -n "$NS"
kubectl describe statefulset -n "$NS"
kubectl describe pvc -n "$NS"
gcloud compute disks list --project="$PROJECT" --filter="name~kavita" -
Do not scale beyond one replica.
min_instance_countandmax_instance_countboth default to1. Kavita has no clustering or shared-storage coordination — running more than one replica against the same PVC is unsafe and risks corrupting the SQLite library index. -
Storage is a real block PVC by default —
stateful_pvc_enabled = true,stateful_pvc_size = "20Gi", storage classstandard-rwo(SSD-backed, drawing the project'sSSD_TOTAL_GBquota). This is deliberate: gcsfuse corrupts SQLite/media index files, so GKE's block PVC is the safer default compared with Kavita_CloudRun, which has no block-PVC option and must use GCS Fuse. If SSD quota is tight, considerstateful_pvc_storage_class = "standard"(HDD) — Kavita's I/O needs don't require SSD IOPS. -
Update the application version by changing the version input in the RAD platform and applying it via Update; a new image builds and a rolling update replaces the pod. Note
application_version = "latest"resolves to a pinnedKAVITA_VERSION = 0.8.7build argument insideKavita_Common, not the generic tag the Foundation injects — bumping the version requires editing that pinned value and rebuilding, not just redeploying. -
Inspect the (normally unmounted) storage bucket:
gcloud storage buckets list --project="$PROJECT" --filter="name~kavita"Kavita_Commonalways creates astoragebucket, but with the default block-PVC layout it exists and is not mounted — it is only mounted at/kavita/configvia GCS Fuse if you setstateful_pvc_enabled = false.
Task 4 — Observe: Logging & Monitoring [Manual]
-
Logs — from
kubectlor the Logs Explorer:kubectl logs -n "$NS" statefulset/<service-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, restart counts, and PVC disk usage. An optional uptime check against
/api/healthcan be enabled (uptime_check_config, disabled by default for this module); if enabled, review Monitoring → Uptime checks and Alerting → Policies.
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 Kavita releases.
- Pod not Ready / CrashLoopBackOff: inspect events and logs. The startup
probe targets
/api/healthwith a generous failure budget (10 attempts) to tolerate first-boot library indexing before the liveness probe takes over.kubectl describe pod -n "$NS" <pod> # Events section shows scheduling/probe/mount errors
kubectl logs -n "$NS" <pod> --previous # logs from the crashed container - PVC stuck
Pending: check forSSD_TOTAL_GBquota exhaustion (a common issue on quota-constrained projects — Kavita's defaultstandard-rwoclass is SSD-backed); switch tostateful_pvc_storage_class = "standard"(HDD) if needed. - Volume unwritable at boot: confirm
stateful_fs_group = 3000is set — Kavita runs as UID 1000/GID 2000, and an incorrect or unsetfsGroupcan leave the PVC unwritable by the container. - Library/data missing after a redeploy: confirm the PVC (not the unmounted
storagebucket) was preserved — the PVC is the entirety of Kavita's durable state under the default layout, and it survives pod restarts but not PVC deletion. - Pending pod / no external IP: check
kubectl describe podevents for resource or quota issues, and confirm the Gateway/HTTPRoute has an assigned address. - Image pull errors: confirm the image exists in Artifact Registry and the node service account can pull it.
See the Configuration Guide's Configuration Pitfalls section for
setting-specific gotchas (including why max_instance_count must stay at 1
and why enable_redis is inert for this module).
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, the block PVC holding Kavita's entire state (SQLite database,
settings, covers), and the unmounted Cloud Storage bucket. Resources owned by
Services_GCP (the VPC, GKE cluster, Artifact Registry) are managed
separately and are not removed here. Because the PVC is the library index
and reading progress, make sure you have a backup or export you care about
before deleting.
Summary
| Task | Type | Outcome |
|---|---|---|
| 1 — Deploy | Automated | Module deploys a StatefulSet with a block PVC mounted at /kavita/config; no database, no init job |
| 2 — Access & verify | Manual | Connect to the cluster; health check passes; complete the first-run setup wizard to create the admin account and first library |
| 3 — Operate | Manual | Inspect the StatefulSet/PVC, keep replicas at 1, update version, manage storage class |
| 4 — Observe | Manual | Query Cloud Logging; review Cloud Monitoring metrics and optional uptime check |
| 5 — Troubleshoot | Manual | Diagnose pod, PVC/quota, fsGroup, scheduling, and image-pull issues |
| 6 — Tear down | Automated | Delete (Trash) removes all module resources, including the PVC holding Kavita's entire state |