Installation with Docker
Compose containers and Ofelia schedule the application jobs. Do not also run host systemd services or legacy cron jobs against the same data. Both installation methods use the same business scripts and database journals.
Requirements and layout
Use Docker Engine and Docker Compose 2.24 or newer, enough storage for PostgreSQL, the PBF, its conversion and reports, and an HTTPS reverse proxy for public access. On Windows, keep the project in WSL's Linux filesystem, preserving POSIX permissions and LF line endings.
Place the repository in /opt/lebontag-v2. Run commands there with an account allowed to use Docker. Ofelia's Docker socket grants substantial privileges: a read-only socket mount does not prevent Docker API operations. Never expose it over the network.
PostgreSQL uses the project's named pgdata volume. OSM files, reports and logs remain in the repository's osm/ and log/. Images contain code and dependencies; secrets are excluded from the build context and supplied at runtime.
| Profile / service | Purpose |
|---|---|
Default: db, webapp | Database and application |
tools | Explicit initialization and first-administrator assignment |
automation | Pipeline, integration, segmentation, cleanup and Ofelia |
emails | Optional notification HTTP worker |
docker compose --env-file .env.db up -d alone does not start scheduled jobs. An explicitly targeted service can run without enabling its entire profile.
1. Configuration
For a new installation:
cp .env.db.example .env.db
cp .env.production.local.example .env.production.local
mkdir -p osm/source osm/replication osm/integration/runs log
chmod 700 osm/integration/runs
chmod 600 .env.db .env.production.local
Do not overwrite existing configuration during upgrades. No recursive project ownership change is needed. Data workers run as root inside their containers; the web server and email worker use node. Keep log/ traversable and its files readable by the web server (for example directory 755, files 644, without secrets).
Set distinct PostgreSQL administrator and application passwords in .env.db. The application role is osm. For a new database, keep POSTGRES_DB=lebontag. Configure .env.production.local:
DB_HOST=db
DB_PORT=5432
DB_NAME=lebontag
DB_USER=osm
DB_PASS=SAME_VALUE_AS_LBT_DB_PASSWORD_IN_ENV_DB
DB_SSL=false
DB_SCHEMA=lebontag
DB_SRID=4326
LBT_CAPTURE_RELATION_CONTEXT=1
Also configure AUTH_URL, AUTH_SECRET, authentication providers, email secrets and OSM_DATA_URL / OSM_REPL_SERVER_URL. The PBF, its .state.txt file and diffs must describe the same extract; see Configuration. Use literal values without variable interpolation; hexadecimal values are suitable for new passwords. Do not assume interpolation between separate env files.
Published ports bind to 127.0.0.1: PostgreSQL 5432 and web 3000. A host reverse proxy can reach http://127.0.0.1:3000; a containerized proxy requires an explicitly shared network. Containers connect to PostgreSQL at db:5432, not localhost.
Optional project settings belong in the same .env.db file:
COMPOSE_PROJECT_NAME=lebontag-v2
LBT_POSTGRES_PORT=5432
LBT_WEB_PORT=3000
LBT_POSTGRES_IMAGE=pgrouting/pgrouting:17-3.5-main
Keep the same COMPOSE_PROJECT_NAME for every command, including the scheduler; avoid overriding it with a different -p. Application upgrades must not change the PostgreSQL major version of an existing volume.
2. Explicit target authorization
Everything is defined in .env.production.local: connection, mode, limits, target and emails. No files under config/ are required. Run sha256sum osm/style/default.style and add this JSON on one line, single-quoted:
LBT_INTEGRATION_MODE=check
LBT_INTEGRATION_LIMIT=500
LBT_INTEGRATION_TIMEOUT=600
LBT_INTEGRATION_OBJECT_LIMIT=1000
LBT_INTEGRATION_TARGET='{"format":1,"host":"db","port":"5432","database":"lebontag","user":"osm","schema":"lebontag","reference_schema":"osm2pgsql","reference_prefix":"osm","srid":2154,"output":"pgsql","osm2pgsql_version":"2.2.0","style_sha256":"SHA256_DU_STYLE"}'
Replace the hash and adapt the explicitly authorized target; host=db, port=5432 refer to the internal Docker connection. Style, version, database and role checks remain mandatory. See Service configuration. Do not derive authorization automatically from DB_*. The file must not be group- or world-writable.
3. Build and initialize a new installation
docker compose --env-file .env.db --profile tools --profile automation config --quiet
docker compose --env-file .env.db --profile tools --profile automation build
docker compose --env-file .env.db up -d db
docker compose --env-file .env.db logs -f db
Leave log following after PostgreSQL is ready and SQL initialization finished without error. On an empty volume only, PostgreSQL runs sql/init.sql, sql/lebontag_v2.sql, then the osm role password/grants script. The data image provides a real Python venv and osm2pgsql 2.2.0; neither is required on the host.
docker compose --env-file .env.db run --rm initializer
The initializer streams the PBF, checks that its state file did not change during download, loads the data and initializes replication progress only after success. It refuses an existing or already-used reference. If loading fails after it starts, inspect logs and repair or restore before retrying; never enable replication on an incomplete initialization.
After the success message:
docker compose --env-file .env.db up -d webapp
Sign in to create the first account. Verify this is the intended administrator before running:
docker compose --env-file .env.db run --rm make-first-user-admin
Configure footprints, professional groups, object groups, rights and segmentation options in the application. The pipeline is not scheduled yet.
4. Check and enable jobs
Run one replication chain and an integration check:
docker compose --env-file .env.db run --rm pipeline python3 lbt-docker-job.py pipeline
docker compose --env-file .env.db run --rm integration python3 lbt-docker-job.py integration --mode check
The pipeline executes download, ingestion, automatic validation and footprint intersections in order, stopping on failure. Integration preserves refusals, pending reviews and incomplete-context blockers; do not force database states to bypass them.
Inspect administration monitoring and osm/integration/runs/. A blocked or partial batch may be expected. Like systemd, the Docker wrapper treats business exit codes 0 and 2 as scheduler success, while preserving the actual code in output and reports. Scheduler success does not mean every object was integrated.
After checking the target and reports:
docker compose --env-file .env.db run --rm integration python3 lbt-docker-job.py integration --mode execute
This requests segmentation with reason after-integration; the segmentation script checks whether rebuilding is enabled and required. A segmentation failure is recorded separately and does not undo a confirmed integration. An uncertain append is never retried automatically.
Then set LBT_INTEGRATION_MODE=execute in .env.production.local:
docker compose --env-file .env.db --profile automation up -d pipeline integration segmentation cleaner
# Inspect startup logs before enabling scheduling:
docker compose --env-file .env.db --profile automation up -d ofelia
| Job | Europe/Paris schedule |
|---|---|
| Full pipeline | Hourly |
| Integration | 03:30 |
| Segmentation | After integration when needed, and nightly at 04:15 |
| Cleanup | 05:30 |
| Optional emails | Every 5 minutes |
On segmentation-container startup, a check also requests rebuilding if no successful publication covers the latest 04:15 deadline. This catch-up is for segmentation; restarting containers does not trigger an append. Segmentation remains optional through db_pgr_update.
Business locks remain active. Docker jobs also prevent overlap of the same job. Do not run legacy cron jobs or host services against these data in parallel.
After editing env files, recreate affected containers and restart Ofelia so it discovers their new IDs. Restart alone does not reload environment variables:
docker compose --env-file .env.db --profile automation up -d --force-recreate integration segmentation
docker compose --env-file .env.db restart ofelia
5. Optional emails
The worker reads .env.production.local, like the web application.
In that file, set LBT_DIGEST_WORKER_URL to the public HTTPS URL reachable from the container, and use the same LBT_EMAIL_DIGEST_SECRET as the application. http://localhost:3000 would point to the email container itself; http://webapp:3000 is rejected by the worker's HTTPS policy. Configure SMTP and subscriptions in the application too.
docker compose --env-file .env.db --profile emails build email-digests
docker compose --env-file .env.db --profile emails up -d email-digests
docker compose --env-file .env.db restart ofelia
The worker calls the application's protected route. With this shared environment it also receives the other application variables and must be treated as a trusted process.
6. Operations and upgrades
Scheduled command output is available through docker compose --env-file .env.db logs ofelia. Business logs remain in log/, reports in osm/integration/runs/, and states in PostgreSQL. Administration monitoring is authoritative for blockers and the latest successful publication. For needs_review, suspend integration scheduling, inspect the batch and follow controlled recovery; never simply repeat the append.
To upgrade an existing installation:
- Record the actual Compose project, PostgreSQL image and volume (
docker compose --env-file .env.db ls,docker volume ls,docker inspect). Preserve the project inCOMPOSE_PROJECT_NAME, the database name (sometimespostgres) and its PostgreSQL major version. - Stop Ofelia, wait for active jobs to finish using their logs and PostgreSQL activity, then stop workers and web using the old configuration. Do not interrupt an append. Disable any old cron jobs or host timers writing to this database.
- Back up the entire database, replication files, style and configurations. Never use
docker compose --env-file .env.db down -vto upgrade. - Install the new files. Follow Upgrades for the SQL chain matching the starting version and any explicitly required reload. Docker image rebuilding replaces package installation and host systemd setup.
- Run each migration in order, adapting the database name:
docker compose --env-file .env.db up -d db
docker compose --env-file .env.db exec -T db psql -U postgres -d lebontag -v ON_ERROR_STOP=1 < sql/update_2.7.4_to_2.8.0.sql
Also adapt -U if the PostgreSQL administrator has a different name. Never run lebontag_v2.sql over an existing database.
- If the application previously connected as
postgres, setLBT_DB_PASSWORDin.env.db, recreatedbto load it, then configure the application role:
docker compose --env-file .env.db up -d db
docker compose --env-file .env.db exec -T db bash /docker-entrypoint-initdb.d/002_app_role.sh
LeBonTag objects must belong to osm, as in the provided SQL; inspect any locally-created objects owned by another role. Change DB_USER=osm and DB_PASS in .env.production.local, and update LBT_INTEGRATION_TARGET. Changing POSTGRES_PASSWORD in a file does not reset the password of an existing PostgreSQL volume.
- Build images, validate configuration and start
webapp. Do not runinitializerduring an upgrade: preserve the reference and replication sequence unless following an explicit reload procedure. - Repeat the manual check in step 4. After success, start workers and Ofelia. Use
--remove-orphanswith all required profiles enabled to remove obsolete Compose services such asupdater. Inspect reports and logs after the first scheduled run.
docker compose --env-file .env.db --profile tools --profile automation build
docker compose --env-file .env.db --profile automation config --quiet
docker compose --env-file .env.db up -d webapp
# After checking, with LBT_INTEGRATION_MODE=execute:
docker compose --env-file .env.db --profile automation up -d --remove-orphans
# Also enable --profile emails if using email notifications.
Do not install host systemd units for this Docker deployment. Enable Docker Engine at boot; persistent containers use restart: unless-stopped.
Development
docker compose --env-file .env.db -f docker-compose.yml -f docker-compose.dev.yml up -d webapp mounts sources for Next.js. Set .env.development.local to DB_HOST=db and internal port 5432. Use a separate directory and COMPOSE_PROJECT_NAME to isolate data. Do not activate automatic workers against production volumes from this environment.