Installation — overview
Liberty Next ships as a public OCI image (ghcr.io/fblettner/liberty-next) plus a release/ directory carrying three compose files + two TLS overlays + an apps overlay + four helper scripts that wire them together. Pick a layout, run one command, you're in.
| Layout | Runtime | Services | Use case |
|---|---|---|---|
| Light | Docker Compose | liberty-next + SQLite (on a volume) | Local trial, single-user demo, quick eval. |
| Full | Docker Compose | liberty-next + PostgreSQL 16 + Traefik + pgAdmin + Portainer | Production / staging on a single host. |
| Swarm | Docker Swarm | same five services, with deploy.* constraints + overlay networking | Single or multi-node swarm. |
| pipx | Plain Python | liberty-next only (default SQLite, point at any DB) | Docker-averse hosts, Python-only environments. |
Both Light and Full can be layered with TLS (Let's Encrypt or operator-provided certs) and with the licensed-apps overlay (Nomasx-1 / Nomajde / Nomaflow plugins). All four overlays are wired automatically by ./install.sh flags; you never juggle -f flags by hand.
The 60-second install
git clone https://github.com/fblettner/liberty-next.git
cd liberty-next/release
./install.sh # interactive — asks light vs full
# OR — non-interactive
./install.sh light # single container, SQLite
./install.sh full # 5-service production stack (latest tag)
./install.sh full --tag 7.0.2 # full stack, pinned to a specific release
What install.sh does on the first run:
| Step | What |
|---|---|
| 1. Detects stale Docker volumes from a previous install. | If pg-data / pgadmin-data / liberty-data exist but .env is missing, the script REFUSES to start and tells the operator to re-run with --reset (wipe + start fresh) or restore the previous .env (the secrets baked into the volumes must match). |
2. Generates .env with cryptographically-random secrets (no $ chars — Compose substitution can't eat them). | First run only — re-runs preserve an existing .env. |
3. Writes COMPOSE_FILE=docker-compose.<layout>.yml[:overlays...] to .env. | Every subsequent docker compose <cmd> (without -f) auto-merges the right files. Critical — operators must NOT pass -f manually after install. |
4. Pulls images via docker compose pull and runs docker compose up -d. | Idempotent — re-running against an up stack just re-applies the compose file. |
5. Waits up to 120 s for the container healthcheck (GET /info). | Reports healthy once the SPA + API are ready. |
6. Prints the SPA URL + the generated admin password + the pgAdmin / Portainer / Traefik dashboard URLs. | The password is also in .env (mode 0600). |
For Docker Swarm, the equivalent helper is ./deploy-swarm.sh (covered in Docker → Swarm).
The four install.sh flags worth knowing
| Flag | Purpose |
|---|---|
--tag <version> | Pin LIBERTY_IMAGE_TAG at install time (e.g. --tag 7.0.2). Default latest (every merge to main → new release; latest always reflects current main). Ignored when .env already exists — edit the var directly there. |
--reset | docker compose down -v + delete .env first. Use when a previous install left stale pg-data with the old password (postgres init only runs on a brand-new volume — fresh secrets + stale volume = auth-fails-forever). |
--apps <wheel-or-URL> | After bringing up the base stack, chain to install-apps.sh with the given wheel — licensed apps (Nomasx-1 / Nomajde / Nomaflow) deploy in the same command. The license key is set afterwards via Settings → App → License in the UI (encrypted at rest in app.toml). |
--ssl letsencrypt --domain --email (full only) | Wire Let's Encrypt automatically. Requires the host be reachable from the public internet on :80/:443 for the TLS-ALPN challenge. |
--ssl provided --cert-dir --cert-file --key-file (full only) | Wire operator-provided certs (corporate CA, internal PKI, air-gapped install). install.sh validates the files exist, bind-mounts the cert directory, generates traefik/dynamic/tls.yml. |
The four flags compose together — ./install.sh full --tag 7.0.2 --ssl letsencrypt --domain liberty.example.com --email ops@example.com --apps ./liberty_apps-7.0.1-py3-none-any.whl is a single-command production install with TLS + the licensed bundle. After the UI is up, paste the license key into Settings → App → License. (--license-key was removed from install.sh — the key now lives in app.toml encrypted at rest, not in .env.)
A wipe-and-exit flag is also worth knowing:
| Flag | Purpose |
|---|---|
--reset | docker compose down + docker volume rm of every Liberty data volume (pg-data, pgadmin-data, portainer-data, liberty-data, liberty-config) + deletes .env, then exits. The traefik-acme volume is intentionally preserved (Let's Encrypt rate-limits at 5 certs / 7 days / domain set — wiping it on every reset cycle would burn through that immediately). Combining --reset with --apps / --ssl errors out — re-run install.sh with your flags after the reset finishes. Use when a previous install left stale volumes whose credentials no longer match a freshly-generated .env. |
The COMPOSE_FILE mental model
After ./install.sh full --ssl letsencrypt --apps <wheel>, .env carries:
COMPOSE_FILE=docker-compose.full.yml:docker-compose.tls-letsencrypt.yml:docker-compose.apps.yml
Docker Compose reads COMPOSE_FILE for every command — docker compose ps / pull / up -d / down / logs / restart automatically merge every overlay listed. The operator never types -f.
| Do | Don't |
|---|---|
docker compose pull && docker compose up -d (picks up every overlay) | docker compose -f docker-compose.full.yml up -d (drops the apps + TLS overlays) |
docker compose logs -f liberty-next (merged context) | docker compose -f docker-compose.full.yml logs -f (no overlays) |
install-apps.sh and the --ssl flag both append to COMPOSE_FILE — re-running install.sh with a new --ssl mode (or chaining install-apps.sh) updates the value cleanly. Manual edits to .env are also fine; the chain is colon-separated, order matters (overlays apply right-to-left).
The three Docker layouts
Light — single container, SQLite
What you get:
- One container (
liberty-next) on port8000. - SQLite framework DB (auth + Nomaflow run history) persisted on a Docker volume.
- Operator-edited TOML files persisted on a second volume — saved through Settings → … in the SPA; no host bind-mount needed.
- No Postgres, no Traefik, no TLS — the framework is exposed directly on
:8000.
Use for trials, demos, evaluation, single-user installs. The licensed apps (Nomasx-1 / Nomajde) work on Light too if you don't need a multi-user Postgres — ./install.sh light --apps <wheel> is supported.
Volumes:
| Volume | Carries |
|---|---|
liberty-data | SQLite DB + auth.toml (Argon2 password hashes). |
liberty-config | Every TOML the operator edits (connectors / dictionary / menus / screens / charts / dashboards). |
Full — production stack behind Traefik
Five services on the host's port 80 (or 443 once TLS is wired):
| Service | Path | What |
|---|---|---|
| Traefik | /traefik | Reverse proxy + dashboard. Basic-auth (default admin/admin — change in traefik/dynamic/dynamic.yml). |
| liberty-next | / (catchall) | SPA + API + admin. Connects to the bundled Postgres for the framework DB. |
| Postgres 16 | (internal :5432, exposed by default) | Framework DB (auth, Nomaflow run history) + a place to host customer pools (Nomasx-1 / Nomajde data). |
| pgAdmin | /pgadmin | Postgres GUI. |
| Portainer | /portainer | Docker UI. |
Volumes:
| Volume | Carries |
|---|---|
liberty-config | Operator-edited TOMLs. |
pg-data | Postgres database files. |
pgadmin-data | pgAdmin registrations + preferences. |
portainer-data | Portainer state. |
traefik-acme | Let's Encrypt certificate storage (when TLS Let's Encrypt is on). |
This is the layout the licensed bundles (Nomasx-1, Nomajde) deploy against — the bundled Postgres hosts their default pool. See Deploy prebuilt apps.
Swarm — Docker Swarm, single or multi-node
Same five services, but with deploy.* keys, overlay networking, Traefik's --providers.swarm and stateful services pinned to a single manager. Deploy / update / status with ./deploy-swarm.sh — docker stack deploy has no --env-file flag, so the helper sources .env into the shell first.
Note: the --apps and --ssl flags on install.sh are Compose-only — Swarm operators apply overlays by editing the stack file or by chaining a separate docker stack deploy with the overlay merged in.
The licensed-apps overlay
Nomasx-1, Nomajde and the bundled Nomaflow jobs ship as a single liberty_apps-<version>-py3-none-any.whl wheel. Two install paths:
Single-command (fresh host)
./install.sh full --apps ./liberty_apps-7.0.1-py3-none-any.whl
That's everything — base stack + licensed apps in one go. Once the SPA is up, paste the vendor-signed license JWT into Settings → App → License (encrypted at rest with the install master key, live on save — connectors rebuild without restart). See App settings.
Or split it: base first, apps later
./install.sh full # base stack
./install-apps.sh ./liberty_apps-7.0.1-py3-none-any.whl # add the apps
What install-apps.sh does:
- Materializes the wheel into
./apps/via a throwawaypython:3.12-slimcontainer — your host needs no local pip or Python install. The wheel ships with aliberty-apps install --target DIRCLI that copiesconfig/+plugins/into the destination, preserving operator-edited TOMLs. - Updates
.env— appendsdocker-compose.apps.ymltoCOMPOSE_FILEand setsAPPS_HOST_PATH=<absolute path>. The license key is not written to.env— set it via the UI after the apps are visible. - Restarts the stack —
docker compose up -dpicks up the apps overlay automatically viaCOMPOSE_FILE(no-fjuggling).
Full detail: Deploy prebuilt apps.
The pipx path — no Docker at all
If you don't want containers (small install on a single host, locked into a Python-only environment):
pipx install liberty-next
liberty-next # → API + SPA on http://localhost:8000
pipx puts Liberty Next in its own isolated venv so its dependencies can't conflict with system Python. The four CLI commands (liberty-next, liberty-admin, liberty-license, liberty-crypto) land on the PATH. Upgrades: pipx upgrade liberty-next. See Python server.
You'll point LIBERTY_DB_URL at an existing Postgres (or stick with the default SQLite — ./liberty.db in the working directory). Config TOMLs are read from ./config/<name>.toml; set LIBERTY_APPS_DIR=/etc/liberty-next/ to keep them somewhere stable.
At a glance
Required environment variables
Two values are required regardless of layout — install.sh generates both with openssl rand. When using pipx or rolling your own compose, generate them yourself.
| Variable | Why | How to generate |
|---|---|---|
LIBERTY_JWT_SECRET | Signs every access + refresh token. Must be stable across restarts — a rotated key invalidates every outstanding token (forces every user to re-sign-in). | python -c "import secrets;print(secrets.token_urlsafe(48))" |
LIBERTY_MASTER_KEY | Field-level encryption key — encrypts secrets (pool passwords, API tokens) at rest in the TOMLs. Must stay constant or ENC: values become unreadable. | python -c "import secrets;print(secrets.token_urlsafe(32))" |
Common optional vars (the full list is in release/.env.example):
| Variable | Default | What |
|---|---|---|
LIBERTY_IMAGE_TAG | latest | Pin to a specific release tag for stability (7.0.1, 7.0.2, etc.). Use ./install.sh --tag <ver> to set it at install time. |
LIBERTY_ADMIN_PASSWORD | (generated, shown once in the install summary) | Bootstrap password for the first-run admin user. install.sh generates a random value, exports it to the shell for the boot, then drops it — not written to .env. On re-runs the existing admin keeps its prior password; reset with docker exec liberty-next liberty-admin reset-admin-password. |
POSTGRES_PASSWORD | (generated) | Full layout only — bundled Postgres password. |
PGADMIN_PASSWORD | (generated) | Full layout only — pgAdmin admin password. The default email is admin@liberty.fr (override with PGADMIN_EMAIL). |
APPS_HOST_PATH | (set by install-apps.sh) | Absolute path to the materialized ./apps/ directory. The apps overlay bind-mounts this at /apps:ro. |
COMPOSE_FILE | (set by install.sh + install-apps.sh) | Colon-separated chain of compose files Docker Compose auto-merges. |
LIBERTY_DOMAIN, ACME_EMAIL | (set by --ssl letsencrypt) | TLS hostname + ACME contact. |
CERT_HOST_PATH | (set by --ssl provided) | Host directory bind-mounted at /etc/certs:ro for operator certs. |
The license key, Anthropic API key and OIDC client secret used to live as env vars (LIBERTY_LICENSE_KEY, ANTHROPIC_API_KEY, LIBERTY_OIDC_CLIENT_SECRET). They now live in app.toml encrypted at rest with the install master key — edit them via Settings → App in the SPA after the stack is up. The env-var path still works as a fallback (set the var + reference it from app.toml as ${VAR}). See App settings.
$ in passwordsDocker Compose performs ${VAR} substitution on every value in .env too — a literal $ in a password gets eaten (e.g. POSTGRES_PASSWORD=foo$bar becomes foo because Compose tries to expand $bar). Either generate passwords without $ (recommended — install.sh does this), or escape every $ as $$.
Read in order
| Step | Page |
|---|---|
| 0 | This overview. |
| 1 | Pick a path: Docker (covers all three Docker layouts) or Python server (pipx). |
| 2 | Wire TLS — Traefik walks the two --ssl modes (Let's Encrypt and operator-provided). |
| 3 | Use the bundled visual tools — Portainer + pgAdmin. |
| 4 | Production hardening — OIDC, JWT rotation, backups, multi-replica caveats: Production. |
| 5 | Deploy NomaUBL / Nomasx-1 / Nomajde on top: Deploy prebuilt apps. |
| 6 | When a new release ships: Upgrading. |
Sanity check — what "installed" looks like
After the chosen path:
curl http://<host>:<port>/inforeturns a JSON payload withversion,frontend_built, counts of loaded screens / menus / connectors.- The SPA renders at
http://<host>:<port>/. - Sign in as
adminwith the password printed byinstall.sh(shown once during the install; not stored in.env). - The container
liberty-nextreports healthy indocker ps(the bundled healthcheck hits/infoevery 30 s).
If any of those don't hold, jump to the path's troubleshooting section.
What's next
- Docker — the three Docker layouts in detail, with the helper-script walk-throughs and the COMPOSE_FILE discipline.
- Python server — pipx-based install for Docker-averse hosts.
- Production — TLS, backups, hardening.