Odoo GKE Module — Configuration Guide
Odoo is a comprehensive open-source ERP platform covering CRM, accounting, inventory, manufacturing, HR, eCommerce, and more. This module deploys Odoo Community Edition on GKE Autopilot using a custom container image built from the official Odoo nightly packages, backed by a managed Cloud SQL PostgreSQL instance and a Filestore NFS volume for shared file storage.
Odoo GKE is a wrapper module built on top of App GKE. It uses App GKE for all GCP infrastructure provisioning (cluster, networking, Cloud SQL, GCS, secrets, CI/CD) and adds Odoo-specific application configuration, initialisation jobs, and runtime defaults on top.
Note: Variables marked as platform-managed are set and maintained by the platform. You do not normally need to change them.
How This Guide Is Structured
This guide documents only the variables that are unique to Odoo_GKE or that have Odoo-specific defaults that differ from the App_GKE base module. For all other variables — project identity, GKE backend configuration, CI/CD, custom SQL scripts, observability alerting, networking, IAP, and Cloud Armor — refer directly to the App_GKE Configuration Guide.
Variables fully covered by the App GKE guide:
| Configuration Area | App GKE.md Section | Odoo-Specific Notes |
|---|---|---|
| Module Metadata & Configuration | §1 Module Overview | Different defaults for module_description and module_documentation. resource_creator_identity behaves identically. |
| Project & Identity | §2 IAM & Access Control | Refer to base App GKE module documentation. |
| Runtime & Scaling | §3.A Compute (GKE Autopilot) | See Odoo Runtime Configuration below. container_port defaults to 8069. session_affinity defaults to "ClientIP". |
| Environment Variables & Secrets | §3.A Compute (GKE Autopilot) | See Odoo Environment Variables below for SMTP defaults. |
| GKE Backend Configuration | §3.A Compute (GKE Autopilot) | Refer to base App GKE module documentation. Note: session_affinity defaults to "ClientIP" for Odoo. |
| Jobs & Scheduled Tasks | §3.E Initialization Jobs & CronJobs | Refer to base App GKE module documentation. The module also injects two platform-managed initialisation jobs — see Platform-Managed Behaviours. |
| CI/CD & GitHub Integration | §6 CI/CD & Delivery | Refer to base App GKE module documentation. The module injects ODOO_VERSION as a Cloud Build substitution variable automatically. |
| Storage — NFS | §3.C Storage (NFS / GCS / GCS Fuse) | NFS is enabled by default (enable_nfs = true). See Platform-Managed Behaviours for the NFS initialisation job. |
| Storage — GCS | §3.C Storage (NFS / GCS / GCS Fuse) | Refer to base App GKE module documentation. |
| Backup Schedule & Retention | §8.B Backup Import & Recovery | Refer to base App GKE module documentation. See also Backup Import & Recovery below. |
| Custom SQL Scripts | §3.E Initialization Jobs & CronJobs | Refer to base App GKE module documentation. |
| Observability & Health | §5 Traffic & Ingress | See Odoo Health Probes below for Odoo-specific probe paths and timing defaults. |
| Reliability Policies | §7 Reliability & Scheduling | Refer to base App GKE module documentation. enable_pod_disruption_budget defaults to true. |
| Resource Quota | §7.C Resource Quotas | Refer to base App GKE module documentation. |
| Custom Domain, Static IP & Network | §5.C Static IP | Refer to base App GKE module documentation. |
| Identity-Aware Proxy | §4.B Identity-Aware Proxy (IAP) | Refer to base App GKE module documentation. |
| Cloud Armor WAF | §4.A Cloud Armor WAF | Refer to base App GKE module documentation. |
| VPC Service Controls | §4.D VPC Service Controls | Refer to base App GKE module documentation. |
| StatefulSet Configuration | §3.A Compute (GKE Autopilot) | Refer to base App GKE module documentation. |
| Database Configuration | §3.B Database (Cloud SQL) | database_type must be "POSTGRES". See Odoo Database Configuration below. |
| Networking & Network Policies | §3.D Networking & Network Policies | Refer to base App GKE module documentation. |
| Additional Services | §3.F Additional Services | Refer to base App GKE module documentation. |
| Binary Authorization | §4.C Binary Authorization | Refer to base App GKE module documentation. |
| Secrets Store CSI | §4.E Secrets Store CSI | Refer to base App GKE module documentation. |
| Cloud CDN | §5.B Cloud CDN | Refer to base App GKE module documentation. |
| Pod Disruption Budget | §7.A Pod Disruption Budget | enable_pod_disruption_budget defaults to true. |
| Topology Spread | §7.B Topology Spread | Refer to base App GKE module documentation. |
| Auto Password Rotation | §7.D Auto Password Rotation | Refer to base App GKE module documentation. |
| Redis / Memorystore | §8.A Redis / Memorystore | enable_redis defaults to false. See Redis Session Store below. |
| Service Mesh | §8.C Service Mesh | Refer to base App GKE module documentation. |
| Multi-Cluster Services | §8.D Multi-Cluster Services | Refer to base App GKE module documentation. |
Platform-Managed Behaviours
The following behaviours are applied automatically by Odoo GKE regardless of the variable values in your tfvars file. They cannot be overridden by user configuration.
| Behaviour | Detail |
|---|---|
| NFS directory initialisation | A nfs-init Kubernetes Job runs automatically on every apply. It mounts the Filestore NFS share and creates the directories /mnt/filestore, /mnt/sessions, and /mnt/extra-addons, setting ownership to UID/GID 101:101 (the Odoo process user) with mode 777. This is required before Odoo starts or it will fail to write session and filestore data. |
| Database initialisation | A db-init Kubernetes Job runs after nfs-init to create the Odoo database and application user using the credentials stored in Secret Manager. The job runs as a PostgreSQL client against the Cloud SQL instance. |
| ODOO_MASTER_PASS secret | A 16-character alphanumeric master password is auto-generated by Odoo Common and stored in Secret Manager under the name app{application_name}{tenant_deployment_id}{random_hex}-master-password (where random_hex is an internally-generated deployment suffix, not the user-supplied deployment_id). The plaintext value is passed to App GKE via explicit_secret_values, which writes it into a Kubernetes Secret and injects it as the ODOO_MASTER_PASS environment variable. |
| Custom Dockerfile | When container_image_source = "custom", Cloud Build uses the Odoo_Common Dockerfile which installs the Odoo version specified by application_version from the official Odoo nightly package repository. The ODOO_VERSION build argument is automatically injected from application_version. |
| SMTP environment defaults | The environment_variables map is pre-populated with Odoo SMTP configuration keys (SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_SSL, EMAIL_FROM). Override these to configure outbound email for Odoo notifications and alerts. |
| Pod Disruption Budget enabled | enable_pod_disruption_budget defaults to true with pdb_min_available = "1", ensuring at least one Odoo pod remains available during node maintenance. This is appropriate for stateful workloads. |
Odoo Application Identity
These variables define how the Odoo deployment is named across GCP and Kubernetes resources.
| Variable | Default | Options / Format | Description & Implications |
|---|---|---|---|
application_name | "odoo" | [a-z][a-z0-9-]{0,19} | Internal identifier used as the base name for the Kubernetes Deployment, Namespace, Cloud SQL database, GCS buckets, and Secret Manager secrets. Functionally identical to application_name in App GKE. Do not change after initial deployment. |
application_display_name | "Odoo ERP" | Any string | Human-readable name shown in the platform UI and GKE monitoring dashboards. Can be updated freely without affecting resource names. |
application_description | "Odoo ERP on GKE Autopilot" | Any string | Brief description of the deployment. Populated into Kubernetes resource annotations. Can be updated freely. |
application_version | "18.0" | Odoo version string, e.g. "18.0", "17.0" | For Odoo this is the Odoo release version, not a semver tag. It maps directly to the Odoo nightly package channel used in the Dockerfile (ODOO_VERSION build arg). Supported values are the Odoo long-term supported (LTS) versions: "18.0", "17.0", "16.0". When container_image_source = "custom", changing this value triggers a new Cloud Build run that installs the specified Odoo version. |
Validating Application Identity
# Confirm the Deployment exists with the expected name
kubectl get deployments -n NAMESPACE -o wide
# Confirm the Odoo version running in the container
kubectl exec -n NAMESPACE deploy/odoo -- odoo --version
Odoo Runtime Configuration
Odoo is a Python/PostgreSQL ERP application that requires more resources than a generic web service, particularly during initial database creation and module installation.
Container Port
| Variable | Default | Options / Format | Description & Implications |
|---|---|---|---|
container_port | 8069 | Integer, 1–65535 | The port Odoo listens on for HTTP traffic. The Odoo server binds to 0.0.0.0:8069 by default. Do not change this unless you have modified the Odoo server configuration to listen on a different port. |
Resource Sizing
The container_resources variable behaves identically to App_GKE (see App_GKE §3.A), but the Odoo defaults are lower than recommended for production:
| Variable | Module Default | Recommended for Production |
|---|---|---|
container_resources.cpu_limit | "1000m" | "2000m" or higher |
container_resources.memory_limit | "512Mi" | "4Gi" (minimum "2Gi") |
container_resources.cpu_request | null | "2000m" |
container_resources.mem_request | null | "4Gi" |
Odoo's Python worker processes and database connection pool together consume 1.5–3 Gi of memory under normal load. Setting memory_limit below "2Gi" will cause OOM kills during peak activity. The initial module installation on first boot also requires significant CPU — under-sizing CPU causes very slow first-boot behaviour.
Recommended production configuration:
container_resources = {
cpu_limit = "2000m"
memory_limit = "4Gi"
cpu_request = "2000m"
mem_request = "4Gi"
}
Session Affinity
| Variable | App GKE Default | Odoo GKE Default | Description & Implications |
|---|---|---|---|
session_affinity | "None" | "ClientIP" | Odoo stores HTTP session data on the local filesystem (/mnt/sessions on NFS) keyed by session ID, but worker processes are assigned by the xmlrpc load balancer. Setting "ClientIP" ensures a given client consistently reaches the same pod, avoiding cross-pod session lookup overhead. Change to "None" only if Redis session storage is configured and all workers share session state. |
Validating Runtime Configuration
# View container resource limits on the running pod
kubectl describe pod -n NAMESPACE -l app=odoo | grep -A10 "Limits:"
# Confirm session affinity on the Service
kubectl get service odoo -n NAMESPACE -o jsonpath='{.spec.sessionAffinity}'
Odoo Health Probes
Odoo performs database schema validation and, on first boot, full module installation. This startup phase can take 2–10 minutes on a fresh deployment, depending on the number of installed modules and available CPU. The startup_probe_config and health_check_config variables in Odoo GKE have Odoo-specific defaults for the /web/health endpoint.
| Variable | Default | Description & Implications |
|---|---|---|
startup_probe_config | { enabled = true, type = "HTTP", path = "/web/health", initial_delay_seconds = 180, timeout_seconds = 60, period_seconds = 120, failure_threshold = 3 } | Determines when the Odoo pod is ready to serve traffic. The initial_delay_seconds = 180 gives Odoo time to load Python modules and perform database migration before the first probe fires. period_seconds = 120 and failure_threshold = 3 allow up to 6 minutes of additional startup time. On first deployment (when the schema is created from scratch), consider increasing failure_threshold to 5 or initial_delay_seconds to 300. |
health_check_config | { enabled = true, type = "HTTP", path = "/web/health", initial_delay_seconds = 30, timeout_seconds = 5, period_seconds = 30, failure_threshold = 3 } | Periodically checks whether a running Odoo instance is healthy. The /web/health endpoint returns HTTP 200 only when Odoo has a live database connection and the application is operational. A period_seconds = 30 check is appropriate. Kubernetes will restart the pod if this probe fails 3 consecutive times. |
Probe routing in Odoo_GKE:
startup_probe_configandhealth_check_configeach serve a dual role — they are passed toOdoo_Common(asstartup_probeandliveness_proberespectively) to configure the Kubernetes container probes, and also forwarded directly toApp_GKEto configure the load balancer backend health checks. Other App_GKE wrapper modules use separatestartup_probe/liveness_probevariables for container probes; Odoo consolidates both paths into the single_configpair. See App_GKE §5 Traffic & Ingress for the App_GKE field reference.
Validating Health Probes
Google Cloud Console: Navigate to Kubernetes Engine → Workloads → odoo and check the Events panel for probe failure messages.
# View probe configuration on the running deployment
kubectl describe deployment odoo -n NAMESPACE | grep -A30 "Liveness:"
# Tail Odoo pod logs to monitor startup progress
kubectl logs -n NAMESPACE -l app=odoo --follow | grep -E "odoo.modules|http.server"
# Manually test the health endpoint from within the cluster
kubectl exec -n NAMESPACE deploy/odoo -- curl -s -o /dev/null -w "%{http_code}" http://localhost:8069/web/health
# Expect: 200
Odoo Database Configuration
Odoo requires PostgreSQL. The database is provisioned by the underlying App_GKE module — see App_GKE §3.B for the full variable reference.
The following defaults are Odoo-specific and differ from the App GKE defaults:
| Variable | App GKE Default | Odoo GKE Default | Recommendation |
|---|---|---|---|
application_database_name | "gkeappdb" | "gkeappdb" | Set to "odoo" to match Odoo conventions. |
application_database_user | "gkeappuser" | "gkeappuser" | Set to "odoo" to match Odoo conventions. |
database_type | "POSTGRES" | "POSTGRES" | Must remain PostgreSQL. Setting database_type = "NONE" or a MySQL/SQL Server type will prevent Odoo from starting. |
Note on PostgreSQL extensions: The
db-initjob (see Platform-Managed Behaviours) creates the database and user but does not install extensions. If your Odoo deployment uses modules that require PostgreSQL extensions such aspostgisorunaccent, enable them usingenable_postgres_extensions = trueandpostgres_extensions = ["postgis", "unaccent"].
Validating Database Configuration
# Confirm the database and user were created by the db-init job
gcloud sql databases list --instance=INSTANCE_NAME --project=PROJECT_ID
gcloud sql users list --instance=INSTANCE_NAME --project=PROJECT_ID
# Confirm DB environment variables are injected into the running pod
kubectl exec -n NAMESPACE deploy/odoo -- env | grep -E "^(DB_|PGHOST|PGUSER)"
Odoo Environment Variables
The environment_variables variable (documented in App_GKE §3.A) has Odoo-specific defaults that configure outbound email delivery.
Default environment_variables in Odoo GKE:
environment_variables = {
SMTP_HOST = ""
SMTP_PORT = "25"
SMTP_USER = ""
SMTP_PASSWORD = ""
SMTP_SSL = "false"
EMAIL_FROM = "odoo@example.com"
}
Odoo uses these variables to configure its outbound mail transport, which is required for order confirmations, password resets, and CRM notifications. Configure them to point to your SMTP server before going live. Move sensitive values such as SMTP_PASSWORD to secret_environment_variables:
environment_variables = {
SMTP_HOST = "smtp.sendgrid.net"
SMTP_PORT = "587"
SMTP_USER = "apikey"
SMTP_SSL = "true"
EMAIL_FROM = "noreply@yourcompany.example.com"
}
secret_environment_variables = {
SMTP_PASSWORD = "odoo-smtp-password" # Secret Manager secret name
}
All other environment_variables and secret_environment_variables behaviour is identical to App_GKE — refer to App_GKE §3.A.
Redis Session Store
Odoo supports Redis as a shared session store. When multiple Odoo pods are running (i.e. max_instance_count > 1), Redis is strongly recommended to avoid session loss when a request is routed to a pod that does not hold the user's local session. Without Redis, session_affinity = "ClientIP" mitigates this but does not eliminate the risk during pod restarts. The Redis integration is provided by App_GKE — see §8.A Redis / Memorystore for the full integration reference.
| Variable | Default | Options / Format | Description & Implications |
|---|---|---|---|
enable_redis | false | true / false | When true, Odoo is configured to use the Redis instance at redis_host:redis_port for session storage. The SESSION_REDIS environment variable is set to true and REDIS_HOST/REDIS_PORT are injected automatically. Requires redis_host to be set to a reachable Redis endpoint (e.g. a Google Memorystore for Redis instance IP). |
redis_host | "" | IP address or hostname | The Redis server hostname or IP. For Google Memorystore, use the instance's primary endpoint IP (available in the Cloud Console under Memorystore → Redis → your instance → Properties). Required when enable_redis = true. |
redis_port | "6379" | Port string (e.g. "6379") | The port on which Redis is listening. Defaults to the standard Redis port. Change only if your Redis instance is configured on a non-standard port. |
redis_auth | "" | String (sensitive) | Authentication password for the Redis server. Leave empty for unauthenticated Redis. For Memorystore instances with AUTH enabled, set this to the instance's auth string. Treated as sensitive — not stored in Terraform state in plaintext. |
Validating Redis Configuration
# Confirm REDIS_HOST and REDIS_PORT are injected into the running pod
kubectl exec -n NAMESPACE deploy/odoo -- env | grep -E "^REDIS_"
# Test Redis connectivity from within the pod (requires redis-cli on the image)
kubectl exec -n NAMESPACE deploy/odoo -- redis-cli -h REDIS_HOST -p REDIS_PORT PING
# Expect: PONG
Backup Import & Recovery
In addition to the scheduled backup (backup_schedule and backup_retention_days, documented in App_GKE §8.B), Odoo_GKE supports a one-time database import during deployment. Use this to migrate an existing Odoo instance to GCP or to seed a new environment with production data.
The backup import variables behave identically to those in App GKE §8.B:
| Variable | Default | Description |
|---|---|---|
enable_backup_import | false | When true, triggers a one-time import job after provisioning. |
backup_source | "gcs" | "gcs" to import from the automatically created backups bucket; "gdrive" to import from a Google Drive file ID. |
backup_file | "backup.sql" | Filename within the GCS backups bucket, or the Google Drive file ID. |
backup_format | "sql" | The format of the backup: "sql", "gz", "tar", "tgz", "tar.gz", "zip", or "auto". |
The Odoo backup is a PostgreSQL dump (
pg_dump). The recommended format for Odoo is"sql"(plain text) or"gz"(gzip-compressed). The import job restores the dump into the database identified byapplication_database_name.
For the full variable reference and validation steps, refer to App_GKE §8.B.
Deployment Prerequisites & Validation
After deploying Odoo GKE, confirm the deployment is healthy:
# Confirm both initialisation jobs completed successfully
kubectl get jobs -n NAMESPACE
# View nfs-init job logs
kubectl logs -n NAMESPACE -l job-name=nfs-init
# View db-init job logs
kubectl logs -n NAMESPACE -l job-name=db-init
# Confirm the Odoo pod is running and healthy
kubectl get pods -n NAMESPACE -l app=odoo
# Retrieve the external IP of the Odoo LoadBalancer service
kubectl get svc -n NAMESPACE
# Test the Odoo web interface (replace EXTERNAL_IP with the LoadBalancer IP)
curl -s -o /dev/null -w "%{http_code}" http://EXTERNAL_IP/web/health
# Expect: 200
# Confirm the ODOO_MASTER_PASS secret was created in Secret Manager
gcloud secrets list --project=PROJECT_ID --filter="name:master-password"
Configuration Pitfalls & Sensible Defaults
Risk levels: Critical (data loss, full outage, security breach) — High (service unavailable or significant degradation) — Medium (degraded function or increased cost) — Low (minor impact).
| Variable | Sensible Default | Risk | Consequence of Incorrect Value |
|---|---|---|---|
project_id | (required) | Critical | No default — deployment fails immediately. |
database_type | "POSTGRES_15" | Critical | Odoo requires PostgreSQL exclusively. Setting to MYSQL causes Odoo to fail at startup — Odoo's ORM uses psycopg2 and is incompatible with MySQL. Setting NONE leaves Odoo with no database. |
enable_nfs | true | Critical | Odoo's filestore (attachments, binary fields, session files, static assets) must reside on a shared volume. Without NFS, each pod has an isolated filestore — attachments uploaded to one pod are invisible to others, and all files are lost on pod restart. NFS is mandatory for any multi-pod Odoo GKE deployment. |
enable_redis | false | Medium | Odoo can use Redis for session management in multi-worker configurations. Without Redis, sessions are memory-resident per pod — users are logged out when routed to a different pod. For production with multiple replicas, enable Redis with an explicit redis_host. |
redis_host | "" | High | Required when enable_redis = true. An empty value causes Odoo session backend failures at startup. |
application_version | "18.0" | High | Used to select the Odoo nightly build. An invalid version tag causes Cloud Build to fail when downloading the source. Use only valid major version strings like "18.0" or "17.0". |
container_image_source | "custom" | High | Odoo requires a custom image wiring the PostgreSQL socket path and filestore configuration. The upstream Odoo Docker Hub image is not configured for Cloud SQL Unix socket connectivity. |
container_resources.memory_limit | "512Mi" (GKE default) | Critical | The base GKE container_resources.memory_limit default is 512Mi. Odoo requires at least 2Gi to start and load its ERP modules. A 512Mi limit causes immediate Python OOM crashes during module loading. Always override to at least 2Gi. |
session_affinity | "ClientIP" | High | Without session affinity, Odoo users are routed to different pods per request and lose their sessions continuously. Keep "ClientIP" for any multi-replica Odoo GKE deployment without a shared Redis session store. |
application_database_name | "odoo" | Critical | Immutable after first deployment — changing this recreates the database and destroys all ERP data. |
application_database_user | "odoo" | Critical | Immutable after first deployment — changing this recreates the user and breaks the database connection. |
workload_type | null | Medium | Setting stateful_pvc_enabled = true auto-selects StatefulSet. Do not set workload_type = "Deployment" alongside stateful_pvc_enabled = true — this fails at plan time. |
stateful_pvc_size | "10Gi" | Medium | Odoo attachments and the filestore grow quickly in any active ERP — invoices, contracts, and product images accumulate. Plan for 100Gi+ for production deployments with active document management. |
quota_memory_requests / quota_memory_limits | "" | Critical (GKE-specific) | Must use binary suffixes (Gi, Mi) when set. Bare integers are treated as bytes by Kubernetes and prevent all pods from scheduling. |
explicit_secret_values (ODOO_ADMIN_PASSWD / master password) | — | Critical | The Odoo master password controls access to the database manager (/web/database/manager). A weak or default password allows any user who can reach the URL to drop the entire Odoo database. Set a strong, unique value before production launch. |
min_instance_count | 1 | High | Scale-to-zero causes 30–60 second Odoo cold starts and disrupts Odoo's background scheduler (cron). Keep at 1 for any production ERP deployment. |
enable_pod_disruption_budget | true | Medium | Already enabled. Disabling allows all pods to terminate simultaneously during node upgrades, causing a full ERP outage. |
backup_retention_days | 7 | High | Odoo contains financial records with legal retention requirements (often 7 years). Seven days of backups is dangerously insufficient. Increase to 90+ days and consider separate long-term backup archiving. |
enable_cloud_armor | false | High | The Odoo database manager and web portal contain highly sensitive business data. Cloud Armor WAF is strongly recommended for any internet-accessible Odoo GKE deployment. |
enable_network_segmentation | false | Medium | Without NetworkPolicy, any pod in the GKE cluster can reach Odoo pods and the PostgreSQL Cloud SQL proxy. Enable for multi-tenant clusters. |