Skip to main content

odooctl

CI License: AGPL v3 Python 3.11+ PRs Welcome

odooctl is a CLI-first, Odoo-aware control plane for self-hosted Odoo on Docker Compose or Kubernetes — think open-source Odoo.sh for your own infrastructure. It handles the operational lifecycle generic deploy tools miss: verified backups, sanitized staging clones, module updates, rollback, environment promotion, upgrade rehearsal, and health checks, all Odoo- and PostgreSQL-aware. Existing Compose projects remain the zero-migration default.

Why odooctl

  • Safety by default. Deploys to protected environments take a database + filestore backup first; failed module updates and failed health checks stop the deployment with a non-zero exit.
  • Verify before destroy. Restores land in a temporary database and are only swapped into place after the restore succeeds — a bad backup never destroys a working environment.
  • Off-host backup policy is explicit. S3 copies can be required, best_effort, or disabled, with byte verification, project-scoped object namespaces, and retention that never treats an unknown old upload as safe to delete.
  • Filestore migrations verify every byte. Local paths and Docker volumes can migrate to a POSIX object mount or a project-scoped S3-compatible generation; cutover is explicit and source deletion is a separate, fully-confirmed operation.
  • Staging clones you can trust. odooctl clone production staging sanitizes by default: mail servers, crons, payment providers, queue jobs, OAuth secrets, IAP tokens, and webhook URLs are neutralized — including wiping Odoo 19 WebAuthn passkeys — so staging cannot email customers or charge cards.
  • Adopt what you already run. odooctl import detects an existing compose deployment read-only, previews the generated config, and only writes on --yes — then registers the project, runs preflight checks, and takes a safety backup.
  • Protected environments. Production-tier environments require elevated confirmation for destructive operations, in the CLI, API, and web UI alike.
  • Secrets stay secret. Config references secrets by env-var name only; logs, errors, and streamed operation events are redacted.
  • A local API and web UI, RBAC'd. odooctl serve runs a localhost-only REST API plus a no-build web UI with viewer/operator/admin/owner roles, HMAC bearer tokens, and a privileged runner kept in a separate process.
  • Upgrade rehearsal, not upgrade roulette. odooctl migrate rehearse clones production into a throwaway DB, runs the (OpenUpgrade-backed) upgrade there, health-checks, and writes a report. Production is never touched.

Quickstart

A. Adopt an existing Docker Compose deployment

# Read-only detection and preview — no containers touched, no secrets stored
odooctl import /srv/odoo/acme

# Adopt: write odooctl.yml, register the project, run doctor, take a safety backup
odooctl import /srv/odoo/acme --name acme --yes

# First verified backup, then a sanitized staging environment in one command
odooctl -p acme backup production --verify
odooctl -p acme env create staging --clone-from production

B. Start a fresh project

mkdir acme && cd acme
odooctl setup --name acme --stack odoo-19-community   # or run interactively: odooctl setup

setup scaffolds odooctl.yml from a pinned stack template (secrets referenced by env-var name, never inlined). Edit the domain, database names, and your docker-compose.yml, then:

export ODOO_DB_PASSWORD='a-strong-secret'
odooctl validate
odooctl doctor
odooctl deploy production --branch main

Either way, day-2 operations look like:

odooctl backup production --verify
odooctl clone production staging --sanitize
odooctl update-modules staging --modules sale,stock
odooctl restore staging --backup latest
odooctl rollback production --mode code
odooctl migrate rehearse --env production --to 19.0 --openupgrade
odooctl status

Features

Feature Command(s) Docs
Import / takeover of existing stacks odooctl import docs/getting-started.md
Deploy with pre-deploy backups odooctl deploy docs/deployment.md
Verified local/remote backups, safe restores odooctl backup --verify, odooctl backup-remote, odooctl restore docs/backup-restore.md
Sanitized staging clones odooctl clone, odooctl env create docs/staging-clone.md, docs/environments.md
Rollback (code or full) odooctl rollback docs/rollback.md
Environment promotion odooctl promote docs/environments.md
Preflight checks odooctl doctor docs/doctor.md
Upgrade rehearsal (17 → 18 → 19) odooctl migrate matrix / scan / rehearse docs/migration.md
Disaster recovery and provider snapshots odooctl dr drill, odooctl dr snapshot docs/disaster-recovery.md
PostgreSQL WAL archiving and PITR odooctl pitr docs/pitr.md
Object-storage filestore migration odooctl filestore docs/filestore-storage.md
Kubernetes runtime and production manifests odooctl deploy, odooctl logs, odooctl status docs/kubernetes.md
GitOps and expiring PR environments odooctl gitops docs/gitops.md
Progressive rollout and automated rollback odooctl deploy, odooctl promote docs/progressive-deployment.md
k3d + Tilt production simulation odooctl local docs/local-simulation.md
Domains / SSL via reverse proxy odooctl domain docs/domains-ssl.md
Local REST API + operation queue odooctl serve, odooctl runner, odooctl ops docs/api.md
RBAC, tokens, secret store odooctl security docs/rbac.md
Stack / addon catalog odooctl catalog, odooctl setup docs/catalog.md
Scheduled backups / checks / DR drills odooctl schedule docs/backup-restore.md#scheduling-backups-verification-and-drills

