Skip to main content

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

WhatWhere
Server configurationodoo.json under the application root
Instance configurationThe master database (default), or per-instance JSON files
Users, jobs, schedules, statisticsThe master database
Instance data trees, backups, secretsThe 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.