Why Kubernetes for Odoo?
Kubernetes orchestrates containerized applications at scale. For Odoo deployments that need high availability, automatic scaling, rolling updates, and self-healing, Kubernetes provides infrastructure that a single-server setup cannot match. When an Odoo worker crashes, Kubernetes restarts it. When traffic spikes, Kubernetes scales horizontally. When you deploy updates, Kubernetes rolls them out with zero downtime.
That said, Kubernetes adds operational complexity. A single Odoo instance serving 50 users does not need Kubernetes. It becomes valuable at scale — multiple Odoo instances, microservice architectures, or organizations that already run Kubernetes for other workloads.
Architecture Overview
Internet → Ingress Controller (nginx) → Odoo Service → Odoo Pods (2-10)
↓
PostgreSQL StatefulSet
↓
PersistentVolumeClaim (data)Prerequisites
- Kubernetes cluster (GKE, EKS, AKS, or self-managed)
- kubectl configured
- Helm 3 installed
- Container registry access (Docker Hub, GCR, ECR)
- Domain name with DNS configured
Step 1: Create Namespace
kubectl create namespace odoo-productionStep 2: Deploy PostgreSQL
# postgresql-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgresql
namespace: odoo-production
spec:
serviceName: postgresql
replicas: 1
selector:
matchLabels:
app: postgresql
template:
metadata:
labels:
app: postgresql
spec:
containers:
- name: postgresql
image: postgres:16
ports:
- containerPort: 5432
env:
- name: POSTGRES_USER
value: odoo
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: odoo-secrets
key: db-password
- name: POSTGRES_DB
value: odoo
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: pg-data
mountPath: /var/lib/postgresql/data
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "4Gi"
cpu: "2000m"
volumeClaimTemplates:
- metadata:
name: pg-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50Gi
---
apiVersion: v1
kind: Service
metadata:
name: postgresql
namespace: odoo-production
spec:
selector:
app: postgresql
ports:
- port: 5432Step 3: Create Secrets
kubectl create secret generic odoo-secrets \
--namespace odoo-production \
--from-literal=db-password='secure_password_here' \
--from-literal=admin-password='odoo_master_password'Step 4: Deploy Odoo
# odoo-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: odoo
namespace: odoo-production
spec:
replicas: 2
selector:
matchLabels:
app: odoo
template:
metadata:
labels:
app: odoo
spec:
containers:
- name: odoo
image: odoo:19
ports:
- containerPort: 8069
- containerPort: 8072 # longpolling
env:
- name: HOST
value: postgresql
- name: PORT
value: "5432"
- name: USER
value: odoo
- name: PASSWORD
valueFrom:
secretKeyRef:
name: odoo-secrets
key: db-password
volumeMounts:
- name: odoo-data
mountPath: /var/lib/odoo
- name: odoo-addons
mountPath: /mnt/extra-addons
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "4Gi"
cpu: "2000m"
readinessProbe:
httpGet:
path: /web/health
port: 8069
initialDelaySeconds: 30
periodSeconds: 10
livenessProbe:
httpGet:
path: /web/health
port: 8069
initialDelaySeconds: 60
periodSeconds: 30
volumes:
- name: odoo-data
persistentVolumeClaim:
claimName: odoo-data-pvc
- name: odoo-addons
persistentVolumeClaim:
claimName: odoo-addons-pvcStep 5: Ingress with SSL
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: odoo-ingress
namespace: odoo-production
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "720"
spec:
ingressClassName: nginx
tls:
- hosts:
- erp.yourdomain.com
secretName: odoo-tls
rules:
- host: erp.yourdomain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: odoo
port:
number: 8069Step 6: Horizontal Pod Autoscaler
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: odoo-hpa
namespace: odoo-production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: odoo
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70Important Considerations
- Session stickiness: Odoo requires session affinity. Configure ingress with
nginx.ingress.kubernetes.io/affinity: cookie - Filestore: Odoo's filestore must be shared across pods. Use ReadWriteMany PVC (NFS, EFS, or object storage)
- Longpolling: Route
/longpollingto port 8072 in your ingress - Database migrations: Run
odoo -u allas a Kubernetes Job, not during normal startup - Cron jobs: Only one Odoo pod should run cron to avoid duplicate execution
Managed Kubernetes Pricing
| Provider | Control Plane | Worker Nodes |
|---|---|---|
| GKE (Google) | Free (Autopilot) | Per-pod pricing |
| EKS (AWS) | $73/mo | EC2 pricing |
| AKS (Azure) | Free | VM pricing |
| DigitalOcean | Free | Droplet pricing |
DeployMonkey + Kubernetes
DeployMonkey handles the complexity of Odoo deployment for you. If you need Kubernetes-level scalability without managing K8s yourself, DeployMonkey provides managed Odoo hosting with auto-scaling capabilities.