Managed demo environments

This guide is for an administrator who manages public, disposable LMSTool demonstrations. Run lifecycle commands from a trusted LMSTool checkout on the administrator workstation. The command connects as a named server administrator, uses passwordless sudo for privileged operations, and uses Dokku's restricted dokku SSH endpoint for Git deployment.

The server does not contain a demo-manager installation, source checkout, release mirror, registry database, provisioner account, or delegated administration user. Dokku, PostgreSQL/PostGIS, marked storage, and HTTPS are the live state.

Before the first demo

Prepare a minimal Ubuntu server by following Install a demo server. You need:

  • Python 3.12 on the workstation, using its latest security patch release;
  • Git and OpenSSH on the workstation;
  • SSH access as a named administrator with passwordless, non-interactive sudo;
  • Dokku 0.38.22 or a later version with equivalent named-storage behavior;
  • PostgreSQL/PostGIS and an empty demo_template database;
  • a dedicated root-owned demo storage directory;
  • wildcard DNS for *.demo.example.org pointing to the server;
  • the Dokku Let's Encrypt plugin and an administrator email address; and
  • the workstation public key registered with Dokku for Git pushes.

Demos are public. Do not use real personal, operational, or production data. Approved datasets are described in Demo seed datasets.

Workstation and server roles

Use the workstation checkout to choose the code and dataset and to run ops/demo/demo-manager. The command temporarily copies its reviewed remote executor to a random root-only directory below /run and removes it after the operation. It pushes the chosen commit directly from the workstation to dokku@SERVER:APP.

The server creates database and Django secrets locally. The initial application administrator password is entered without echo on the workstation and travels only over the active encrypted SSH input stream. Secrets are never command arguments or Dokku configuration values.

Resolve an exact release

Release approval is an administrator decision made through the project's review process. The command proves availability and identity, not approval.

List useful local refs and full commit identifiers:

git fetch --prune --tags origin
git log --oneline --decorate -20
git show-ref --heads --tags
git rev-parse --verify 'HEAD^{commit}'

The last command prints the 40-character commit that will be deployed. A tag is optional. Any local ref or full commit is accepted if it resolves to exactly one commit in a non-shallow checkout.

Create a demo

Ensure the approved release exists in the workstation checkout before running create. The server does not fetch missing releases.

ops/demo/demo-manager --host assela@demo-server.example.org create demo_tutorial \
  --release 0123456789abcdef0123456789abcdef01234567 \
  --seed tutorial \
  --admin-username demo_admin \
  --admin-email demo-admin@example.org

Enter a unique password twice when prompted. This is the password for the new demo website account named by --admin-username; it is not an SSH or sudo password. Typing is deliberately invisible, so type the password and press Enter at each prompt. The command creates and marks the Dokku anchor first, then the database roles/database and named storage. It pushes the exact commit, tests database connectivity from the deployed container with a short timeout, and only then runs migrations. It subsequently loads the approved workflow dataset, provisions the login, enables Let's Encrypt, and reports the public HTTPS address only after verification.

Valid canonical names begin with demo_ and use lowercase letters, numbers, and underscores. The part after demo_ contains 2–40 characters, so the complete name contains 7–45 characters. The corresponding Dokku app and DNS label use hyphens, for example demo_tutorial becomes demo-tutorial.

List and inspect demos

ops/demo/demo-manager --host assela@demo-server.example.org list
ops/demo/demo-manager --host assela@demo-server.example.org status demo_tutorial

List includes only explicitly marked demo applications. Status reads current Dokku, PostgreSQL, storage, release, process, certificate, and ownership evidence. It reports inconsistent instead of guessing when evidence disagrees.

Use --json before the command name for line-delimited machine-readable events:

ops/demo/demo-manager --host assela@demo-server.example.org --json status demo_tutorial

Update a demo

ops/demo/demo-manager --host assela@demo-server.example.org update demo_tutorial \
  --release 89abcdef0123456789abcdef0123456789abcdef

Update requires a healthy, consistently marked demo and deploys one exact commit. It does not claim an automatic rollback. If update fails, inspect status, delete the demo, and recreate it.