Provider snapshots are deliberately coarse DR artifacts: one provider source is bound to one environment, recovery is plan-only by default, and execution creates isolated resources only after exact snapshot/source confirmation. They supplement rather than replace portable database + filestore backups; see the lifecycle and cost notes.

Web UI

The optional web UI is a no-build vanilla-JS SPA served by the local API:

pip install 'odooctl[api]'
export ODOOCTL_API_KEY="$(python -c 'import secrets; print(secrets.token_hex(32))')"
odooctl serve            # binds 127.0.0.1:8787 by default

Open http://localhost:8787/ and paste a token minted with:

odooctl security token mint --action api --env "*" --project "*" --role operator

The server is localhost-only by design and speaks plain HTTP — do not bind it to a public address; use an SSH tunnel for remote access. The API process never touches Docker or PostgreSQL directly: privileged work is delegated to odooctl runner through a durable queue with short-lived, single-use capability tokens. See docs/web-ui.md and docs/api.md.

How it compares

Compared to Odoo.sh, odooctl is self-hosted and free: your server, your data, no per-worker pricing — with the same core workflow of environments, backups, staging builds, and upgrade testing. Compared to doodba, odooctl is not a project scaffolding framework; it manages the operational lifecycle (backup, clone, restore, promote, rehearse) of whatever compose project you already have — including one built with doodba. Compared to hand-rolled compose scripts, every destructive path here ships with tested safety rails: pre-deploy backups, restore-into-temp-then-swap, sanitization that is on by default, protected-environment confirmation, and secret redaction, backed by a continuously growing unit suite plus a real-Odoo integration suite.

Supported Odoo versions

Odoo 17, 18, and 19 (Community) are integration-tested: a disposable-stack harness in tests/integration/ runs the full operator lifecycle against real odoo:17/odoo:18/odoo:19 images. See docs/odoo-versions.md for per-version notes.

Install

Install the stable release from PyPI:

pipx install 'odooctl==0.3.0'
# or: uv tool install 'odooctl==0.3.0'
odooctl --version

The host also needs Docker Engine with the Compose plugin and tar. With the recommended execution_mode: docker, PostgreSQL client tools run inside your DB container, so the host needs none. See docs/installation.md for upgrade and rollback commands.

Development

uv sync --frozen --extra dev --extra api           # exactly what CI installs
uv run --frozen pytest -q                          # unit suite
uv run --frozen ruff check odooctl tests           # lint
uv run --frozen pytest -m integration tests/integration  # opt-in real-Odoo matrix (needs Docker)

uv.lock is authoritative — --frozen keeps your environment identical to CI's. See CONTRIBUTING.md for changing dependencies.

Safety defaults

  • Deploys to protected environments create database and filestore backups first.
  • Module-update and health-check failures fail the deployment with a non-zero exit.
  • Restores go into a temporary database and are swapped in only after success.
  • DR drills restore into disposable PostgreSQL and Odoo containers on an internal network with dedicated volumes; live services and data volumes are never restore targets.
  • PITR restores into an isolated PostgreSQL runtime and a new verified database before an explicit, OID-fenced cutover. WAL recovery is database-only and never claims to rewind the Odoo filestore.
  • Filestore migration cutover re-verifies both the target and unchanged source; source deletion is a separate operation requiring exact environment and migration confirmations.
  • Clone sanitization is on by default and disables mail, fetchmail, crons, payment providers, queue jobs, and automation rules; scrubs OAuth/IAP/webhook credentials; deletes Odoo 19 passkeys; and rewrites and freezes web.base.url.
  • Secrets are referenced via environment variables (password_env) and redacted from logs, errors, and operation events; never commit secret values.
  • The API/runner split is structurally enforced: odooctl security runner-check verifies the API layer imports no privileged adapters.

See docs/security.md for the trust model.

Contributing, security, license

Contributions are welcome — see CONTRIBUTING.md and the Code of Conduct. Good entry points are issues labeled good first issue and help wanted. Report vulnerabilities privately per SECURITY.md.

odooctl is free software, dual-licensed: AGPL-3.0-or-later for open use — including running it commercially for yourself or your clients — with a commercial license available for embedding or reselling it in proprietary products. See LICENSING.md.

Metadata

Release files for odooctl 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for odooctl 0.3.0
File Size Uploaded
odooctl-0.3.0.tar.gz 569.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for odooctl 0.3.0
File Interpreter ABI Platform
odooctl-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 942.1 kB

Release files / odooctl-0.3.0.tar.gz

Download URL odooctl-0.3.0.tar.gz
Size 569.2 kB
Tags Source
SHA-256 checksum
How to use checksums
f69554ddaaa6502507c737e448430961640d78cddc44cd46ce6de7c0a8e91ad4
BLAKE2b-256 checksum
How to use checksums
70e6d1fecf4c6eeef774eff43858efe894a6b0c9c8aaf9862fb8cb40157b3d6b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 24, 2026.

Transparency log

Release files / odooctl-0.3.0-py3-none-any.whl

Download URL odooctl-0.3.0-py3-none-any.whl
Size 372.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d2a1d9b6483eb22c7ce9ede1c00c1cc57f069675cd546783beb5946263c828be
BLAKE2b-256 checksum
How to use checksums
be8d24841d91e378b31b359bc08f47dca2d0e94ac6204b95e6d1d2d702210dd0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 24, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page