Skip to main content

Installation from sources

This procedure installs a new instance on Debian 12 or 13, from an account with sudo. For an existing database, follow Upgrades; for Docker, follow Docker installation.

Root administers the repository, venv and dependencies in /opt/lebontag-v2. Every command writing there uses sudo; reading files and cd do not require it. Do not make the project world-writable or recursively change its ownership.

All native services run under the dedicated lbt-integration account, created during commissioning. Root retains ownership of code and dependencies. Sudo below is for installation, building and administration only.

This page should be read in order: installation, service account, web, OSM loading, checks and scheduling. Optional steps are marked; do not enable timers before the last step.

1. Prepare the server​

Provide PostgreSQL with PostGIS 3, hstore and pgRouting, locally or remotely, plus a domain and an HTTPS reverse proxy for production.

sudo apt update
sudo apt upgrade
sudo apt install -y ca-certificates curl wget xz-utils dos2unix git openssl acl postgresql-client

Optional proxy​

Configure proxies according to your infrastructure. Sudo does not automatically preserve user-shell proxy variables. If needed and permitted by sudoers, use sudo --preserve-env=http_proxy,https_proxy,no_proxy ... for the relevant command, or configure the tool's proxy settings (APT, Git, etc.). Services have their own environment: also check network access from background jobs. No cron configuration is required.

2. Clone the repository​

The destination must be unused; archive an old installation first. Do not clone into the directory of an active instance.

sudo git clone --branch main https://gitlab.com/Geonov/lebontag-v2.git /opt/lebontag-v2
sudo git -C /opt/lebontag-v2 rev-parse HEAD

Record the deployed commit SHA. The following commands use that revision's files.

3. Create the database and initial schema​

Example for local PostgreSQL, a new osm role and a new lebontag_production database. The password prompt is interactive. If the role already exists, verify its privileges and reuse its password rather than changing it without considering other databases.

sudo -u postgres createuser --login --pwprompt osm
sudo -u postgres createdb --owner=osm lebontag_production
sudo -u postgres psql -v ON_ERROR_STOP=1 -d lebontag_production -c 'CREATE EXTENSION postgis; CREATE EXTENSION hstore; CREATE EXTENSION pgrouting;'
sudo psql -h localhost -U osm -d lebontag_production -v ON_ERROR_STOP=1 -f /opt/lebontag-v2/sql/lebontag_v2.sql

sudo selects the Linux account; -U osm selects the PostgreSQL role. For a remote server, have its PostgreSQL administrator create the database and adapt -h, -p and -d for import. Never put passwords on the command line.

The script creates the lebontag, osm2pgsql and osm2pgrouting schemas with the expected owners. It includes the current migrations: do not subsequently run upgrade SQL on this new database.

4. Install dependencies​

OSM tools and osm2pgsql​

sudo apt install -y --no-install-recommends osmctools gdal-bin osmium-tool pyosmium
sudo apt install -y make cmake g++ libboost-dev libexpat1-dev zlib1g-dev libbz2-dev libpq-dev libproj-dev libluajit-5.1-dev pandoc nlohmann-json3-dev

Compile osm2pgsql 2.2.0, matching the supported profile. Its binary resides in /usr/local/bin, independently of any APT version used by other applications.

sudo install -d -m 0755 /usr/local/src
sudo curl --fail --location --retry 3 https://github.com/osm2pgsql-dev/osm2pgsql/archive/refs/tags/2.2.0.tar.gz -o /usr/local/src/osm2pgsql-2.2.0.tar.gz
sudo tar -xzf /usr/local/src/osm2pgsql-2.2.0.tar.gz -C /usr/local/src
sudo cmake -S /usr/local/src/osm2pgsql-2.2.0 -B /usr/local/src/osm2pgsql-2.2.0/build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=OFF -DWITH_LUAJIT=ON
sudo cmake --build /usr/local/src/osm2pgsql-2.2.0/build --parallel 2
sudo cmake --install /usr/local/src/osm2pgsql-2.2.0/build
/usr/local/bin/osm2pgsql --version

Python virtual environment​

Root creates and populates the venv. Use its interpreter directly: shell activation is unnecessary and packages are not installed into system Python.

sudo apt install -y python3 python3-pip python3-venv python3-dev libpq-dev
sudo python3 -m venv /opt/lebontag-v2/.venv
sudo -H /opt/lebontag-v2/.venv/bin/python3 -m pip install --upgrade pip
sudo -H /opt/lebontag-v2/.venv/bin/python3 -m pip install -r /opt/lebontag-v2/python/requirements.txt
/opt/lebontag-v2/.venv/bin/python3 --version

