How Antelao is put together
You can administer Antelao without reading this page. It is here because a handful of ideas explain most of the product's behaviour, and knowing them turns "why did it do that" into "of course".
Server, instance, database
Three nouns, nested.
A server is one Linux host running the managed stack. A instance is one Odoo deployment on
it: its own container, its own Odoo version and edition, its own addons and its own host names. A
database is an Odoo database inside an instance. All databases from all instances live in the
one shared odoo-postgres container.
Instance containers are named odoo- followed by twelve hexadecimal characters. That shape is not
cosmetic: Fail2Ban jail names are limited to 29 characters, and the id had to fit.
The three front-ends and who does the work
The API does the work. The Web UI holds no Docker socket and no domain code at all: it is an
HTTP client of the API and nothing else. odoocli is the same by default, and only touches the
host directly in local mode.
The practical consequence: if the API container is down, the Web UI can show you nothing and change
nothing. odoocli --local still works, which is why the repair commands are local-only.
Everything is a background job
Every state-changing operation is queued as a job rather than performed inline. The API accepts your request, writes a job row, returns immediately and works through the queue.
This is why the UI shows queued → running → completed rather than success straight away, and why a
remote odoocli command streams a log until it finishes.
Jobs are persisted and resumable. If the API restarts mid-queue, queued jobs are picked up
again; jobs that were running are marked Interrupted, because half-finished work may be
destructive and re-running it blindly is worse than stopping.
Concurrency is bounded by maxConcurrentJobs in odoo.json, which defaults to 1, meaning jobs
run one at a time. Two rules hold whatever you set it to:
- Two jobs never run against the same instance at once.
- A server-level job (one with no instance) drains the queue and runs alone, because it mutates Docker, PostgreSQL or the filesystem that everything else shares.
Secrets never reach a job row. A password you submit goes through a one-shot cache and is referenced by an opaque handle on the payload; a password the server generates is revealed once through a separate endpoint with a ten-minute lifetime. Neither appears in the job, its result or its log.
Where state lives
| What | Where |
|---|---|
| Server configuration | odoo.json under the application root |
| Instance configuration | The master database (default), or per-instance JSON files |
| Users, jobs, schedules, statistics | The master database |
| Instance data trees, backups, secrets | The filesystem under the instance root |
| Licences | *.lic files under the application root's license/ directory |
The master database is SQLite by default, and can be PostgreSQL or SQL Server. Its schema is managed by the application itself, upgraded forward on startup. That upgrade is forward-only: an older binary will refuse to start against a newer schema, so a rollback means restoring the database from before the upgrade too.
odoo.json is read once, at API startup. Editing it by hand while the API runs changes nothing
until you restart odoo-api, and in the meantime regenerated nginx and Fail2Ban configuration will
quietly use the old values.
Startup and running configuration
An instance has two configurations: the one you have edited, and the one the running container was actually built with. Antelao keeps a snapshot of the second.
That is what the Changes page compares. A pending change is one you have saved but not applied; applying it rebuilds or recreates whatever the change needs. Some settings, such as adding an addons bind mount, only take effect when the container is recreated rather than restarted.
Events
The API publishes one event stream that carries signals, not data: "this job changed", "this instance changed state", "there are new statistics". Clients then re-read the authoritative state through the normal endpoints, which are role-scoped.
The reason is a security one as much as a design one: a signal that says "something changed" cannot leak anything to a viewer who is not allowed to see what changed.