ClassicPress on GKE Autopilot
ClassicPress is a free, open-source, business-focused CMS — a lightweight fork of WordPress 4.9.x that preserves the classic (pre-Gutenberg) editing experience, with plugins, themes, a media library, and a REST API. This module deploys ClassicPress 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 ClassicPress 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.
What ClassicPress costs on RAD, and how that compares
ClassicPress on RAD's GKE Autopilot module costs about US$110.13 a month in a Google Cloud project you own, plus a one-time 175-credit module fee (US$17.50). ClassicPress is a lightweight, pre-Gutenberg fork of WordPress for business sites. For the lowest cost, the Cloud Run guide runs the same ClassicPress for about US$83.61 a month; pick GKE here only if ClassicPress must stay up continuously, scale across pods, or run beside other Kubernetes workloads.
If ClassicPress is only needed occasionally — a demo, a trial, a seasonal or study workload — a RAD-managed project can be deleted and restored within 30 days for a handful of credits, so that US$110.13 becomes a few dollars a month instead. See Pause it for free, below.
What you pay on RAD
| In a Google Cloud project you own | In a project RAD manages for you | |
|---|---|---|
| Module fee, once per deployment | 175 credits (US$17.50 at the top-up price) | 157.5 credits (10% lower) |
| Build time | A few credits per build (roughly 3–6) | The same |
| Google Cloud running cost | Billed by Google to your own billing account, about US$110.13 a month for the default configuration (table below) | Metered hourly in credits; RAD publishes 99 credits a day, about 2970 a month (about US$297 at the top-up price) |
| Who owns what | You own the project and its billing; RAD deploys and updates it | RAD owns the project, with guardrails, quotas and budget alerts; you get console access, and a minimum purchased balance is held in reserve |
10 credits cost US$1 on a one-off top-up, less on a plan. In a project RAD manages, the database, file server and network are shared by every application in the project, so a second application does not add a second database.
Default running cost in your own project (us-central1, Google list prices):
| Resource (module default) | Per month |
|---|---|
| GKE Autopilot pod, 1x vCPU / 2 GiB | US$39.68 |
| Cloud SQL for MySQL, 1 vCPU / 3.75 GB, zonal, 10 GB SSD | US$51 |
| NFS/cache file server (small VM) | US$13.43 |
| Cloud NAT and networking | US$5 |
| Cloud Storage (add-ons, backups) | US$1 |
| GKE cluster management fee | shared across every GKE app in the project; $0 if this is your only cluster, else ~73 |
| Total | about US$110.13 |
How it compares
- Run it yourself and a VPS running PHP, MySQL and ClassicPress yourself costs less in cash: a Hetzner CPX22 or DigitalOcean 2 vCPU/4 GB box is about $24 a month, or a GCP
e2-standard-2about $49 a month — plus your own time for backups, security patches and upgrades, which RAD's managed Cloud SQL, Secret Manager and monitoring cover for you. - RAD never claims to be cheaper than bare infrastructure in cash terms — it usually is not. The difference is what RAD manages for you: patching, backups, secrets and monitoring, plus the ability to pause ClassicPress for free rather than paying for idle capacity (see below).
- Most of the running cost above is the managed MySQL database (and, where used, the shared file server) — the part a bare VPS comparison has to run and back up by hand.
Pause it for free: delete a RAD-managed project, restore it when you need it
If ClassicPress runs in a project RAD manages for you, you have a second option that goes well beyond scaling to zero: delete the whole project, and restore it within 30 days for close to nothing. This suits ClassicPress that you only need occasionally far better than running it continuously.
- How it works. Deleting a RAD-managed project unlinks its billing first, then asks Google to delete the project. Google does not remove the project immediately: it keeps it, recoverable, for 30 days. Because billing is already unlinked, nothing is charged while it waits. Unlike deleting one module, this does not tear down Cloud SQL, the VM or GKE Autopilot resources one by one — the whole project, and everything in it, simply stops.
- Restoring costs a handful of credits, not a rebuild. Within 30 days, the project's owner can restore it. RAD asks Google to undelete the project and reattaches its billing account, then asks you to run Update on each deployment to confirm everything came back. Because nothing was individually destroyed, that Update finds the same resources already there — it is a check, not a rebuild, and an Update never charges the module fee again. That is a handful of credits (under US$1) in total for a typical ClassicPress chain, against the 180 credits a full redeploy costs.
- So a month of occasional use can cost a few dollars, not US$110.13. Deploy ClassicPress, use it, delete the project. Restore it next time you want it, confirm with Update, and delete it again when you're done. You pay only for the module fee once, the builds, and whatever hours it was actually live.
- What this needs. You must own the project (not a bring-your-own one RAD only manages billing for), and you restore it yourself within the 30 days — after that, Google deletes it for good. Restoring is admitted like creating a new project: your purchased credit balance must still clear the tier's floor (100 credits for the sandbox tier most study and demo use fits). Google says most services are fully working again within 36 hours of a restore.
- One real gap: the backup bucket does not survive. Nightly backups are written to a bucket inside the project, and that bucket is not protected by Cloud Storage's soft-delete, so it is very likely gone as soon as you delete the project — even though the project itself is recoverable for 30 days. If you have customised ClassicPress and want to keep that work, copy a backup out (to Google Drive, or a bucket outside the project) before you delete. For a default installation with nothing irreplaceable in it, this does not matter.
Pay only while you use it, the other way: delete and redeploy
GKE Autopilot keeps at least one pod running, so it never scales to zero on its own. The option above only applies to a RAD-managed project; in your own project, or once the 30-day window has passed, the way to stop paying is to delete the deployment and deploy it again when you need it.
- What a redeploy costs. The module fee again plus the builds: about 180 credits (US$18) in your own project. Deleting saves money only once ClassicPress would otherwise sit unused for about 5 days or more in your own project (US$3.67 a day), or about 2 days or more in a RAD-managed one (99 credits a day).
- Most of the running cost is usually the database and the shared file or cache server. They stop only when nothing else in the project uses them, so deleting ClassicPress while something else shares the project saves only this app's own compute part.
- Keep your data first. Nightly backups are written to a bucket inside the deployment and are deleted with it, so copy the latest backup out before deleting if you want to keep it.
- Note your settings. RAD does not recreate a deleted deployment for you; you enter the settings again when you deploy.
Lab sessions and Managed Environments
- Lab sessions, for training. A trainer runs a session for a class. Each participant gets the app in their own Google Cloud project for 15 minutes to 24 hours, within an allowance the trainer sets. Either the trainer funds every place, or each participant pays for their own. Everything is deleted when the session ends and unused credits go back to the trainer.
- Managed Environments, for consultancies. A partner runs the app for a client from a ring-fenced wallet it funds, and settles with the client directly. If the wallet runs low, billing pauses and the data is kept, so nobody receives an unexpected charge. At the end the partner hands the project over and the deployments become the client's own.
Credits can be bought in more than 20 currencies, including XAF, XOF, NGN, GHS, KES and ZAR, by card, bank transfer or mobile money.
Sources (8 October 2026): Google Cloud list prices from the Cloud Billing Catalog API; RAD's own fees and daily-credit estimates from radmodules.dev/pricing; Delete and restore projects for the 30-day recovery window and what it says about Cloud Storage objects without soft delete. Prices change; check each source before relying on a figure.
1. Overview
ClassicPress runs as a single PHP/Apache web workload built from a thin custom image
FROM classicpress/classicpress. The deployment wires together a focused set of
Google Cloud services:
| Capability | Google Cloud service | Notes |
|---|---|---|
| Compute | GKE Autopilot | PHP/Apache pod on port 80, 1 vCPU / 2 GiB by default |
| Workload shape | Kubernetes StatefulSet + block PVC | stateful_pvc_enabled = true auto-resolves workload_type to StatefulSet; a 10Gi standard-rwo (SSD) PVC is mounted at /var/www/html — the whole ClassicPress install (code, plugins, themes, wp-content/uploads) lives on this per-pod volume |
| Database | Cloud SQL for MySQL 8.0 | Fixed — ClassicPress_Common hardcodes database_type = "MYSQL_8_0" |
| File persistence (secondary) | Cloud Filestore (NFS) | Mounted at /var/www/html/wp-content by default (enable_nfs = true) — a subpath of the StatefulSet PVC below; this is the confirmed mechanism for upload/plugin/theme persistence (see Section 3) |
| Object storage | Cloud Storage | A classicpress-uploads bucket is provisioned automatically but is not mounted into the pod by default |
| Secrets | Secret Manager | Auto-generated CLASSICPRESS_SALT_SEED (derives the 8 WordPress-style auth keys/salts); database password |
| Ingress | Cloud Load Balancing | External LoadBalancer with a reserved static IP; custom domain + managed certificate enabled by default |
Sensible defaults worth knowing up front:
- MySQL 8.0 is mandatory and hardcoded.
ClassicPress_Common'sconfigsetsdatabase_type = "MYSQL_8_0"directly; the variant'sdatabase_typevariable (defaultnull) only takes effect if explicitly overridden, and doing so breaks the MySQL-specificdb-initjob and entrypoint. Leave itnull. - Custom build, not prebuilt.
container_image_sourcedefaults to"custom"— Cloud Build produces a thin imageFROM classicpress/classicpressthat grafts an entrypoint shim aliasing the Foundation's injectedDB_*vars onto ClassicPress'sCLASSICPRESS_DB_*and deriving stable auth salts. The stock upstream image is never deployed directly. - Cloud SQL is reached via the Auth Proxy sidecar on loopback. The variant sets
DB_HOST = 127.0.0.1; a cloud-sql-proxy sidecar (enable_cloudsql_volume = true) listens on127.0.0.1:3306, and the entrypoint aliases the injectedDB_*values ontoCLASSICPRESS_DB_*(TCP, no SSL needed for MySQL). - Single replica, StatefulSet by default.
min_instance_count = 1,max_instance_count = 1. Each pod gets its own PVC viastateful_pvc_enabled = true(default), so scaling beyond 1 replica would give each pod a separate, unsynced copy of the install rather than a shared one. - NFS is also enabled by default (
enable_nfs = true, mounted at/var/www/html/wp-content— a subdirectory of the StatefulSet PVC mount below). ClassicPress reads/writes uploaded media, plugins, and themes underwp-content, and the upstream entrypoint's first-boot copy logic explicitly skips an existingwp-contentdirectory, so this mount is a genuine, confirmed persistence path for that data — not spare/unused storage. See the pitfalls table. - No auto-install — first login is manual.
ClassicPress_Commongenerates no admin-password secret and sets no auto-install flag. ClassicPress creates its schema and admin account through its own first-run web installer oncedb-inithas provisioned the empty database. CLASSICPRESS_SALT_SEEDis generated automatically and stored in Secret Manager; the entrypoint derives all 8 WordPress-styleAUTH_KEY/SALTvalues from it deterministically, so cookies and sessions survive pod restarts.- Startup probe is generous (TCP, 20 retries) to allow time for the upstream
image's own entrypoint to populate the empty
/var/www/htmlPVC on first boot.
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 ClassicPress workload
ClassicPress runs as a StatefulSet (auto-selected because stateful_pvc_enabled
defaults to true), scheduled on Autopilot, which bills for the CPU/memory the pod
actually requests. The single replica owns a dedicated standard-rwo (SSD) PVC.
- Console: Kubernetes Engine → Workloads → select the ClassicPress workload for pods, revisions, and events. Kubernetes Engine → Services & Ingress shows the external IP.
- CLI:
kubectl get statefulset,pods,svc -n "$NAMESPACE" --selector 'app~classicpress' 2>/dev/null || \
kubectl get statefulset,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 workload type (Deployment vs StatefulSet) are managed, and Group 7 for StatefulSet/PVC mechanics.
B. Cloud SQL for MySQL 8.0
ClassicPress stores all application data (posts, pages, users, options, plugin/theme
settings) in a managed Cloud SQL for MySQL 8.0 instance. Pods reach it through the
Cloud SQL Auth Proxy sidecar on 127.0.0.1:3306; no public IP is exposed. On
first deploy the db-init job creates the application database, user, and grants,
verifies the app user can connect, then shuts the sidecar down.
- Console: SQL → select the instance for connections, backups, flags, metrics.
- CLI:
gcloud sql instances list --project "$PROJECT"
gcloud sql instances describe <instance-name> --project "$PROJECT"
gcloud sql connect <instance-name> --user=<db-user> --database=<db-name> --project "$PROJECT"
The instance name, database, user, and the Secret Manager secret holding the password are all in the Outputs. See App_GKE for the connection model, automated backups, and password rotation.
C. Storage — StatefulSet PVC, NFS, and Cloud Storage
Three distinct storage mechanisms are present. The StatefulSet PVC (10Gi,
standard-rwo, mounted at /var/www/html) holds the entire ClassicPress install,
including uploaded media — persistence for the whole webroot as long as the pod
keeps its PVC. NFS (Cloud Filestore) is separately mounted at
/var/www/html/wp-content (enable_nfs = true), a subdirectory of the PVC mount
above; the upstream entrypoint's copy logic explicitly skips an existing wp-content
directory, so this is a genuine, confirmed persistence path for uploads/plugins/themes
specifically — not an unused mount (see the pitfalls table). A Cloud Storage
bucket (suffix classicpress-uploads) is also provisioned but not wired into the pod
as a gcs_volumes mount by default; add an entry to gcs_volumes to use it.
- Console: Kubernetes Engine → Storage → PersistentVolumeClaims; Filestore → Instances; Cloud Storage → Buckets.
- CLI:
kubectl get pvc -n "$NAMESPACE"
gcloud filestore instances list --project "$PROJECT"
gcloud storage buckets list --project "$PROJECT" --filter="name~classicpress-uploads"
See App_GKE Groups 7, 13, and 14 for PVC lifecycle, NFS discovery, and GCS Fuse mount mechanics.
D. Secret Manager
One ClassicPress-specific secret is generated automatically and stored in Secret
Manager: CLASSICPRESS_SALT_SEED, a 64-character random seed from which the
entrypoint derives all 8 WordPress-style AUTH_KEY/SECURE_AUTH_KEY/
LOGGED_IN_KEY/NONCE_KEY and their matching SALT values (SHA-256 of the seed
plus a fixed per-key suffix). The database password is managed separately by the
foundation. On GKE, secrets are projected into pods via the Secret Store CSI driver.
- Console: Security → Secret Manager.
- CLI:
gcloud secrets list --project "$PROJECT" --filter="name~classicpress"
gcloud secrets versions access latest --secret=<db-password-secret-name> --project "$PROJECT"
See App_GKE for the Secret Store CSI integration and rotation.
E. Networking & ingress
By default the workload is exposed through an external Cloud Load Balancing IP
(service_type = LoadBalancer, reserve_static_ip = true so the address survives
redeploys), and enable_custom_domain = true provisions an Ingress resource ready
for a Google-managed certificate once application_domains is populated.
- Console: Network services → Load balancing; VPC network → IP addresses.
- CLI:
kubectl get svc,ingress -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 and Cloud SQL metrics flow to Cloud Monitoring. Optional uptime checks and alert policies are available.
- 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. ClassicPress Application Behaviour
- First-deploy database setup. The
db-initjob runsdb-init.shusingmysql:8.0-debian. It waits for TCP connectivity (or a Cloud SQL socket), idempotently creates the application database, user, and grants, verifies the app user can connect, then gracefully shuts down the Cloud SQL Auth Proxy sidecar (POST /quitquitquit, falling back toSIGKILL). The job is safe to re-run (execute_on_apply = true,max_retries = 3). - No separate migration job — manual first-run install. There is no
auto-install flag for ClassicPress. Once
db-inithas provisioned the empty database, ClassicPress creates its own schema and admin account through the first-run web installer (/wp-admin/install.phpin the upstream image). Visit the service URL after first deploy and complete the installer to set the admin username, password, and email — there is no generated admin-password secret. - DB env-var aliasing on loopback. The Foundation injects
DB_HOST = 127.0.0.1(the proxy sidecar),DB_PORT,DB_NAME,DB_USER,DB_PASSWORD; ClassicPress readsCLASSICPRESS_DB_*. The graftedentrypoint.shbuildsCLASSICPRESS_DB_HOSTashost:port(or alocalhost:<socket>form if a Cloud SQL socket directory is found) and aliases the rest, before handing off to the upstreamdocker-entrypoint.sh. - Auth keys/salts are derived, not stored individually.
CLASSICPRESS_SALT_SEEDis the only generated secret; the entrypoint computes all 8CLASSICPRESS_AUTH_KEY/..._SALTvalues assha256(seed-<key-name>), so every restart and every pod (if you ever scale out) agrees on the same values without persistingwp-config.phpstate. - The install itself lives on the StatefulSet PVC. The upstream
docker-entrypoint.shwriteswp-config.phpand copies the ClassicPress application into/var/www/htmlon first boot — which on this module is a 10Gistandard-rwoblock PVC, not ephemeral container storage, so the install (plugins, themes, andwp-content/uploads) survives pod restarts and rescheduling. - NFS mount path is the confirmed persistence mechanism for
wp-content.enable_nfs = truemounts Filestore at/var/www/html/wp-contentby default — a subdirectory of the StatefulSet PVC mounted at/var/www/html. ClassicPress (a WordPress fork) reads/writes uploaded media, plugins, and themes underwp-content, and the upstream entrypoint's first-boot copy logic explicitly skips an existingwp-contentdirectory, so mounting NFS there is safe and effective. On this module the PVC already persists the whole webroot per-pod, so the NFS mount is most valuable ifstateful_pvc_enabledis ever disabled or a future multi-replica scale-out needswp-contentshared across pods (the PVC is per-pod, NFS is shared). - Redis is optional and off by default. When
enable_redis = true, leavingredis_hostempty lets the Foundation's ownREDIS_HOSTinjection (the shared NFS-VM Redis IP whenenable_nfs = true) take effect; settingredis_hostexplicitly points ClassicPress at an external Redis/Memorystore instance instead. - Health paths. Startup probe is TCP on port 80 with a generous
failure_threshold = 20(first boot copies the whole application onto the empty PVC, which can take time); liveness probe is HTTPGET /with a 300-second initial delay — a 200 (installed site) or a 302 redirect to the installer (fresh site) both count as healthy. - Inspect the init job and running config:
kubectl get jobs -n "$NAMESPACE"
kubectl logs -n "$NAMESPACE" job/<db-init-job-name>
kubectl exec -n "$NAMESPACE" statefulset/<service-name> -- env | grep CLASSICPRESS_DB
4. Configuration Variables
Variables are grouped exactly as they appear on the deployment platform (by their
{{UIMeta group=N}} tag, not by the .tf file's section comments, which are
occasionally out of sync with the tags). Only settings specific to or notable for
ClassicPress 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 | classicpress | Base name for resources. Do not change after first deploy. |
application_version | latest | Maps to the classicpress/classicpress image tag. Tags are PHP-qualified (php8.3-apache, php8.4-apache), not version-qualified; latest resolves to a pinned PHP-qualified tag at build time via the app-specific CLASSICPRESS_VERSION build arg (the generic APP_VERSION build arg the Foundation injects would otherwise silently overwrite it with the literal, non-existent tag "latest"). |
Group 4 — Runtime & Scaling
| Variable | Default | Description |
|---|---|---|
container_image_source | custom | Builds the thin FROM classicpress/classicpress image with the grafted entrypoint shim; required — the stock image cannot alias DB_* on its own. |
cpu_limit | 1000m | CPU limit for the ClassicPress container. Increase for high-traffic sites or heavy plugins. |
memory_limit | 2Gi | Memory limit. WordPress-family apps typically need ≥512Mi; more for large media libraries. |
min_instance_count / max_instance_count | 1 / 1 | Keep both at 1 — the StatefulSet PVC is per-pod and not shared. |
container_port | 80 | ClassicPress runs on Apache, port 80. |
php_memory_limit | 512M | PHP memory limit; must be ≤ memory_limit. |
upload_max_filesize / post_max_size | 64M | Max upload / POST size; keep post_max_size ≥ upload_max_filesize. |
enable_cloudsql_volume | true | Auth Proxy sidecar (loopback) — required on GKE. |
Group 6 — GKE Backend & Cluster
| Variable | Default | Description |
|---|---|---|
service_type | LoadBalancer | External IP for the ClassicPress UI. |
workload_type | null → StatefulSet | Auto-resolved because stateful_pvc_enabled = true. |
session_affinity | ClientIP | Sticky routing so a client reaches the same pod. |
Group 7 — StatefulSet / PVC
| Variable | Default | Description |
|---|---|---|
stateful_pvc_enabled | true | ClassicPress stores its whole install under /var/www/html; enabling this auto-resolves workload_type to StatefulSet. |
stateful_pvc_size | 10Gi | Per-pod PVC size. Raise for large media libraries. |
stateful_pvc_mount_path | /var/www/html | Where the ClassicPress install (code + wp-content/uploads) lives. |
stateful_pvc_storage_class | standard-rwo | SSD-backed Balanced PD; draws the SSD_TOTAL_GB regional quota. Override to standard (HDD) if quota-constrained. |
Group 10 — Observability & Health
| Variable | Default | Description |
|---|---|---|
startup_probe | { type: TCP, path: "/", failure_threshold: 20, period_seconds: 15 } | Generous threshold — first boot populates the empty PVC with the full application. |
liveness_probe | { type: HTTP, path: "/", initial_delay_seconds: 300 } | A 200 or a 302-to-installer both count as healthy. |
Group 13 — Filesystem (NFS)
| Variable | Default | Description |
|---|---|---|
enable_nfs | true | Provisions Filestore-backed shared storage mounted at nfs_mount_path — the confirmed persistence mechanism for uploads/plugins/themes; see Section 3. |
nfs_mount_path | /var/www/html/wp-content | Container mount path for the NFS share, a subdirectory of the StatefulSet PVC. The upstream entrypoint's copy logic skips an existing wp-content directory, so mounting here is safe. |
Group 15 — Redis Cache
| Variable | Default | Description |
|---|---|---|
enable_redis | false | Enables ClassicPress's WordPress-style object cache backend. |
redis_host | "" | Leave empty to fall back to the Foundation's own REDIS_HOST (the NFS-VM Redis IP when enable_nfs = true); set explicitly to use an external Redis/Memorystore instance. |
Group 16 — Database Backend
| Variable | Default | Description |
|---|---|---|
database_type | null → MYSQL_8_0 | Hardcoded in ClassicPress_Common; leave null. Overriding it breaks the MySQL-specific db-init job and entrypoint. |
application_database_name | classicpress | Database name. Immutable after first deploy. |
application_database_user | classicpress | Application database user; password auto-generated in Secret Manager. |
Group 19 — Custom Domain, Static IP & Networking
| Variable | Default | Description |
|---|---|---|
enable_custom_domain | true | Provisions an Ingress ready for a managed certificate once application_domains is set. |
reserve_static_ip | true | Stable external IP across redeploys. |
application_domains | [] | Custom hostnames — empty by default; add one to activate the managed certificate. |
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 ClassicPress. |
database_instance_name | Cloud SQL instance name. |
database_name / database_user | Application database name / user. |
database_password_secret | Secret Manager secret holding the DB password. |
database_host / database_port | DB endpoint (127.0.0.1 via the Auth Proxy) / port. |
storage_buckets | Created Cloud Storage buckets (including classicpress-uploads). |
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. |
initialization_jobs / db_import_job | Names of the setup (db-init) and (optional) import jobs. |
deployment_id / tenant_id / resource_prefix | Naming identifiers. |
project_id / project_number | Project identifiers. |
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. ClassicPress also runs its own preconditions (validation.tf) forupload_max_filesize ≤ post_max_size,min_instance_count ≤ max_instance_count, Redis without a host source, IAP without OAuth credentials, andenable_cloudsql_volumewithdatabase_type = "NONE".
| Setting | Sensible value | Risk | Consequence if wrong |
|---|---|---|---|
database_type | null (→ MYSQL_8_0) | Critical | ClassicPress_Common's db-init job and entrypoint are MySQL-specific; overriding to a Postgres/SQL Server engine breaks both. |
application_database_name / application_database_user | Set once | Critical | Immutable after first deploy; renaming recreates the DB/user and orphans all data. |
CLASSICPRESS_SALT_SEED (auto-generated) | Never change | Critical | Changing the seed after first boot invalidates all signed cookies and logged-in sessions. |
stateful_pvc_size / stateful_pvc_mount_path | 10Gi / /var/www/html | Critical | This PVC holds the entire install (code + uploads) — resizing down or losing the PVC destroys the site; the mount path must match where ClassicPress's entrypoint writes. |
max_instance_count | 1 | High | Each StatefulSet pod gets its own PVC; scaling beyond 1 gives every replica a separate, diverging copy of the site rather than a shared one. |
enable_cloudsql_volume | true | High | The Auth Proxy sidecar on 127.0.0.1:3306 is required for DB connectivity on GKE. |
enable_nfs | true | Low | Provisions and pays for a Filestore instance mounted at /var/www/html/wp-content — the confirmed mechanism for upload/plugin/theme persistence (see Section 3), on top of the StatefulSet PVC's per-pod persistence for the rest of the webroot. |
stateful_pvc_storage_class | standard-rwo (SSD) | Medium | Draws the tight regional SSD_TOTAL_GB quota. Override to standard (HDD pd-standard) on quota-constrained projects — fine for a low-IOPS PHP/MySQL workload. |
| First-run admin setup | Complete /wp-admin/install.php promptly after deploy | Medium | Until the installer runs, the site has no schema and no admin account — there is no generated admin-password secret to recover with. |
memory_limit | 2Gi | Medium | Below ~512Mi the PHP/Apache pod risks OOM under load or with heavier plugins. |
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 can change across redeploys, breaking DNS and any hardcoded site URL. |
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.
ClassicPress-specific application configuration shared with the Cloud Run variant is
described in the ClassicPress_Common module (no standalone ClassicPress_Common.md
guide exists yet in this docs set).
Related guides
- Hands-on lab: ClassicPress on GKE Autopilot — deploy it step by step, with the console screens and commands at each stage.
- ClassicPress on Google Cloud Run — the same application on Cloud Run, for when you need the other deployment target.
- ClassicPress Common — Shared Application Configuration — the configuration shared by both deployment targets.
Need RAD to do something it does not do yet? Request it on the roadmap, or vote on what is already there.