The integration account will subsequently receive read/execute access to the code and venv, without modification rights.

Node.js and npm​

Install Node 26.3.0, system-wide. This removes dependence on a user's fnm session and ensures sudo and services use the same Node. The following supports amd64 and arm64, checks the published SHA256 and installs into /usr/local. If another application uses Node at this prefix, verify compatibility before replacing it.

sudo bash <<'NODE'
set -euo pipefail
case "$(dpkg --print-architecture)" in
amd64) lbt_node_arch=x64 ;;
arm64) lbt_node_arch=arm64 ;;
*) echo "Unsupported architecture for this example" >&2; exit 1 ;;
esac
lbt_node_version=26.3.0
lbt_node_archive="node-v${lbt_node_version}-linux-${lbt_node_arch}.tar.xz"
install -d -m 0755 "/usr/local/src/node-${lbt_node_version}"
cd "/usr/local/src/node-${lbt_node_version}"
curl --fail --location --retry 3 "https://nodejs.org/dist/v${lbt_node_version}/${lbt_node_archive}" -o "$lbt_node_archive"
curl --fail --location --retry 3 "https://nodejs.org/dist/v${lbt_node_version}/SHASUMS256.txt" -o SHASUMS256.txt
awk -v archive="$lbt_node_archive" '$2 == archive {print}' SHASUMS256.txt > selected.sha256
test -s selected.sha256
sha256sum --check selected.sha256
tar --extract --xz --file="$lbt_node_archive" --directory=/usr/local --strip-components=1 --no-same-owner
NODE
sudo /usr/local/bin/node --version
sudo env PATH=/usr/local/bin:/usr/bin:/bin /usr/local/bin/npm --version

The explicit PATH below makes npm and its child processes use /usr/local/bin/node. Any personal fnm installation is outside this procedure.

Application dependencies​

cd /opt/lebontag-v2
sudo -H env PATH=/usr/local/bin:/usr/bin:/bin HUSKY=0 /usr/local/bin/npm ci

Use npm ci with the repository lockfile; do not subsequently use npm install to change dependency versions arbitrarily.

5. Configure the application env file​

Create the environment file only if absent, then edit it:

sudo test -e /opt/lebontag-v2/.env.production.local || sudo install -m 0600 /opt/lebontag-v2/.env.production.local.example /opt/lebontag-v2/.env.production.local
sudoedit /opt/lebontag-v2/.env.production.local

Set:

  • DB_HOST, DB_PORT, DB_NAME, DB_USER=osm, DB_PASS and DB_SSL for PostgreSQL; keep DB_SCHEMA=lebontag and DB_SRID=4326.
  • AUTH_URL, AUTH_TRUST_HOST=true, AUTH_DEBUG=false, AUTH_SECRET and at least one AUTH_XXX_ID / AUTH_XXX_SECRET provider, using the example's links.
  • Distinct email secrets LBT_MAIL_ENCRYPTION_KEY and LBT_EMAIL_DIGEST_SECRET, plus the job options below. Initially keep LBT_INTEGRATION_MODE=check.

Generate an independent value using openssl rand -hex 32 for each relevant secret. Never regenerate an existing SMTP encryption key during an upgrade.

.env.production.local serves both web and jobs. .env.development.local is unnecessary on a server running a production build. .env.local contains only public version metadata generated below; never add secrets there.

Scheduled Job settings​

The web app and jobs use .env.production.local, copied from its example. .env.development.local is only for development; a server running a production build does not need it, even when verifying a fresh installation. .env.local contains generated public version metadata only. Docker additionally uses .env.db for PostgreSQL initialization and Compose options.

LBT_INTEGRATION_TARGET target on one line, quoted. Explicitly authorize host, port, database, user and schema; these are checked against DB_* and the actual connection. Compute style_sha256 with sha256sum /opt/lebontag-v2/osm/style/default.style after confirming it is the initial import style. The profile remains osm2pgsql 2.2.0, pgsql output, osm2pgsql.osm, EPSG:2154; DB_SRID=4326 concerns LeBonTag. An empty target does not authorize a real database. Do not derive this authorization automatically from the connection: it must detect a target mistake.

An empty LBT_DIGEST_WORKER_URL uses AUTH_URL. Native installations may use HTTP loopback; Docker requires the public HTTPS URL reachable from its container. The worker uses the existing LBT_EMAIL_DIGEST_SECRET in the same file. OAuth and SMTP secrets are unchanged.

