Skip to content

PostgreSQL state and drift

AI-translated from Russian.

From odpm 4.3 onward, PostgreSQL cluster configuration (compose, odoo.conf, data directory, application role) is tracked via .odpm/database/last_run.json. This is separate from Odoo database operations (-d, --db-backup, --db-restore).

Why it exists

Database “truth” used to be spread across .env, docker-compose.yml, odoo.conf, the bind-mounted data directory, and the live PostgreSQL cluster. Renaming a service, changing ports, or reusing an old data dir often failed deep inside the container.

odpm now:

  1. Collects configuration fingerprints.
  2. Compares them to the last snapshot (last_run.json).
  3. Surfaces drift in odpm plan and before compose up.
  4. Adopts legacy projects without a snapshot on first startup.

.odpm/database/last_run.json

Property Value
Generated by odpm automatically
Git do not commit (like runtime/config.json)
Container mount .odpm/database/run/odpm/database (read-write for checker)
schema_version 1

The snapshot includes:

  • compose — PostgreSQL service name, image tag, absolute data path, host port;
  • odoo_confdb_host, db_port, db_user;
  • cluster — non-empty data dir, PostgreSQL major (PG_VERSION), application role and presence.

When the baseline is updated

Event Writer
First run without last_run.json Adoption — before compose up (role + baseline)
Successful credential check in checker Checker — after TCP and psql as db_user

Adoption runs once. While the file exists, only drift vs the saved snapshot is reported.

Legacy project adoption

If last_run.json is missing (typical with an inherited PostgreSQL data directory), odpm before the full stack:

  1. Starts PostgreSQL if needed.
  2. Runs ensure_app_role — creates or updates the application role (default odoo), including single-user bootstrap when no admin login role exists.
  3. Writes the current configuration as baseline.

Adoption does not:

  • reassign PostgreSQL owners of existing Odoo databases;
  • drop or restore Odoo databases;
  • auto-wipe the data directory on PostgreSQL major changes.

See also legacy-project.md.

Drift kinds and severity

KIND Severity Behaviour
first_run info No snapshot; baseline adopted on startup
service_name, db_host_mismatch, host_port low Warning in plan
odpm_scenario, data_dir_empty_changed medium Interactive prompt or --accept-database-drift
data_path, postgres_major, app_role_missing high Prompt; blocks container start without resolution

Prepare step database.drift in odpm plan reflects mismatches before compose is written.

Interactive resolution

With a TTY, odpm prompts (accept new data path, create role, show wipe instructions, etc.). Without TTY — error listing KINDs; use --accept-database-drift=KIND (repeatable).

Accepted KIND values:

  • data_path
  • postgres_major
  • app_role_missing
  • odpm_scenario
  • data_dir_empty_changed

See non-interactive runs.

database subcommand

odpm database status
odpm database status --format json
odpm database ensure-role
Command Purpose
database status Fingerprints, drift, live probes (postgres container, readiness, role)
database status --format json Same as JSON for scripts
database ensure-role Create or update the application role in a running PostgreSQL

Commands run prepare (without compose up) when needed to read configuration.

.env and odoo.conf

POSTGRES_SERVICE_NAME in .env sets the postgres compose service name and the expected db_host in odoo.conf when ODPM_COMPOSE_PREFIX is unset.

ODPM_COMPOSE_PREFIX sets physical built-in service names ({prefix}db, {prefix}odoo), the data volume, and the Docker Compose project name. The last_run.json snapshot stores compose.service_name (postgres), compose.compose_project_name, and compose.odoo_service_name. When the prefix or POSTGRES_SERVICE_NAME changes relative to the snapshot:

  • prepare step template.odoo_conf regenerates the config on db_host mismatch;
  • drift service_name, compose_project_name, or db_host_mismatch appears in plan.

See .env variables, odoo.conf.

Odoo databases: backup, restore, drop

--db-drop, --db-restore, --db-backup operate on Odoo databases inside the cluster, not on last_run.json.

On a legacy cluster a database may exist but be owned by another PostgreSQL user. Standard Odoo exp_drop then skips silently; odpm falls back to direct DROP DATABASE and fails clearly if the database remains.

Example:

odpm -d test_db --db-drop --db-restore my_backup.zip -i -u --set-admin-pass

Troubleshooting

odpm database status
odpm plan --skip-start
docker compose logs db-dev    # postgres service name from .env (or acme-db when ODPM_COMPOSE_PREFIX=acme)

After changing POSTGRES_SERVICE_NAME or ODPM_COMPOSE_PREFIX, remove orphan containers (with the same project scope odpm uses):

docker compose down --remove-orphans
# odpm passes -p automatically; manually: docker compose -p acme down --remove-orphans

What odpm does not automate

  • Data migration on PostgreSQL major upgrades (backup + wipe per prompt instructions).
  • ALTER DATABASE … OWNER TO for old Odoo DBs — use --db-drop or manual psql.
  • Sharing last_run.json across machines — snapshot is local per environment directory.