Daemon that runs on each tenant VPS to manage Odoo instances via Docker. Receives jobs from the orchestrator over NATS JetStream, executes them, and streams progress back. Distributed as a single compiled binary — no Python runtime required on the VPS.
Dashboard (React)
└── REST / WebSocket ──► Orchestrator (FastAPI)
└── NATS JetStream ──► Agent (per VPS)
├── Docker Engine
├── Postgres + PgDog
├── Traefik (reverse proxy)
└── Backups (local / S3)
| Repo | Description |
|---|---|
dooservice/agent |
This repo |
dooservice/orchestrator |
Central API server |
dooservice/core |
Shared SDK, models, transport, protocol |
curl -fsSL https://raw.githubusercontent.com/dooservice/agent/main/install.sh | sudo bashThe installer will:
- Install Docker (official Ubuntu repository)
- Download the
dooservice-agentbinary from GitHub Releases to/usr/local/bin/ - Create system user
dooservice(uid1500) and add todockergroup - Detect server specs (RAM, CPU cores) and calculate optimal Postgres/PgDog settings
- Prompt for NATS credentials, base domain, TLS, DNS provider and S3 (optional)
- Generate
/var/lib/dooservice/agent.tomlwithchmod 600 - Install and enable the
dooservice-agentsystemd service - Run
bootstrap configureandbootstrap start(Postgres → PgDog → Traefik)
Requirements: Ubuntu 22.04+, root access, internet access.
Config is loaded from /var/lib/dooservice/agent.toml (created by the installer). See agent.example.toml for all options with inline comments.
nats_url = "nats://nats.example.com:4222"
nats_user = "agent"
nats_password = "secret"
region = "eu-west"
heartbeat_interval = 30 # seconds
max_concurrent_backups = 3
[sdk]
data_dir = "/var/lib/dooservice" # agent.db, pgdog/, letsencrypt/, projects/
debug = false
[sdk.postgres]
superuser_password = "<auto-generated>"
max_connections = 200 # low — PgDog handles per-tenant pooling
shared_buffers = "16GB" # ~25% of RAM
effective_cache_size = "48GB" # ~75% of RAM
# ... full tuning options in agent.example.toml
[sdk.pgdog]
workers = 16 # = CPU cores
pooler_mode = "session" # required for Odoo (LISTEN/NOTIFY, longpolling)
default_pool_size = 12
[sdk.proxy]
base_domain = "clientes.example.com"
server_ip = "1.2.3.4"
acme_email = "admin@example.com"
dns_provider = "cloudflare_token"
[sdk.dns]
cloudflare_api_token = "..."
[sdk.s3] # optional — leave empty to keep backups on local disk
endpoint = "https://s3.amazonaws.com"
access_key = "..."
secret_key = "..."
bucket = "dooservice-backups"Config is loaded in this order: --config flag → DOOSERVICE_AGENT_CONFIG env var → /var/lib/dooservice/agent.toml → auto-generated from template.
# Start the daemon (reads config, connects to NATS, runs scheduler)
dooservice-agent serve [--config PATH]
# Infrastructure stack
dooservice-agent bootstrap configure # set base domain, TLS, DNS provider
dooservice-agent bootstrap start # bring up Postgres → PgDog → Traefik
dooservice-agent bootstrap stop
dooservice-agent bootstrap rebuild
dooservice-agent bootstrap status # show running state of all 3 services
# Proxy container (Traefik)
dooservice-agent proxy show # show current proxy config
dooservice-agent proxy start|stop|rebuild|status
# Postgres container
dooservice-agent pg start|stop|rebuild|status
# PgDog connection pooler
dooservice-agent pgdog start|stop|rebuild|status
# systemd service management
dooservice-agent agent install # write unit file + daemon-reload
dooservice-agent agent start|stop|restart|enable|disable
dooservice-agent agent status
dooservice-agent agent logs [--no-follow] [--lines 200]The daemon runs a built-in APScheduler that fires daily at 03:00 local time for each production environment.
Guarantees:
- Only production environments are scheduled (development excluded)
- One backup per environment at a time — if a manual backup is in progress, the scheduled one is skipped
- At most
max_concurrent_backups(default 3) running in parallel across all environments - Previous day's backup file (S3 object or local file) is deleted before creating the new one — storage stays clean even if the new backup fails
- The DB record is never deleted — marked
DROPPEDfor permanent audit history - Graceful shutdown: waits up to 120 seconds for in-flight backups before releasing DB connections
- Invalid timezone strings fall back to UTC with a warning instead of crashing
Requires Python 3.13 and uv.
git clone git@github.com:dooservice/agent.git
cd agent
uv sync # fetches core packages via git tag from dooservice/core
make check # ruff lint + format
make test # pytestTo run the daemon locally:
uv run dooservice-agent serve --config /path/to/agent.tomlReleases are created by pushing a version tag. GitHub Actions compiles the binary with PyInstaller on Ubuntu 24.04 and publishes it to GitHub Releases.
# 1. Update CHANGELOG.md with what changed
# 2. Run:
make bump VERSION=1.1.0This will:
- Update
versioninpyproject.toml - Commit:
chore: bump agent to 1.1.0 - Tag:
agent/v1.1.0 - Push branch and tag → CI triggers
The install script always downloads from releases/latest, so users get the new version on their next install or manual update.
This repo depends on dooservice/core at a fixed git tag. When core releases a new version:
- Edit
pyproject.toml— updatetag = "core/v1.0.0"→tag = "core/v1.1.0"in all 8[tool.uv.sources]entries - Run
uv sync - Test and bump:
make bump VERSION=1.1.0