Use literal values without export, shell substitutions or ${VARIABLE} interpolation. For new secrets, hexadecimal values avoid interpolation differences between readers. Do not source this file: Python, Node, systemd and Compose read it directly. Services use .env.production.local; manual Python commands can select an environment with --env-file.

6. Check and build​

cd /opt/lebontag-v2
sudo -H env PATH=/usr/local/bin:/usr/bin:/bin /usr/local/bin/npm run prepare:env
sudo -H env PATH=/usr/local/bin:/usr/bin:/bin NEXT_TELEMETRY_DISABLED=1 /usr/local/bin/npm run check
sudo -H env PATH=/usr/local/bin:/usr/bin:/bin NEXT_TELEMETRY_DISABLED=1 /usr/local/bin/npm run build
sudo dos2unix /opt/lebontag-v2/scripts/*.sh
sudo chmod +x /opt/lebontag-v2/scripts/*.sh

Do not start the application if checks or build fail.

7. Create the service account and permissions​

All six native services (web, pipeline, integration, segmentation, cleaning, email-digests) use lbt-integration. This Linux account differs from PostgreSQL's osm role, the non-superuser owner of application and reference objects.

Root continues administering code, units, Node, the venv and dependencies. Installation, builds and migrations use sudo; routine jobs do not. The shared account reads the common env file: it is a trusted application account, not isolation between modules.

After building, prepare directories and ACLs:

sudo apt install -y acl
getent group lbt-integration >/dev/null || sudo groupadd --system lbt-integration
id lbt-integration >/dev/null 2>&1 || sudo useradd --system --gid lbt-integration --home-dir /nonexistent --no-create-home --shell /usr/sbin/nologin lbt-integration
sudo install -d -o lbt-integration -g lbt-integration -m 0700 /opt/lebontag-v2/log /opt/lebontag-v2/osm/replication /opt/lebontag-v2/osm/changeset /opt/lebontag-v2/osm/osc_update /opt/lebontag-v2/osm/integration/runs /opt/lebontag-v2/.next/cache
sudo setfacl -m u:lbt-integration:--x /opt /opt/lebontag-v2 /opt/lebontag-v2/osm /opt/lebontag-v2/osm/integration /opt/lebontag-v2/osm/style
sudo setfacl -R -m u:lbt-integration:r-X /opt/lebontag-v2/python /opt/lebontag-v2/scripts /opt/lebontag-v2/.venv /opt/lebontag-v2/node_modules /opt/lebontag-v2/.next /opt/lebontag-v2/public
sudo setfacl -m u:lbt-integration:r-- /opt/lebontag-v2/package.json /opt/lebontag-v2/next.config.ts /opt/lebontag-v2/tsconfig.json /opt/lebontag-v2/.env.local /opt/lebontag-v2/osm/style/default.style
sudo chmod 0600 /opt/lebontag-v2/.env.production.local
sudo setfacl -m u:lbt-integration:r-- /opt/lebontag-v2/.env.production.local
sudo -u lbt-integration /opt/lebontag-v2/.venv/bin/python3 --version
sudo -u lbt-integration /usr/local/bin/osm2pgsql --version
sudo -u lbt-integration /usr/local/bin/node --version

If the venv contains Python symlinks, check their targets too. Do not install executables under /root: ProtectHome=true prevents access. Reapply ACLs after replacing code, dependencies or the build.

ServicePermitted persistent writes
Web.next/cache/
Pipelinelog/, osm/replication/, osm/changeset/, osm/osc_update/
Integration and segmentationosm/integration/runs/
Cleanuplog/, osm/changeset/, osm/osc_update/
EmailNone; HTTP call to the application

All retain ProtectSystem=strict, NoNewPrivileges=true and private temporary storage. Linux permissions alone are insufficient: units also restrict writable paths. Private logs remain readable in administration because web uses the same account.

8. Install units without enabling timers​

The repository supplies .service and .timer files; no drop-in is required. Install only the desired optional services. Do not enable timers before checking jobs.

sudo systemctl link /opt/lebontag-v2/services/lbt-web.service
sudo systemctl link /opt/lebontag-v2/services/lbt-pipeline.service /opt/lebontag-v2/services/lbt-pipeline.timer
sudo systemctl link /opt/lebontag-v2/services/lbt-integration.service /opt/lebontag-v2/services/lbt-integration.timer
sudo systemctl link /opt/lebontag-v2/services/lbt-cleaning.service /opt/lebontag-v2/services/lbt-cleaning.timer
# OPTIONAL / OPTIONNEL :
sudo systemctl link /opt/lebontag-v2/services/lbt-segmentation.service /opt/lebontag-v2/services/lbt-segmentation.timer
sudo systemctl link /opt/lebontag-v2/services/lbt-email-digests.service /opt/lebontag-v2/services/lbt-email-digests.timer
sudo systemctl daemon-reload

9. Start web and configure Nginx​

The service runs Next.js directly with system Node and the dedicated account, without PM2. It listens on 127.0.0.1:3000 behind Nginx.

sudo systemctl enable --now lbt-web.service
sudo systemctl status lbt-web.service
sudo journalctl -u lbt-web.service -n 80 --no-pager

Nginx​

sudo apt install -y nginx

Choose one example below. HTTPS requires an existing certificate and key at the configured paths.

# HTTP:
sudo cp /opt/lebontag-v2/nginx/nginx-80.conf /etc/nginx/sites-available/lebontag.conf
# OR HTTPS:
sudo cp /opt/lebontag-v2/nginx/nginx-443.conf /etc/nginx/sites-available/lebontag.conf
sudoedit /etc/nginx/sites-available/lebontag.conf

Replace [WEB_SITE_URL], certificate and key paths, and check listen directives. On a shared server, do not declare a second default_server or remove the default site without verifying it is unused. Proxy to LeBonTag's port 3000; do not expose that port directly to the Internet.

On a new dedicated server, if the unused Debian default site conflicts with default_server, remove its link before checking:

# Only in that case:
sudo rm /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/lebontag.conf /etc/nginx/sites-enabled/lebontag.conf
sudo nginx -t
# Only after successful verification:
sudo systemctl enable --now nginx
sudo systemctl reload nginx

If the link already exists, inspect it instead of recreating it. On a new dedicated server using listen ... default_server, remove the default-site link only after checking it, then rerun nginx -t.

10. Create the first administrator​

Log in to the application with the previously configured authentication service.

To make this first user an administrator (after they log in), run the following SQL command:

INSERT INTO lebontag.lbt_user_userrole (u_id, ur_id) VALUES(1, 1);

11. Load the PBF and initialize replication​

Enable LBT_CAPTURE_RELATION_CONTEXT=1 in .env.production.local before first ingestion. The PBF, state and replication server must match the same extract. Sequence numbers below are examples, not installation defaults.

sudo install -d -m 0755 /opt/lebontag-v2/osm/source

Reference OSM data​

Reference OSM data must contain certain metadata (contributor, date, changeset, etc.).

Go to https://download.openstreetmap.fr/extracts/europe/ and download the file containing your work area, for example:

Place the downloaded file on the server, in ‘/opt/lebontag-v2/osm/source/’ and rename it ‘data.osm.pbf’ (or modify the ‘osm_pbffile’ parameter in the database or from the administration interface).

Warning: never delete this file, as it will be used by the application to load data.

Also download the associated ‘state.txt’ file, for example:

Direct-download example for Hérault; adapt both URLs for another extract. The state must match the downloaded PBF version; if the publication changes during download, retrieve a consistent pair again.

sudo curl --fail --location --retry 3 https://download.openstreetmap.fr/extracts/europe/france/languedoc_roussillon/herault.osm.pbf -o /opt/lebontag-v2/osm/source/data.osm.pbf
sudo curl --fail --location --retry 3 https://download.openstreetmap.fr/extracts/europe/france/languedoc_roussillon/herault.state.txt -o /opt/lebontag-v2/osm/source/state.txt

Now trigger the loading of reference data into the database using an osm2pgsql script:

sudo /opt/lebontag-v2/scripts/lbt-load-data.sh

The load log file will be created in ‘/opt/lebontag-v2/log/lbt-load-data/’. Check its contents.

Continue only after a successful import. On failure, do not start the pipeline or mark the sequence as loaded.

Note the ‘sequenceNumber’ in the state file associated with the PBF, for example: 6844104.

Write this number in the new file /opt/lebontag-v2/osm/replication/sequence.txt, for example:

printf '%s\n' 6844104 | sudo tee /opt/lebontag-v2/osm/replication/sequence.txt >/dev/null

Replace 0000000 with this number in the lebontag.lbt_repl_state table, for example:

UPDATE lebontag.lbt_repl_state SET last_done_seq=6844104 WHERE id=1;

Specify the URL for data replication (diff files per minute) from ‘https://download.openstreetmap.fr/replication’.

For example, ‘https://download.openstreetmap.fr/replication/europe/france/languedoc_roussillon/herault/minute/’.

In SQL:

INSERT INTO lebontag.lbt_setting (s_name, s_value) VALUES ('osm_repl_server','https://download.openstreetmap.fr/replication/europe/france/languedoc_roussillon/herault/minute/') ON CONFLICT (s_name) DO UPDATE SET s_value = EXCLUDED.s_value;

12. Assign permissions to loaded data​

Initial loading with sudo may create root-owned logs and replication state. Before starting jobs, assign only the data directories below to the service account. Verify they are the expected local directories, not links to another installation. Never apply this change to all of /opt/lebontag-v2.

sudo chown -R --no-dereference lbt-integration:lbt-integration /opt/lebontag-v2/log /opt/lebontag-v2/osm/replication /opt/lebontag-v2/osm/changeset /opt/lebontag-v2/osm/osc_update /opt/lebontag-v2/osm/integration/runs /opt/lebontag-v2/.next/cache

13. Configure footprints and business rights​

  • Associate users with rights of footprints, business groups, roles, etc.
  • Activate object groups or create new ones.
  • Set validation rights.

14. Check pipeline, integration and segmentation​

sudo systemctl start lbt-pipeline.service
sudo journalctl -u lbt-pipeline.service -n 100 --no-pager

After this successful run, review and approve a few objects in the application. Then check integration:

# LBT_INTEGRATION_MODE=check
sudo systemctl start lbt-integration.service
sudo systemctl show lbt-integration.service -p Result -p ExecMainStatus
sudo journalctl -u lbt-integration.service -n 150 --no-pager

The pipeline runs its four steps in order and stops on the first error. Check mode performs no append but records its checks. Approve a few objects through the application and inspect blockers. Result=success with ExecMainStatus=2 means business blockers, possibly following partial integration; it is not a service failure.

After checking the target, set LBT_INTEGRATION_MODE=execute in the env file and rerun integration. Inspect the integrated batch, commit_confirmed, verification.json and administration monitoring. The hook requests segmentation when required; its failure is journaled separately and cannot undo a confirmed append.

Segmentation is enabled by db_pgr_update=1. Initial loading creates its destination tables. Publication is transactional, but segment IDs are unstable and this is not a complete routing guarantee. Readers may wait during publication. For an initial manual rebuild, only if the hook has not just published:

sudo -u lbt-integration /opt/lebontag-v2/.venv/bin/python3 /opt/lebontag-v2/python/lbt-rebuild-segments.py --env-file /opt/lebontag-v2/.env.production.local --output-root /opt/lebontag-v2/osm/integration/runs --reason manual

15. Check cleanup​

The cleanup service is installed in step 8. It uses log_retention and osm_retention, strictly positive day counts. It retains metadata referenced by history and does not follow symlinks. It does not purge the PBF, replication queue or integration reports/journals.

sudo systemctl start lbt-cleaning.service
sudo journalctl -u lbt-cleaning.service -n 100 --no-pager

16. Configure optional emails​

Configure SMTP in administration, verify a test message and subscriptions before enabling the email timer. The worker shares the env file, runs as lbt-integration and needs no persistent writes. An empty LBT_DIGEST_WORKER_URL uses AUTH_URL; keep the same LBT_EMAIL_DIGEST_SECRET as web.

17. Enable timers and monitor​

sudo systemctl enable --now lbt-pipeline.timer lbt-integration.timer lbt-cleaning.timer
# OPTIONAL / OPTIONNEL :
sudo systemctl enable --now lbt-segmentation.timer lbt-email-digests.timer
sudo systemctl list-timers 'lbt-*' --all

The pipeline runs approximately hourly, integration at 00:30 and segmentation at 01:30 Europe/Paris (with random delay for the latter two). Cleanup runs daily at 02:30 Europe/Paris. Nightly segmentation runs even without new integration; its persistent timer may fire immediately upon activation. After= orders simultaneous starts but does not guarantee the preceding service succeeded. Business locks remain necessary.

Monitor the first nightly cycle, freshness in About and batches in administration. For needs_review, suspend integration scheduling and inspect the batch; never blindly repeat an append. Failed segmentation retains the previous publication through rollback. Reports remain under osm/integration/runs/.

Log locations​

  • /log/lbt-autovalidation: self-validated OSM objects
  • /log/lbt-fetch: retrieval of modified OSM objects
  • /log/lbt-footprints-intersection: intersection between modified objects and footprints
  • /log/lbt-ingest : inserting modified OSM objects into the database
  • /log/lbt-load-data: loading of reference data
  • /osm/integration/runs/: integration and segmentation reports of validated OSM data; systemd logs through journalctl.