Cloudreve on GKE Autopilot
Cloudreve is a popular open-source, self-hosted cloud storage and file-sharing platform written in Go. It provides a web UI for uploading, organising, previewing and sharing files, with pluggable storage back-ends. This module deploys Cloudreve on GKE Autopilot on top of the App_GKE foundation, which provisions and manages the shared Google Cloud and Kubernetes infrastructure.
This guide focuses on the cloud services Cloudreve uses and how to explore and operate them from the Google Cloud Console and the command line. For the mechanics that are common to every GKE application — Workload Identity, ingress, autoscaling, CI/CD, Cloud Armor, IAP, Binary Authorization, VPC Service Controls, backups, and the deployment lifecycle — refer to the App_GKE foundation guide rather than repeating them here.
1. Overview
Cloudreve runs as a single Go binary serving both the web UI and file storage API. The deployment wires together a focused set of Google Cloud services:
| Capability | Google Cloud service | Notes |
|---|---|---|
| Compute | GKE Autopilot | Single Go binary on port 5212, StatefulSet by default |
| Persistence | Block Persistent Volume (via stateful_pvc_enabled) | Mounted at /cloudreve; holds the embedded SQLite DB, conf.ini, and uploaded files |
| Object storage | Cloud Storage | A storage bucket provisioned automatically, but only wired in as a GCS FUSE mount when the block PVC is disabled |
| Database | None | Cloudreve uses an embedded SQLite database on the block volume — no Cloud SQL instance is created |
| Secrets | Secret Manager | None created — the first-run admin password is generated by Cloudreve itself and printed to container logs |
| Ingress | Kubernetes Gateway / Cloud Load Balancing | Custom domain enabled by default; static IP reserved by default |
Sensible defaults worth knowing up front:
- No SQL database.
database_typeis fixed toNONEbyCloudreve_Common; every Cloud SQL-related variable is forwarded to the foundation only for interface compatibility and has no effect. stateful_pvc_enabled = trueby default — required, not optional. Cloudreve's embedded SQLite database and local file storage live together in its working directory,/cloudreve. A GCS FUSE mount there would corrupt SQLite's file locking, so the module defaults to a block PVC (StatefulSet) instead;workload_typeneed not be set separately.- Binary/data co-location is handled in the image build. Upstream's
cloudreve/cloudreveimage keeps both thecloudrevebinary and its data in/cloudreve. This module's Dockerfile relocates the binary to/usr/local/bin/cloudrevein a multi-stage build (ENTRYPOINT ["/usr/local/bin/cloudreve"],WORKDIR /cloudreve), so mounting the PVC at/cloudreveonly shadows data files, never the binary. See Section 3. - GCS volume and block PVC are mutually exclusive at the same path. The
variant only mounts the auto-created
storagebucket at/cloudreve(enable_gcs_storage_volume = !stateful_pvc_enabled) when the block PVC is off, avoiding a double-mount conflict — with the default stateful config, that bucket exists but is not mounted into the pod. - No Cloud SQL, no Redis.
enable_cloudsql_volumedefaultsfalseandenable_redisis explicitly overridden tofalseinmain.tf. - No injectable admin secret. Cloudreve generates its own initial admin
password on first boot and prints it to container logs — no Secret
Manager secret is created;
secret_ids/secret_valuesare empty maps. - Single replica by default.
min_instance_count = max_instance_count = 1, matching Cloudreve's lack of distributed/multi-node clustering support. enable_pod_disruption_budgetdefaultstrue(unusual among app modules),pdb_min_available = "1", protecting the single stateful pod.- Custom domain and static IP are on by default
(
enable_custom_domain = true,reserve_static_ip = true), unlike many other application modules where these default off.
2. Google Cloud Services & How to Explore Them
All commands assume you have run
gcloud container clusters get-credentials <cluster> --region $REGION --project $PROJECT
and that PROJECT, REGION, and NAMESPACE are set. The namespace and other
identifiers are reported in the deployment Outputs.
A. GKE Autopilot — the Cloudreve workload
Cloudreve pods are scheduled on Autopilot, which bills for the CPU/memory the
pod actually requests. Because stateful_pvc_enabled = true by default, the
workload is a StatefulSet with a single stable pod identity and a per-pod
block PVC, not a Deployment.
- Console: Kubernetes Engine → Workloads → filter for the Cloudreve workload for pods, revisions, and events. Kubernetes Engine → Services & Ingress shows the external IP / Gateway route.
- CLI:
kubectl get statefulsets,pods,svc -n "$NAMESPACE"
kubectl logs -n "$NAMESPACE" statefulset/<service-name> --tail=100
kubectl describe pod -n "$NAMESPACE" -l app=<service-name>
See App_GKE for how Autopilot, scaling, and the Deployment-vs-StatefulSet workload type decision are managed.
B. Block Persistent Volume (StatefulSet PVC)
Cloudreve's embedded SQLite database (cloudreve.db), generated conf.ini,
and uploaded files all live under /cloudreve, the container's working
directory. stateful_pvc_enabled = true provisions a per-pod block PVC
(stateful_pvc_storage_class = standard-rwo by default, an SSD-backed
Balanced PD) mounted at that path — a GCS FUSE mount here would break
SQLite's file-locking semantics, so the block device is mandatory.
- Console: Kubernetes Engine → Storage → filter for the Cloudreve PVC; Compute Engine → Disks shows the underlying Persistent Disk.
- CLI:
kubectl get pvc -n "$NAMESPACE"
kubectl describe pvc -n "$NAMESPACE" -l app=<service-name>
gcloud compute disks list --project "$PROJECT" --filter="name~cloudreve"
Remember that scaling to zero (kubectl scale --replicas=0) frees CPU/memory
but keeps the PVC — only deleting the PVC (or the namespace) releases the
SSD quota it draws. See App_GKE for the StatefulSet / PVC group
mechanics and the SSD-vs-HDD storage-class trade-off.
C. Cloud Storage
A storage Cloud Storage bucket is created automatically by
Cloudreve_Common, but it is only mounted into the pod as a GCS FUSE volume
when stateful_pvc_enabled = false (avoiding a double-mount at /cloudreve
with the block PVC). With the default stateful configuration, the bucket
exists but sits unused unless you add it as an explicit gcs_volumes entry
at a different mount path.
- Console: Cloud Storage → Buckets → filter for the deployment's
storagesuffix. - CLI:
gcloud storage buckets list --project "$PROJECT" --filter="name~storage"
kubectl get pvc,pv -n "$NAMESPACE"
See App_GKE for CMEK options and the GCS Fuse CSI driver.
D. Secret Manager
Cloudreve_Common creates no Secret Manager secrets for Cloudreve
itself — the first-run admin password is generated internally by Cloudreve
and printed to container logs on first boot, not stored in Secret Manager.
Any secrets you configure via secret_environment_variables are still
projected through the standard Secret Store CSI mechanism.
- Console: Security → Secret Manager.
- CLI:
gcloud secrets list --project "$PROJECT" --filter="name~cloudreve"
kubectl logs -n "$NAMESPACE" statefulset/<service-name> --tail=200 | grep -i "admin\|password"
See App_GKE for the Secret Store CSI integration and rotation mechanics (rotation has no effect here since no service secret exists).
E. Networking & ingress
By default the workload uses enable_custom_domain = true with
reserve_static_ip = true (Kubernetes Gateway API with a Google-managed
certificate). service_type defaults to ClusterIP — the Gateway, not the
Service, is the external entry point unless you switch to LoadBalancer.
- Console: Network services → Load balancing / Gateways; VPC network → IP addresses.
- CLI:
kubectl get svc,gateway,httproute -n "$NAMESPACE"
gcloud compute addresses list --project "$PROJECT"
See App_GKE for custom domains, Cloud CDN, and static IP details.
F. Cloud Logging & Monitoring
Pod stdout/stderr flow to Cloud Logging; GKE metrics flow to Cloud
Monitoring. Optional uptime checks and alert policies are available
(uptime_check_config.enabled defaults false).
- Console: Logging → Logs Explorer; Monitoring → Dashboards / Alerting.
- CLI:
gcloud logging read 'resource.type="k8s_container" AND resource.labels.namespace_name="'"$NAMESPACE"'"' \
--project "$PROJECT" --limit 50
3. Cloudreve Application Behaviour
- No database initialisation job.
Cloudreve_Commondoes not inject a defaultdb-init/db-createjob — Cloudreve has no SQL database to provision.initialization_jobsonly runs jobs you explicitly supply. - First-boot self-setup, admin password in logs. On first start Cloudreve
creates its SQLite schema on the mounted volume and generates the initial
admin account, printing the generated password to container logs. There is
no separate migration step and no Secret Manager secret to retrieve it
from — capture it with
kubectl logsbefore the log buffer rotates, then change it via the web UI. - Volume-shadowing is pre-fixed in the Dockerfile. Without the relocation
described in Section 1, the block PVC mount at
/cloudrevewould shadow the co-located binary, producingexec ./cloudreve: no such file or directory(CrashLoopBackOff). This is a custom-build module (container_image_source = "custom",image_source = "custom"inCloudreve_Common) precisely so this fix (modules/Cloudreve_Common/scripts/Dockerfile) can be baked in — it is not a bare pass-through of the upstream image. Editing the Dockerfile requires a rebuild (tofu taint 'module.app_gke.module.app_build.null_resource.build_and_push_application_image[0]'if a content-hash trigger misses the change). - Version pinning uses an app-specific build ARG. The Dockerfile reads
CLOUDREVE_VERSION(pinned to3.8.3whenapplication_version = "latest"), not the genericAPP_VERSIONthe Foundation injects and would otherwise force to the unresolvable taglatest. - Health probe paths. Startup probe is HTTP
GET /(initial_delay_seconds = 15,failure_threshold = 10, i.e. up to ~100s to become ready); liveness probe is HTTPGET /as well (initial_delay_seconds = 30,period_seconds = 30). Cloudreve has no dedicated health endpoint distinct from its web UI —/returns 200 once the server is serving. - Single-replica,
OrderedReadysemantics. Withstateful_pod_management_policyleft at itsnull→OrderedReadydefault andmax_instance_count = 1, only one pod exists at a time; do not raisemax_instance_countwithout verifying Cloudreve's own multi-node/clustering support (not managed by this module). - Inspect the running config and StatefulSet rollout:
kubectl get statefulsets -n "$NAMESPACE"
kubectl rollout status statefulset/<service-name> -n "$NAMESPACE"
kubectl exec -n "$NAMESPACE" statefulset/<service-name> -- ls -la /cloudreve
4. Configuration Variables
Variables are grouped exactly as they appear on the deployment platform. Only settings specific to or notable for Cloudreve are listed; every other input is inherited from App_GKE with its standard behaviour and defaults.
Group 3 — Application Identity
| Variable | Default | Description |
|---|---|---|
application_name | cloudreve | Base name for resources. Do not change after first deploy. |
application_version | latest | cloudreve/cloudreve image tag; latest is pinned to 3.8.3 at build time via the CLOUDREVE_VERSION build ARG. |
description | Cloudreve — self-hosted cloud storage / file-sharing system | Populates the GKE workload description. |
Group 4 — Runtime & Scaling
| Variable | Default | Description |
|---|---|---|
cpu_limit | 1000m | CPU allocated to the Cloudreve container. |
memory_limit | 1Gi | Memory allocated; size for file-serving and concurrent transfers. |
min_instance_count / max_instance_count | 1 / 1 | Cloudreve has no distributed/clustering mode — keep both at 1. |
container_port | 5212 | Fixed by Cloudreve_Common; the variable is not forwarded to App_GKE and has no effect. |
enable_cloudsql_volume | false | Cloudreve has no Cloud SQL database. |
enable_image_mirroring | true | Mirrors the built image into Artifact Registry. |
Group 6 — GKE Backend & Cluster
| Variable | Default | Description |
|---|---|---|
service_type | ClusterIP | External access goes through the Gateway (enable_custom_domain), not the Service, by default. |
workload_type | null → StatefulSet | Resolves automatically because stateful_pvc_enabled = true. |
session_affinity | None | No sticky routing configured by default. |
Group 7 — StatefulSet Configuration
| Variable | Default | Description |
|---|---|---|
stateful_pvc_enabled | true | Required for Cloudreve — its SQLite DB and uploads must live on a block volume, not gcsfuse. |
stateful_pvc_size | 20Gi | Size for the SQLite DB plus uploaded files; raise for larger libraries. |
stateful_pvc_mount_path | /cloudreve | Cloudreve's working directory — do not change without also updating the app's data-path expectations. |
stateful_pvc_storage_class | standard-rwo | SSD-backed Balanced PD; switch to standard (HDD) if the tight SSD_TOTAL_GB quota is a constraint (see App_GKE). |
stateful_fs_group | 3000 | Matches the Cloudreve Helm chart's fsGroup convention (container runs as UID 1000/GID 2000) so the PVC is group-writable. |
Group 9 — Reliability
| Variable | Default | Description |
|---|---|---|
enable_pod_disruption_budget | true | On by default (unusual among app modules) to protect the single stateful pod. |
pdb_min_available | "1" | Requires the one pod to stay available during voluntary disruptions. |
Group 10 — Observability & Health
| Variable | Default | Description |
|---|---|---|
startup_probe | HTTP GET /, initial_delay=15s, failure_threshold=10 | No dedicated health endpoint — Cloudreve serves / once ready. |
liveness_probe | HTTP GET /, initial_delay=30s, period=30s | Same endpoint as the startup probe. |
Group 14 — Cloud Storage
| Variable | Default | Description |
|---|---|---|
gcs_volumes | [] | The auto-created storage bucket is only mounted at /cloudreve when stateful_pvc_enabled = false; otherwise it exists unused unless mounted at a different path here. |
Group 15 — Redis
| Variable | Default | Description |
|---|---|---|
enable_redis | true (App_GKE default) but overridden to false in main.tf | Cloudreve does not use Redis; the variable has no effect regardless of its setting. |
Group 16 — Database Configuration
| Variable | Default | Description |
|---|---|---|
database_type | NONE | Fixed by Cloudreve_Common — Cloudreve has no SQL database; all other database_*/db_*/sql_* variables are forwarded for foundation interface compatibility only and have no effect. |
Group 19 — Custom Domain, Static IP & Networking
| Variable | Default | Description |
|---|---|---|
enable_custom_domain | true | On by default, unlike many other application modules. |
reserve_static_ip | true | Stable external address across redeploys. |
network_tags | ["nfsserver"] | Default carries the nfsserver tag even though enable_nfs defaults false for Cloudreve — harmless unless NFS is separately enabled. |
All other inputs follow standard App_GKE behaviour.
5. Outputs
These values are returned on a successful deployment and are the quickest way to locate and explore the running resources.
| Output | Description |
|---|---|
service_name | Kubernetes Service name. |
namespace | Namespace the workload runs in. |
service_cluster_ip | In-cluster ClusterIP. |
stage_service_cluster_ips | Map of ClusterIPs for stage-specific services. |
service_external_ip | External LoadBalancer IP (when a static IP is reserved). |
service_url | URL to reach Cloudreve. |
storage_buckets | Created Cloud Storage buckets (the storage bucket, mounted only if stateful_pvc_enabled = false). |
network_name / network_exists / regions | VPC network, presence, available regions. |
container_image / container_registry | Deployed image and Artifact Registry repo. |
monitoring_enabled / monitoring_notification_channels | Monitoring status and channels. |
deployment_id / tenant_id / resource_prefix | Naming identifiers. |
project_id / project_number | Project identifiers. |
initialization_jobs | Names of any user-supplied initialization jobs (Cloudreve injects none by default). |
statefulset_name | Name of the StatefulSet. |
cicd_enabled / cicd_configuration | CI/CD status and details (repo, trigger, registry). |
github_repository_url / github_repository_owner / github_repository_name | CI/CD GitHub details. |
artifact_registry_repository / cloudbuild_trigger_name / cloudbuild_trigger_id | Registry and build trigger. |
kubernetes_ready | Whether the cluster/workload is ready. |
vpc_sc_enabled / vpc_sc_perimeter_name / vpc_sc_dry_run_mode | VPC-SC status. |
audit_logging_enabled / artifact_registry_cmek_enabled | Audit logging and CMEK status. |
6. Configuration Pitfalls & Sensible Defaults
Risk: Critical (data loss / outage / security) — High (service degraded) — Medium (cost or partial degradation) — Low (minor).
Inherited plan-time validation. This module passes its configuration through the App_GKE foundation engine, which validates values and combinations at plan time — a
StatefulSetforced alongside a stateless setting, IAP with no authorized identities,quota_memory_*given as bare integers, an out-of-rangecontainer_port/backup_retention_days. Invalid configuration fails the plan with a clear, named error before any resource is created, so most mistakes below are caught up front rather than at apply or runtime.
| Setting | Sensible value | Risk | Consequence if wrong |
|---|---|---|---|
stateful_pvc_enabled | true | Critical | Disabling it without providing an equivalent block-storage mount lets SQLite live on gcsfuse (or ephemeral disk), risking database corruption or total data loss on pod recreation. |
stateful_pvc_mount_path | /cloudreve | Critical | Changing this away from Cloudreve's working directory disconnects the persistent volume from where the app actually reads/writes its DB and uploads. |
Dockerfile binary relocation (/usr/local/bin/cloudreve) | Keep as shipped | Critical | Reverting to ENTRYPOINT ["./cloudreve"] inside /cloudreve re-introduces volume-shadowing: the block PVC mount hides the binary and the pod CrashLoopBackOffs with exec ./cloudreve: no such file or directory. |
max_instance_count | 1 | High | Cloudreve has no verified multi-node/clustering mode in this module; scaling beyond 1 risks concurrent writers against the same SQLite file (only one pod actually owns the PVC in OrderedReady StatefulSet mode, but do not assume higher counts are safe). |
stateful_pvc_storage_class | standard-rwo (SSD); standard (HDD) under quota pressure | Medium | SSD draws the tight SSD_TOTAL_GB quota. Scaling to zero does not release the PVC — only deletion does. |
| Admin password retrieval | Capture from kubectl logs immediately after first boot | Medium | The generated admin password is only printed to container logs once; missing it locks you out of the first-run super-admin account until reset via the container. |
quota_memory_requests / _limits | binary units (4Gi, 8192Mi) | Critical | Bare integers are treated as bytes and block all pod scheduling in the namespace. |
reserve_static_ip | true | Medium | Without it, the external IP/Gateway address can change across redeploys, breaking DNS and bookmarked links. |
gcs_volumes at /cloudreve while stateful_pvc_enabled = true | Avoid | High | A GCS FUSE mount and the block PVC both targeting /cloudreve conflict; the Common module only enables the GCS mount when the block PVC is off. |
backup_retention_days | 7 (raise for prod) | Medium | Too short for compliance retention. |
For the foundation behaviour referenced throughout — IAM and Workload
Identity, autoscaling, ingress and certificates, CI/CD, Cloud Armor, IAP,
Binary Authorization, VPC-SC, backups, and image mirroring — see
App_GKE. Cloudreve-specific application configuration
shared with the Cloud Run variant is described in the Cloudreve_Common
module (modules/Cloudreve_Common); a dedicated Cloudreve_Common.md guide
does not yet exist in this documentation set.