Reset a demo

ops/demo/demo-manager --host assela@demo-server.example.org reset demo_tutorial

Reset means safe deletion followed by creation with the recorded release and dataset. Confirm the exact name and enter a new administrator password. A new environment identifier is created.

By default, the recreated administrator is demo_admin with an empty email address. To retain another administrator identity, pass --admin-username USERNAME --admin-email EMAIL to reset.

Delete a demo

ops/demo/demo-manager --host assela@demo-server.example.org delete demo_tutorial

Type the exact canonical name when prompted. For reviewed unattended deletion:

ops/demo/demo-manager --host assela@demo-server.example.org delete demo_tutorial --confirm

Deletion checks each resource's deterministic identity and ownership marker. Verified resources are removed best-effort and the anchor application is removed last. Missing resources are harmless. Unmarked or conflicting resources are retained and reported. Repeating delete is safe.

Recover from failure

The supported recovery for a disposable demo is delete and recreate:

ops/demo/demo-manager --host assela@demo-server.example.org status demo_tutorial
ops/demo/demo-manager --host assela@demo-server.example.org delete demo_tutorial --confirm
ops/demo/demo-manager --host assela@demo-server.example.org create demo_tutorial \
  --release APPROVED_COMMIT --seed tutorial \
  --admin-username demo_admin --admin-email demo-admin@example.org

If create reports deterministic application identity is already occupied, a previous attempt usually left a partial, marked demo with the same canonical name. Inspect and remove it with the manager:

ops/demo/demo-manager --host assela@demo-server.example.org status demo_tutorial
ops/demo/demo-manager --host assela@demo-server.example.org \
  delete demo_tutorial --confirm
ops/demo/demo-manager --host assela@demo-server.example.org \
  delete demo_tutorial --confirm

The second deletion verifies that all managed resources are absent. Then rerun create. Do not manually run dokku apps:destroy first: the manager uses the application's ownership marker to identify and safely remove its associated database, roles, and storage. If deletion reports retained_unverified, stop and investigate the reported ownership evidence instead of forcing removal.

If a cleanup attempt reports another lifecycle mutation is active, an earlier coordinator may still be finishing or releasing the global lock after a failed deployment. Wait briefly and retry. If the message persists, inspect the lock holder without changing it:

ssh assela@demo-server.example.org \
  sudo -n fuser -v /run/lock/lmstool-demo-manager.lock

Output identifying a process means a lifecycle operation is still alive. Do not delete the lock file or start a competing mutation. Allow the bounded operation to finish, or investigate that specific process before deciding how to stop it. No output and exit status 1 means no process currently holds the lock, so retry the lifecycle command. The lock file may remain on disk after release; that is normal because exclusion depends on the kernel-held open file descriptor, not on whether the path exists.

Do not manually remove markers to force deletion. If a resource is retained, compare the reported evidence with the deterministic identity and investigate before changing it.

Diagnostics

Progress identifies the active stage. Failures include a redacted diagnostic and recovery instruction. Protected server history is available to root:

ssh assela@demo-server.example.org \
  sudo -n \
  journalctl -t lmstool-demo-manager --since today

Do not enable Dokku trace mode during secret-bearing work. Never paste secret files, complete database URLs, or private keys into support reports.

When create has produced a partial demo and reports a PostgreSQL connection failure, test the exact generated credentials from the Docker bridge without printing them. Run this on the demo server:

sudo docker run --rm \
  --network bridge \
  --mount type=bind,src=/var/lib/lmstool/demo-storage/demo-tutorial/secrets,dst=/run/lmstool-demo-secrets,readonly \
  --entrypoint python \
  dokku/demo-tutorial:latest \
  -c "from pathlib import Path; import psycopg2; dsn=Path('/run/lmstool-demo-secrets/database_url').read_text().strip(); c=psycopg2.connect(dsn,connect_timeout=5); print('database authentication successful'); c.close()"

Replace the demo name in both paths and the image name. Success confirms the listener, firewall, HBA rule, and generated credentials. The command reads the URL only inside the disposable container; do not replace it with cat or place the URL in an argument or environment variable.