Installation¶
System Requirements¶
- Docker (Docker Engine or Docker Desktop)
- Python 3.10+
- Git
- fuse-overlayfs (for filestore overlay mounting)
macOS support
On macOS, Docker Desktop runs containers inside a Linux VM and projects
files via VirtioFS. fuse-overlayfs is not needed — filestore overlays
are skipped and a plain directory is used instead.
File ownership (chown) is handled automatically: Oduflow detects the
PermissionError that VirtioFS raises and falls back to running chown
inside a throwaway container. No extra configuration is required.
Install fuse-overlayfs¶
The /dev/fuse device must be available (present by default on Ubuntu).
In /etc/fuse.conf, uncomment user_allow_other so the Docker daemon (root) can access FUSE mountpoints created by the user:
Install Oduflow¶
Run without installing¶
With uv you can run Oduflow directly — no installation step needed. uvx downloads the package into a temporary environment and runs it:
This is the quickest way to try Oduflow or use it in CI pipelines.
Permanent installation¶
Install via uv (recommended — manages an isolated environment automatically):
Alternative — install via pip:
After installation, the oduflow command is available globally.
From source¶
git clone https://github.com/oduist/oduflow.git
cd oduflow
uv sync # or: python -m venv .venv && pip install -e .
Upgrade¶
During upgrade, Oduflow overwrites bundled files (agent guides, postgresql.conf, sanitize scripts, odoo.conf) with the latest versions. If you have customized any of these files and want to prevent them from being overwritten, add # KEEP as the very first line of the file:
Files marked with # KEEP will be skipped during upgrade and listed as (kept) in the upgrade output.
Configuration Reference¶
All settings are configured via a TOML file. Oduflow searches for oduflow.toml in the following order:
ODUFLOW_TOMLenvironment variable (explicit path)/etc/oduflow/oduflow.toml~/.oduflow/conf/oduflow.toml~/.oduflow/oduflow.toml
If no config file exists when Oduflow starts, the bundled default is automatically copied to the appropriate location.
Minimal configuration¶
Full configuration reference¶
# ── Server ────────────────────────────────────────────
[server]
host = "0.0.0.0" # HTTP server bind address
port = 8000 # HTTP server port
# trace = false # verbose tracing for git analysis & env ops
# ── Routing ───────────────────────────────────────────
[routing]
mode = "port" # "port" (direct host port) | "traefik" (reverse proxy with auto-HTTPS)
# acme_email = "admin@example.com" # required when mode = "traefik"
# ── OAuth (optional) ──────────────────────────────────
# Set to the public URL of this instance to turn Oduflow into a self-hosted
# OAuth 2.1 Authorization Server (for Claude.ai and other OAuth MCP clients).
# Each team's auth_token doubles as client_id, client_secret, and access token.
[oauth]
# oauth_base_url = "https://oduflow.example.com"
# ── Database ──────────────────────────────────────────
[database]
user = "odoo" # PostgreSQL user for the shared database container
password = "odoo" # PostgreSQL password
image = "postgres:15" # PostgreSQL Docker image
# ── Storage ───────────────────────────────────────────
[storage]
# data_dir = "/srv/oduflow" # base directory for all data (default: /srv/oduflow or ~/.oduflow/data)
overlay_threshold_mb = 50 # template filestore size threshold (MB) — larger uses fuse-overlayfs, smaller uses copy
# ── Lifecycle ─────────────────────────────────────────
[lifecycle]
auto_stop_hours = 48 # auto-stop environments idle for N hours (no MCP/dashboard work); 0 disables
auto_delete_hours = 72 # auto-delete environments stopped for N hours; 0 disables (protected envs are exempt)
# ── Teams ─────────────────────────────────────────────
# Each team gets isolated workspaces, templates, credentials, and services.
# At least one [team.*] section is required.
[team.1]
hostname = "localhost" # port mode: http://{hostname}:{port}, traefik mode: https://{slug}.{hostname}
auth_token = "" # MCP bearer token (empty = MCP auth disabled)
ui_password = "" # Web UI password (empty = UI auth disabled)
port_range = [50000, 50100] # port range for Odoo containers [start, end)
Server settings¶
| Key | Default | Description |
|---|---|---|
[server].host |
0.0.0.0 |
HTTP server bind address |
[server].port |
8000 |
HTTP server port |
[server].trace |
false |
Enable detailed trace logging for git analysis and environment operations |
[server].disable_telemetry |
false |
Disable anonymous usage telemetry (see Telemetry) |
Routing settings¶
| Key | Default | Description |
|---|---|---|
[routing].mode |
port |
port — direct host port mapping; traefik — reverse proxy with auto-HTTPS |
[routing].acme_email |
(empty) | Let's Encrypt email for TLS certificates. Required when mode = "traefik" |
OAuth settings¶
| Key | Default | Description |
|---|---|---|
[oauth].oauth_base_url |
(empty) | Public URL of this Oduflow instance. When set, Oduflow runs a self-hosted OAuth 2.1 Authorization Server (exposes /.well-known/oauth-authorization-server, /authorize, /token) so OAuth-based MCP clients like Claude.ai can connect. Each team's auth_token doubles as client_id, client_secret, and the issued access token. Empty = plain Bearer-token auth only. See Authentication & Security |
Database settings¶
| Key | Default | Description |
|---|---|---|
[database].user |
odoo |
PostgreSQL user for the shared database container |
[database].password |
odoo |
PostgreSQL password |
[database].image |
postgres:15 |
PostgreSQL Docker image |
Storage settings¶
| Key | Default | Description |
|---|---|---|
[storage].data_dir |
/srv/oduflow or ~/.oduflow/data |
Base directory for all data. Team data directories are team_{ID} subdirectories inside |
[storage].overlay_threshold_mb |
50 |
Template filestore size threshold (MB). Templates smaller than this use a simple copy per environment; larger templates use fuse-overlayfs. The decision is stored in metadata.json at template creation time |
[lifecycle].auto_stop_hours |
48 |
Auto-stop environments after N hours without work (env-scoped MCP calls or dashboard actions). 0 disables. Protected environments are exempt |
[lifecycle].auto_delete_hours |
72 |
Auto-delete environments N hours after they stopped (manual stops count). 0 disables. Protected environments are exempt; pull_and_apply wakes a stopped environment automatically |
Per-team settings¶
Each [team.*] section defines an isolated team with its own workspaces, templates, credentials, and services. At least one team is required.
| Key | Default | Description |
|---|---|---|
hostname |
localhost |
Team hostname. In port mode: http://{hostname}:{port}. In traefik mode: https://{slug}.{hostname} |
auth_token |
(empty) | Bearer token for MCP HTTP auth. Empty = MCP auth disabled for this team |
ui_password |
(empty) | Password for Web UI Basic auth (user: admin). Separate from MCP auth token. Empty = UI auth disabled |
port_range |
[50000, 50100] |
Port range for Odoo containers [start, end) — supports up to 100 concurrent environments |
Team data is stored at {data_dir}/team_{ID}/:
team_{ID}/
├── workspaces/ # Per-branch environments
├── templates/ # Reusable database snapshots
├── shared_repos/ # Extra addon repositories (bare clones)
├── ports.json # Port registry
├── .git-credentials # Git credentials for this team
└── agent_guides/ # AI agent guides (markdown)
Configuration file overrides¶
On startup, Oduflow copies the bundled postgresql.conf and odoo.conf to the config directory (if they don't already exist). These files take priority over the bundled defaults — edit them to customize PostgreSQL tuning or Odoo settings globally:
/etc/oduflow/ (or ~/.oduflow/conf/)
oduflow.toml ← main configuration file
postgresql.conf ← custom PostgreSQL tuning (used by oduflow-db)
odoo.conf ← custom Odoo defaults (used by new environments)
license.key ← license file (optional)
traefik/ ← Traefik dynamic configuration (auto-generated)
If a repository contains an odoo.conf in its .oduflow/ directory (<repo>/.oduflow/odoo.conf), it takes priority over both the bundled and system-level versions for that specific environment.
Telemetry¶
Oduflow collects anonymous usage telemetry to help us understand adoption and prioritize development. Two events are sent:
first_run— sent once on the very first startup (when the instance ID is created).env_created— sent each time a new environment is provisioned.
Each event contains only:
- The event name
- The oduflow version
- A random instance ID (UUID)
No personal data, hostnames, IP addresses, branch names, repository URLs, or environment details are collected.
Opt out¶
Add to your oduflow.toml:
Auto-start with systemd¶
On Linux servers, Oduflow can be registered as a systemd service so it starts automatically on boot.
Prerequisites¶
# Install uv (if not already installed)
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install oduflow as a tool (as root)
uv tool install oduflow
# Create the configuration file (optional — Oduflow auto-creates a default oduflow.toml on first start)
Install the service¶
This will:
- Generate a systemd unit file at
/etc/systemd/system/oduflow.service - Run
systemctl daemon-reload - Enable the service for auto-start on boot
Manage the service¶
# Start
systemctl start oduflow
# Status
systemctl status oduflow
# Logs (follow)
journalctl -u oduflow -f
# Restart after config changes
systemctl restart oduflow
Remove the service¶
This stops, disables, and removes the unit file.