This operator allows you to deploy and manage Odoo instances on Kubernetes with ease. It handles the lifecycle of the Odoo application, including provisioning PostgreSQL databases (managed or external), handling persistent storage, managing Odoo Enterprise and custom modules from Git repositories, and performing backups and restores.
- Managed Deployment: Automated deployment of Odoo (StatefulSet) and PostgreSQL (StatefulSet or external).
- Custom Modules Management:
- Install community or custom modules directly from Git repositories.
- Support for private repositories via SSH keys.
- Automatic updates of modules when configuration changes.
- Odoo Enterprise Ready: Easy configuration to pull Enterprise addons from the official private repository.
- Resource Control: Granular configuration of CPU/Memory for all containers.
- Backup & Restore: Native support for backing up the Odoo database and restoring it (
OdooBackupandOdooRestoreCRDs). - Automatic Upgrades: Triggers migration scripts (
odoo -u ...) automatically when the Odoo version changes.
# Install CRDs and deploy the operator manager
make install
make deploy IMG=alterway/odoo-operator:latestReleases also publish a Helm chart as an OCI artifact to GHCR (see .github/workflows/release.yml), which Flux's HelmRepository can consume directly:
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: odoo-operator
namespace: flux-system
spec:
type: oci
interval: 1h
url: oci://ghcr.io/kreibich04/charts
# Only needed while the GHCR package is private:
# secretRef:
# name: ghcr-pull-secret
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: odoo-operator
namespace: flux-system
spec:
interval: 1h
chart:
spec:
chart: odoo-operator
version: ">=0.1.0"
sourceRef:
kind: HelmRepository
name: odoo-operator
install:
createNamespace: true
targetNamespace: operator-systemIf the GHCR package is private, create secretRef as a kubernetes.io/dockerconfigjson secret (flux create secret oci or kubectl create secret docker-registry) with a token that has read:packages.
Create a simple Odoo Community instance with a managed PostgreSQL database.
apiVersion: cloud.alterway.fr/v1alpha1
kind: Odoo
metadata:
name: odoo-community
namespace: default
spec:
version: "19"
size: 1
service:
type: LoadBalancer
ingress:
enabled: true
host: odoo.example.com
tls: true
storage:
data:
size: "5Gi"
postgres:
size: "5Gi"
modules:
install:
- sale
- websiteThis example demonstrates how to deploy Odoo Enterprise and install custom modules from a private Git repository.
Prerequisites:
- Create a Kubernetes Secret
ssh-key-odoocontaining the SSH private key for the Enterprise repo. - Create a Kubernetes Secret
ssh-key-customfor your private custom repository.
apiVersion: cloud.alterway.fr/v1alpha1
kind: Odoo
metadata:
name: odoo-enterprise
spec:
version: "18.0"
enterprise:
enabled: true
sshKeySecretRef: ssh-key-odoo # Secret containing 'ssh-privatekey'
modules:
# Clone custom addons from a private Git repo
repositories:
- name: my-custom-addons
url: git@github.com:my-org/my-odoo-addons.git
version: "18.0"
sshKeySecretRef: ssh-key-custom
# List of modules to install (from core, enterprise, or custom repos)
install:
- account_accountant # Enterprise module
- my_custom_module # From the custom repo
- saleBackup:
apiVersion: cloud.alterway.fr/v1alpha1
kind: OdooBackup
metadata:
name: backup-daily
spec:
odooRef:
name: odoo-enterprise
storageLocation:
pvc:
claimName: backup-pvcRestore:
apiVersion: cloud.alterway.fr/v1alpha1
kind: OdooRestore
metadata:
name: restore-job
spec:
odooRef:
name: odoo-enterprise
backupSource:
odooBackupRef:
name: backup-daily
restoreMethod: StopAndRestore # Will stop Odoo, restore DB, and restartTo enable scalable session storage, you can configure Odoo to use Redis. The operator can manage a Redis instance for you or connect to an external one.
Managed Redis:
apiVersion: cloud.alterway.fr/v1alpha1
kind: Odoo
metadata:
name: odoo-ha
spec:
version: "19"
size: 2 # Now you can scale Odoo!
redis:
enabled: true
managed: trueExternal Redis:
apiVersion: cloud.alterway.fr/v1alpha1
kind: Odoo
metadata:
name: odoo-external-redis
spec:
# ...
redis:
enabled: true
managed: false
host: "my-redis-service.default.svc.cluster.local"
port: 6379
secretRef: "my-redis-password" # Secret containing 'password' key
databaseIndex: 0
prefix: "odoo_prod"
isCacheEnabled: true
cacheDatabaseIndex: 1
options: # These options will be merged into odoo.conf. Password can be passed via env var or directly from secret.
session_store: redis
session_redis_host: my-redis-service.default.svc.cluster.local
session_redis_port: "6379"
session_redis_dbindex: "0"
session_redis_prefix: odoo_prod
session_redis_password: ${REDIS_PASSWORD} # Odoo will pick this up from environment variables if set in StatefulSet
enable_redis: "True" # For cache (requires appropriate Odoo modules)
redis_host: my-redis-service.default.svc.cluster.local
redis_port: "6379"
redis_dbindex: "1"
redis_password: ${REDIS_PASSWORD}Note: For external Redis, you must manually ensure that the REDIS_PASSWORD environment variable (or equivalent, depending on your Odoo image) is passed to the Odoo Pods, typically by referencing the secretRef in the Odoo StatefulSet configuration. When redis.managed is true, the operator handles this automatically.
The master key (admin_passwd in odoo.conf) guards Odoo's database manager (/web/database/manager, /web/database/selector) — the endpoints that create, duplicate, backup, restore, and drop databases. It is unrelated to any Odoo user's login password.
Master key resolution, in order:
masterKey.value— set the key literally in the resource.masterKey.secretRef— name of a Secret containing the key under themasterkeydata key.- If neither is set, the operator generates a random key and stores it in a Secret named
<odoo-name>-masterkey.
apiVersion: cloud.alterway.fr/v1alpha1
kind: Odoo
metadata:
name: odoo-prod
spec:
version: "19"
size: 1
masterKey:
secretRef: odoo-prod-masterkey # must contain a 'masterkey' keyDatabase manager exposure: allowDatabaseManager controls Odoo's list_db setting. It defaults to false, which 404s /web/database/manager and /web/database/selector regardless of how Odoo is reached (Ingress, LoadBalancer, NodePort, etc.). Set it to true to expose the database manager UI:
spec:
allowDatabaseManager: trueNote that list_db: false only hides the web UI — the underlying db service RPC methods (reachable via /jsonrpc or /xmlrpc/2/db) are still gated solely by the master key either way.
| Field | Description | Default |
|---|---|---|
version |
Odoo version tag (e.g., "19", "18.0") | "19" |
size |
Number of Odoo replicas | 1 |
database |
External DB config. If empty, a managed Postgres is created. | - |
enterprise |
Configuration for Enterprise edition (enabled, repo, key). | - |
modules |
List of modules to install and external Git repositories. | - |
redis |
Configuration for Redis session storage. | - |
storage |
PVC configurations for data, logs, addons, and postgres. | - |
resources |
Resource requests/limits for containers. | - |
databaseSecretName |
Name of the Secret with DB credentials (user, password, dbname). Auto-created if empty and using a managed DB. |
- |
masterKey |
Database manager master password (value literal or secretRef). Auto-generated into a Secret if unset. |
random, generated |
allowDatabaseManager |
Exposes Odoo's database manager (list_db) instead of 404ing it. |
false |
(See CRD definitions in api/v1alpha1 for full details)
Prerequisites
- go version v1.24.6+
- docker version 17.03+.
- kubectl version v1.11.3+.
- Access to a Kubernetes v1.11.3+ cluster.
Run locally:
make install
make runRun tests:
make testCopyright 2025.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.