Skip to content

Multi-version Odoo

Audience

PLANA staff and customers planning a version upgrade. Covers how we keep v17, v18 and v19 working in production side-by-side.

The upstream Odoo community ships a new major release every October. We support that cadence by running the current major and the previous one in full production, with the next major in limited release while we wait for the OCA community to finish porting the dependencies we use.

YearDefaultAlso supportedLimited
2026 (now)v18v17 (legacy, EOL 2027)v19 (some OCA modules pending)
2027v19v18v20 (limited)
2028v20v19v21 (limited)

A tenant stays on its current major until PLANA verifies that every module it uses is available on the target version. Migrations are operator-driven, not customer-driven.

How the cluster is laid out

Three sibling namespaces, one per major:

NamespaceMajorStatus (2026-05)
plana-odoo17Empty — last v17 tenant migrated 2026-05-22
plana-odoo-1818Production — default for new tenants
plana-odoo-1919Limited release

Each namespace has:

  • Its own worker-odoo Deployment with HPA
  • Its own worker-odoo-test Deployment for staging clones
  • Its own filestore PV (RWX, NFS-backed)
  • Its own ResourceQuota
  • Its own redis-allow-consumers NetworkPolicy entry
  • Its own provider-sql-postgres-creds secret (copy of the one in crossplane-system)
  • Its own forgejo-registry image-pull secret

The shared eg-gateway Gateway resource lives in plana-odoo and stays there permanently — even when plana-odoo has no tenants. HTTPRoutes in other namespaces reference it across namespaces via ReferenceGrant.

Where the source lives

The Odoo monorepo is branch-per-version at git.planapulse.com/plana-pulse/odoo-modules:

BranchTracksTag
17.0Odoo 17base-17:<sha>
18.0Odoo 18base-18:<sha>
19.0Odoo 19base-19:<sha>

Plus -upgrade variants used by the cross-major migration path (base-{N}-upgrade), which include the OpenUpgrade framework.

A single Dockerfile parametrized by ARG ODOO_VERSION builds all three. Cherry-picks flow 17.0 → 18.0 → 19.0 chronologically. A platform change is committed once and ported forward; a port is committed once per version branch.

Per-major module set

What ships on each major depends on the OCA community's porting cadence. Today (2026-05):

Module groupv17v18v19
plana_* modules (PLANA-built)
OCA web, server-auth, server-backend, server-ux
OCA account-financial-tools, account-financial-reporting
OCA l10n-bulgaria (BG fiscal pack)
OCA account-analytic, account_analytic_sequence— pending
OCA agreement, agreement_legal— pending
OCA helpdesk (7 modules)— pending
OCA dms— pending

PLANA does not back-port OCA modules. We follow the upstream community branches and pick them up as they land. A weekly cron (oca-sync.yml workflow) refreshes the OCA submodules on all three version branches every Saturday.

Provisioning a tenant on a specific version

A PLANAClient CR with spec.odooVersion selects the major. The Composition's transforms: map: translates the version into the namespace where the worker lives:

yaml
apiVersion: planapulse.com/v1alpha1
kind: PLANAClient
metadata: { name: acme }
spec:
  subdomain: acme
  projectId: 23
  tier: pro
  odooVersion: "18"

→ HTTPRoute backendRefs.namespace=plana-odoo-18. Database created on pg01. Filestore PVC subdir created under the v18 NFS export. Backup CronJob scoped to the right namespace. See Crossplane for the full fan-out.

XRD defaults were bumped to "18" on 2026-05-22 — any new CR without an explicit version targets v18.

Same-major vs cross-major upgrades

PathPatternVehicle
Same major (v17 → v17)Module set changed, code updateodoo --update=all in a Job
Cross major (v17 → v18, v18 → v19)Framework + schema migrationOpenUpgrade two-pass

The cross-major two-pass:

  1. Pass 1 under the source-version binary (base-17-upgrade): odoo -i openupgrade_framework --stop-after-init — installs the OpenUpgrade framework module on the DB so its module-loader patches activate.
  2. Pass 2 under the target-version binary (base-18-upgrade): agreement_legal SQL pre-fix (DELETE the two stale ir_model_data rows that cause a unique-constraint violation), then odoo --update=all --load=base,web,openupgrade_framework.

Both passes run via the TenantUpgrade Composition. The strategy is selected by spec.strategy: same-major | snapshot-then-upgrade.

The filestore is not migrated by the upgrade — that is a separate tar-pipe between holder Pods in the source and target namespaces. The runbook is at Operations → Upgrading a tenant.

Annual port cycle

When upstream Odoo ships a new major (October):

WeekAction
W0Bootstrap a new branch from N: git checkout -b N+1. Bump ARG ODOO_VERSION.
W1–W3Port PLANA modules in dependency order
W4–W5Port OCA fork patches; rebase -planapulse branches; file upstream PRs
W6Stand up plana-odoo-N+1-staging namespace; build TemplateSnapshot{odooVersion=N+1}
W7–W8Pilot 2–3 friendly tenants via TenantUpgrade
W9–W10GA to new tenants; tier supported_odoo_versions += N+1
W11–W12Bulk migrate willing tenants; mark N-1 branch protected/read-only

Recurring effort: ~25–30 engineer-days/year. The framework was built once in 2026; subsequent cycles re-run the same checklist.

Known API breaks port to port

Captured during the 2026-05-21 v18 + v19 rollout — the patterns repeat between any two majors:

  • Field renames — e.g. project.project.analytic_account_idaccount_id in v18. Search the codebase for the old name and update.
  • Import path movesfrom odoo import registry was removed in v19; use odoo.modules.registry.Registry instead. We work around this with try/except aliases so the same module file compiles on multiple majors.
  • Bus renamesImBusBusBus in v19. Same try/except aliasing.
  • Enterprise dependencies — modules marked as Enterprise on a newer major must be dropped from the tier seed data. We are Community-only.
  • View tag renames<tree><list> in v18. Bulk rename across all view XML files.
  • Base image UID change — v17 ran as UID 101; v18 and v19 run as UID 100. Per-namespace runAsUser and filestore ownership must match.
  • Debian PEP 668 — Odoo 18 base is Debian 12; pip install requires --break-system-packages.

These are captured in infra/docs/runbooks/annual-odoo-port.md for next year's cycle. We treat the port runbook as a living checklist, updated during each port.

Where to read more

© PLANA Digital Ltd.