Skip to main content

Outline on GKE Autopilot

Outline on GKE Autopilot

Outline is an open-source, Notion-style team knowledge base and wiki: real-time collaborative Markdown documents, powerful full-text search, and nested document collections, authenticated exclusively through an external identity provider (OIDC, Google, Slack, etc.) rather than its own username/password store. This module deploys Outline 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 Outline 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 Outline costs on RAD, and how that compares​

For a team of ten, Outline on RAD's GKE Autopilot module costs about US$110 a month with no per-user licence. GKE suits an Outline that must stay up, scale across pods, or run beside other Kubernetes workloads. For the lowest cost, the Cloud Run module runs the same Outline for about US$82 a month. Figures are as at 8 October 2026; sources are listed at the end of this section.

If you only need it occasionally — studying for a certification, a demo, a seasonal business — a RAD-managed project can be deleted and restored within 30 days for a few credits, so that US$110 becomes a few dollars a month instead. See Pause it for free, below.

What you pay on RAD​

In a Google Cloud project you ownIn a project RAD manages for you
Module fee, once per deployment175 credits (US$17.50 at the top-up price)157.5 credits (10% lower)
Build timeAbout 19 credits per build (RAD's average build takes about 19 minutes)The same
Google Cloud running costBilled by Google to your own billing account (table below)Metered hourly in credits; RAD publishes 95 credits a day, about 2,850 credits a month (about US$285 at the top-up price, US$228 at the Scale plan's rate)
Who owns whatYou own the project and its billing; RAD deploys and updates itRAD 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, and less on a monthly 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 GiBUS$39.68
Cloud SQL for PostgreSQL, 1 vCPU / 3.75 GB, zonal, 10 GB SSDUS$51.02
NFS/cache file server (small VM)US$13.43
Cloud NAT and networkingUS$5
Cloud Storage (add-ons, backups)US$1
GKE cluster management feeshared across every GKE app in the project; $0 if this is your only cluster, else ~73
Totalabout US$110, or about US$183 without the free tier
  • GKE's free tier covers the management fee of one Autopilot or zonal cluster per billing account, so it applies only if this is your only such cluster. Cloud SQL costs the same in africa-south1 (Johannesburg); the pod and VM cost about 10% more there.
  • GKE Autopilot does not scale to zero. At least one Outline pod always runs, so the cost above is the same whether or not anyone is using it; extra pods are added only under load.
  • Avoid Filestore for a small deployment. The module uses a small NFS/cache VM by default. Filestore's smallest instance is 1 TiB, which costs about US$164 a month on its own.

How it compares​

  • Outline sells its own hosted Cloud plan. The Starter plan is a flat US$10/month for a team of up to 10 members (getoutline.com/pricing, 8 October 2026), rising to US$79/month for 11–100 members; both plans include the same features.
  • RAD's own-project figure above is higher in cash terms because it is dedicated infrastructure, not a shared multi-tenant service — a dedicated Cloud SQL instance, NFS file server and Cloud Run service, rather than a slice of Outline's own.
  • A bare VPS — Hetzner CPX22 or a DigitalOcean 2 vCPU/4 GB Droplet, both about US$24/month — undercuts RAD's own-project figure too, at the cost of running PostgreSQL, Redis, backups and patches yourself. Outline only needs 1 vCPU / 1–2 GiB, so a smaller/cheaper VPS tier than that would likely do.
  • Outline Cloud is the cheapest of the three until a team outgrows 10 members or needs the data to stay inside its own Google Cloud project.

Pause it for free: delete a RAD-managed project, restore it when you need it​

If Outline 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 occasional use — studying for a certification, a demo environment, a seasonal business — far better than running Outline 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, any VM or the GKE cluster 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 costs a handful of credits (under US$1) for a typical 2–3-deployment chain, against the 180 credits (US$18) a full redeploy costs below.
  • So a month of occasional use can cost a few dollars, not US$110. Deploy Outline, use it for a while, 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 time Outline 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. Outline's 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 made changes to Outline you want to keep, copy a backup out (to Google Drive, or a bucket outside the project) before you delete, the same as the redeploy workflow below. 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 does not scale to zero between requests. 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), because RAD recreates the project and shared services before Outline. Deleting saves money only once Outline would otherwise sit unused for about 5 days or more, both in your own project (about US$3.67 a day) and in a RAD-managed one (95 credits a day).
  • Most of the running cost is usually the database and the shared file/cache VM. They stop only when nothing else in the project uses them, so deleting Outline while something else shares the project saves only Outline's own compute part.
  • Keep data first. 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.
  • 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; radmodules.dev/pricing for RAD's own fees and daily-credit estimates; Delete and restore projects for the pause/restore window and what it says about Cloud Storage objects without soft delete; Outline pricing.


1. Overview​

Outline runs as a single Node.js workload with a mandatory Redis dependency (used for realtime collaboration/session coordination, not just caching). The deployment wires together a focused set of Google Cloud services:

CapabilityGoogle Cloud serviceNotes
ComputeGKE AutopilotNode.js pod on port 3000, 1 vCPU / 2 GiB by default
DatabaseCloud SQL for PostgreSQL 15Required — Outline (Sequelize) supports Postgres only
Cache / coordinationRedis (self-managed, co-hosted on the NFS VM by default)Required, not optional — Outline needs it even single-replica
File persistenceCloud Filestore (NFS)Uploaded files persist under /var/lib/outline/data, shared across pods
Object storageCloud StorageTwo buckets provisioned by default (...-storage, ...-data); neither is mounted into the pod out of the box
SecretsSecret ManagerAuto-generated SECRET_KEY and UTILS_SECRET; database password
IngressCloud Load BalancingExternal LoadBalancer with a reserved static IP by default; optional custom domain + managed certificate, IAP, Cloud Armor

Sensible defaults worth knowing up front:

  • PostgreSQL 15 is effectively mandatory (database_type defaults to POSTGRES_15) — Outline's Sequelize migrations, pg_trgm search extension, and entrypoint connection-string logic all assume Postgres.
  • Redis is required, not optional. enable_redis = true by default; with enable_nfs = true (also default) the foundation points REDIS_HOST at the NFS server IP, which co-hosts Redis — no separate Memorystore needed.
  • Multi-replica by default (min_instance_count = 1, max_instance_count = 3) — unlike most NFS-backed apps here, Outline is designed to run several replicas concurrently (Redis coordinates state).
  • NFS-backed rollouts still use Recreate, stopping all replicas before starting the new set — with max_instance_count = 3 that's a brief full outage on every redeploy, not a rolling one.
  • Two Cloud Storage buckets are created but unused by default — one from Outline_Common (storage), one from this variant's own default (data). Outline actually stores uploads on NFS (FILE_STORAGE=local); neither bucket is mounted via gcs_volumes unless configured.
  • SECRET_KEY/UTILS_SECRET are auto-generated in Secret Manager and wired both as Secret Manager references and raw explicit_secret_values (a read-after-write hedge; the Secret-Manager-ID path wins).
  • An auth provider is required before login works. The OIDC_* vars ship blank; Outline builds its OIDC redirect_uri from URL, so with no provider configured it registers zero auth methods. See Section 3.
  • The default LoadBalancer is plain HTTP, which breaks OAuth logins outright until a domain and TLS are added — see Section 6.

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 Outline workload​

Outline pods are scheduled on Autopilot, which bills for the CPU/memory the pods actually request. Because enable_nfs = true, the Deployment uses the Recreate strategy — with max_instance_count > 1 this means every rollout stops all replicas before starting the replacement set.

  • Console: Kubernetes Engine → Workloads → select the Outline workload for pods, revisions, and events. Kubernetes Engine → Services & Ingress shows the external IP.
  • CLI:
    kubectl get pods,svc -n "$NAMESPACE" --selector app~outline
    kubectl logs -n "$NAMESPACE" deploy/<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.

B. Cloud SQL for PostgreSQL 15​

Outline stores all documents, collections, users, and search indexes in a managed Cloud SQL for PostgreSQL 15 instance, reached through the Cloud SQL Auth Proxy sidecar on 127.0.0.1:5432 (enable_cloudsql_volume = true); no public IP is exposed. On first deploy the db-init job creates the database/user/grants; the custom entrypoint runs Outline's Sequelize migrations on every container start (idempotent).

  • 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. Redis​

Outline's collaboration/session layer and API rate limiting require Redis — this is not an optional cache tier the way it is for many apps. By default the foundation points REDIS_HOST at the NFS server VM's internal IP (it co-hosts a Redis process); set redis_host to use Memorystore instead.

  • Console: Compute Engine → VM instances (self-hosted Redis on the NFS VM), or Memorystore → Redis instances if you switched to a managed one.
  • CLI:
    kubectl exec -n "$NAMESPACE" deploy/<service-name> -- env | grep -E '^REDIS_'
    gcloud redis instances list --project "$PROJECT" --region "$REGION" # only if using Memorystore

See App_GKE for how enable_redis/redis_host resolve.

D. Cloud Filestore (NFS) & Cloud Storage​

Outline's uploaded files (FILE_STORAGE=local) live on NFS (Cloud Filestore), mounted at /var/lib/outline/data, shared across all pods. Two Cloud Storage buckets (suffixes storage and data) are also provisioned automatically — neither is mounted into the pod by default, since Outline is configured for local/NFS storage rather than S3-compatible object storage.

  • Console: Filestore → Instances; Cloud Storage → Buckets.
  • CLI:
    gcloud filestore instances list --project "$PROJECT"
    kubectl get pvc -n "$NAMESPACE"
    gcloud storage buckets list --project "$PROJECT" --filter="name~outline"

See App_GKE for CMEK options and GCS Fuse mounts (gcs_volumes) if you want to wire one of the provisioned buckets in as a mounted volume.

E. Secret Manager​

Two Outline-specific secrets are generated automatically and stored in Secret Manager: SECRET_KEY (encrypts cookies and sensitive data at rest) and UTILS_SECRET (internal API/utilities authentication). The database password is managed separately by the foundation. On GKE, Secret-Manager-backed values are materialised into the cluster as native Kubernetes Secrets by the SecretSync controller (the secretsyncs.secret-sync.gke.io CRD), which the App_GKE foundation waits for during cluster bootstrap.

  • Console: Security → Secret Manager.
  • CLI:
    gcloud secrets list --project "$PROJECT" --filter="name~outline"
    gcloud secrets versions access latest --secret=<secret-key-secret-name> --project "$PROJECT"
    kubectl get secret -n "$NAMESPACE" # the SecretSync-materialised "<prefix>-secrets" object

See App_GKE for the SecretSync/CSI integration and rotation.

F. Networking & ingress​

By default the workload is exposed through an external Cloud Load Balancing IP (service_type = LoadBalancer, reserve_static_ip = true, plain HTTP — no TLS terminator). A custom domain with a Google-managed certificate can be enabled via enable_custom_domain + application_domains, which is required for Outline's OAuth logins to work at all (see Section 6).

  • 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, IAP, Cloud Armor, and static IP details.

G. Cloud Logging & Monitoring​

Pod stdout/stderr flow to Cloud Logging; GKE and Cloud SQL metrics flow to Cloud Monitoring. uptime_check_config is disabled by default for this module (enabled = false) — enable it once the app is reachable over HTTPS.

  • 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. Outline Application Behaviour​

  • First-deploy database setup. The db-init job runs db-init.sh using postgres:15-alpine. It connects to Cloud SQL through the Auth Proxy sidecar, idempotently creates the application role and database (owned by that role), grants privileges on the database and public schema, then signals the proxy sidecar to shut down (quitquitquit) so the Job completes. Safe to re-run (execute_on_apply = true).
  • Migrations run on every container start, not in a separate job. The custom entrypoint.sh (baked into the image at build time) assembles DATABASE_URL from the platform-injected DB_* values (branching on whether DB_HOST is a socket path, 127.0.0.1, or a private IP), waits for Postgres to accept connections, runs Outline's Sequelize migrations (sequelize db:migrate --env=production-ssl-disabled, idempotent), and only then execs the Node server.
  • URL must resolve to the public address, or OIDC registers zero providers. Outline derives its OIDC redirect_uri from URL. Unlike the Cloud Run variant (which sets service_url_env_var_name = "URL" so the foundation injects the predicted URL directly), the GKE variant declares no service_url_env_var_name variable — App_GKE unconditionally injects the computed URL as GKE_SERVICE_URL, and the entrypoint applies URL="${CLOUDRUN_SERVICE_URL:-${GKE_SERVICE_URL:-}}" only if URL isn't already set. So a fresh deploy fills URL in automatically from the external LoadBalancer IP (or https://<domain> once application_domains is set) — no manual URL override needed unless forcing a hostname ahead of DNS/cert provisioning.
  • OIDC placeholders — operator must configure post-deploy. OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_AUTH_URI, OIDC_TOKEN_URI, OIDC_USERINFO_URI ship blank (OIDC_DISPLAY_NAME/OIDC_SCOPES are pre-filled). Set them via environment_variables, or preferably bind OIDC_CLIENT_ID/OIDC_CLIENT_SECRET as secret_environment_variables (see Section 6). Register <URL>/auth/oidc.callback on the same host as URL.
  • Health probes. Startup: HTTP GET /, 60s initial delay, 10s period, failure threshold 6 (~120s budget). Liveness: HTTP GET /, 60s initial delay, 30s period, failure threshold 3 — the generous delay covers the migration run on first boot. FORCE_HTTPS is forced to "false" by Outline_Common so these HTTP probes aren't 301-redirected to a :443 with no listener behind the default plain-HTTP LoadBalancer.
  • Scaling constraints. min_instance_count = 1, max_instance_count = 3 by default. Because enable_nfs = true, rollouts use the Recreate strategy — all replicas are stopped before the new ones start, so expect a short full outage on every redeploy (not a rolling, zero-downtime one).
  • 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" deploy/<service-name> -- env | grep -E '^(URL|OIDC_|REDIS_|DATABASE_URL)'

4. Configuration Variables​

Variables are grouped exactly as they appear on the deployment platform (matching the {{UIMeta group=N}} tags in variables.tf). Only settings specific to or notable for Outline are listed; every other input is inherited from App_GKE with its standard behaviour and defaults.

Group 3 — Application Identity​

VariableDefaultDescription
application_nameoutlineBase name for resources. Do not change after first deploy.
application_versionlatestoutlinewiki/outline image tag used as the custom-build base.

Group 4 — Runtime & Scaling​

VariableDefaultDescription
container_port3000Outline's Node.js server listens on 3000.
container_resources.cpu_limit / .memory_limit1000m / 2Gi1 vCPU / 2 GiB minimum recommended.
min_instance_count / max_instance_count1 / 3Higher default ceiling than most modules — Outline supports multi-replica via Redis coordination.
enable_cloudsql_volumetrueAuth Proxy sidecar (loopback 127.0.0.1:5432) — required on GKE.

Group 5 — Environment Variables & Secrets​

VariableDefaultDescription
environment_variables(see Outline_Common)Pre-populated with PGSSLMODE, FORCE_HTTPS=false, FILE_STORAGE*, and blank OIDC_* placeholders; set the auth provider and, optionally, URL here.
secret_environment_variables{}Secret Manager references injected via SecretSync; use for OIDC_CLIENT_ID/OIDC_CLIENT_SECRET instead of plain text.

Group 6 — GKE Backend & Cluster​

VariableDefaultDescription
service_typeLoadBalancerExternal IP for the Outline UI; plain HTTP by default.
workload_typenull → DeploymentDeployment, Recreate strategy (NFS-backed).
session_affinityClientIPSticky routing so a client reaches the same pod.

Group 13 — Filesystem (NFS)​

VariableDefaultDescription
enable_nfstrueRequired for uploaded files to persist and be shared across replicas.
nfs_mount_path/var/lib/outline/dataMust match FILE_STORAGE_LOCAL_ROOT_DIR set by Outline_Common.

Group 14 — Cloud Storage​

VariableDefaultDescription
storage_buckets[{ name_suffix = "data" }]Merged with Outline_Common's own storage bucket — two buckets are created; neither is used by the app by default.
gcs_volumes[]Not populated by default; Outline uses NFS, not GCS Fuse, for uploads.

Group 15 — Redis​

VariableDefaultDescription
enable_redistrueRequired by Outline — do not disable.
redis_host""Leave blank to use the co-hosted Redis on the NFS VM; set for an external Memorystore instance.
redis_port6379Redis port.

Group 16 — Database Backend​

VariableDefaultDescription
database_typePOSTGRES_15PostgreSQL only — Outline's migrations and pg_trgm extension assume Postgres.
application_database_nameoutlineDatabase name. Immutable after first deploy.
application_database_useroutlineApplication database user; password auto-generated in Secret Manager.
postgres_extensions["pg_trgm"]Required for Outline's full-text document search.

Group 19 — Custom Domain, Static IP & Networking​

VariableDefaultDescription
enable_custom_domaintrueProvisions an Ingress; needed for real (non-nip.io) TLS.
application_domains[]Setting a real hostname here (plus a cert) is what actually gets service_url/URL onto https:// — see Section 6.
reserve_static_iptrueStable external IP across redeploys.

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.

OutputDescription
service_nameKubernetes Service name.
namespaceNamespace the workload runs in.
service_cluster_ipIn-cluster ClusterIP.
stage_service_cluster_ipsMap of ClusterIPs for stage-specific services.
service_external_ipExternal LoadBalancer IP (when a static IP is reserved).
service_urlURL to reach Outline (also injected into the container as GKE_SERVICE_URL).
database_instance_nameCloud SQL instance name.
database_name / database_userApplication database name / user.
database_password_secretSecret Manager secret holding the DB password.
database_host / database_portDB endpoint (127.0.0.1 via the Auth Proxy) / port.
storage_bucketsCreated Cloud Storage buckets.
network_name / network_exists / regionsVPC network, presence, available regions.
container_image / container_registryDeployed image and Artifact Registry repo.
monitoring_enabled / monitoring_notification_channelsMonitoring status and channels.
initialization_jobs / db_import_jobNames of the setup (db-init) and (optional) import jobs.
deployment_id / tenant_id / resource_prefixNaming identifiers.
project_id / project_numberProject identifiers.
cicd_enabled / cicd_configurationCI/CD status and details (repo, trigger, registry).
github_repository_url / github_repository_owner / github_repository_nameCI/CD GitHub details.
artifact_registry_repository / cloudbuild_trigger_name / cloudbuild_trigger_idRegistry and build trigger.
kubernetes_readyWhether the cluster/workload is ready.
vpc_sc_enabled / vpc_sc_perimeter_name / vpc_sc_dry_run_modeVPC-SC status.
audit_logging_enabled / artifact_registry_cmek_enabledAudit 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 StatefulSet forced alongside a stateless setting, IAP with no authorized identities, quota_memory_* given as bare integers, an out-of-range container_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.

SettingSensible valueRiskConsequence if wrong
database_typePOSTGRES_15CriticalOutline's Sequelize migrations and pg_trgm search extension assume Postgres; a non-Postgres engine breaks the schema and search.
application_database_name / application_database_userSet onceCriticalImmutable after first deploy; renaming recreates the DB/user and orphans all documents.
SECRET_KEY (auto-generated)Never changeCriticalRotating it invalidates every existing session cookie and any data encrypted at rest with the old key.
URL / auto-injected GKE_SERVICE_URLLeft unset (auto) or explicitly the real public URLCriticalIf it resolves to the wrong host, Outline's OIDC redirect_uri won't match what's registered with the IdP, and/or zero providers register — unusable login page.
HTTPS in front of the ServiceConfigure enable_custom_domain + application_domains + TLS before enabling authCriticalPassport sets the OAuth state cookie secure: true; over the default plain-HTTP L4 LoadBalancer, /auth/<provider> returns 500 — Cannot send secure cookie over unencrypted connection. The landing page loads fine, masking the real cause.
enable_redistrueCriticalOutline requires Redis even at a single replica; disabling it breaks realtime/session coordination and the app will not function correctly.
enable_nfstrueHighDisabling it makes uploaded files ephemeral — lost on pod recreation — and breaks sharing across replicas.
OIDC_CLIENT_ID / OIDC_CLIENT_SECRET moved from plain to secret-backedSet only in secret_environment_variables; remove from environment_variables in the same applyHighThese ship as plain empty-string env vars. Unlike Cloud Run's imperative gcloud run services update (which needs a --remove-env-vars step first, erroring "already set with a different type"), GKE's switch is declarative — tofu apply renders the whole desired env list in one pass. But leaving the key in both maps at once puts two env entries of the same name in the Pod spec (one value, one valueFrom.secretKeyRef); Kubernetes accepts this, but which value the process observes is unverified here — remove the plain-text key when adding the secret-backed one.
max_instance_count with enable_nfs = trueUnderstand the Recreate trade-offHighEvery redeploy stops all running replicas (not just a surge pod) before starting the replacement set — a brief full outage on every update, worse than the single-replica case in most other NFS-backed modules.
quota_memory_requests / _limitsbinary units (4Gi, 8192Mi)CriticalBare integers are treated as bytes and block all pod scheduling in the namespace.
reserve_static_iptrueMediumWithout it, the external IP can change across redeploys, breaking DNS, the registered OIDC redirect URI, and URL.
uptime_check_config.enabledfalse by defaultMediumNo automatic outage alerting until you turn it on — sensible to leave off until HTTPS/auth are configured, since the app is expected to be unusable (crashlooping or 500 on login) until then.
backup_retention_days7 (raise for prod)MediumToo 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. Outline-specific application configuration shared with the Cloud Run variant — the auto-generated secrets, default environment variables, entrypoint.sh/Dockerfile, and the db-init job — is described in Outline_Common.

Need RAD to do something it does not do yet? Request it on the roadmap, or vote on what is already there.