Skip to content

Deploy Odoo on Kubernetes: Complete K8s Guide (2026)

DeployMonkey Team · March 23, 2026 12 min read

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-production

Step 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: 5432

Step 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-pvc

Step 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: 8069

Step 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: 70

Important 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 /longpolling to port 8072 in your ingress
  • Database migrations: Run odoo -u all as a Kubernetes Job, not during normal startup
  • Cron jobs: Only one Odoo pod should run cron to avoid duplicate execution

Managed Kubernetes Pricing

ProviderControl PlaneWorker Nodes
GKE (Google)Free (Autopilot)Per-pod pricing
EKS (AWS)$73/moEC2 pricing
AKS (Azure)FreeVM pricing
DigitalOceanFreeDroplet 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.