Documentation menu
These are pre-release docs from the main branch.

How to run the local demo stack

Audience: admin — you run Marula on your own machine to show it to someone, and you want one command between a clone and a working demo.

scripts/demo-up.sh takes a fresh clone to a booted, seeded demo. Postgres and Valkey run in Docker. core-api, the worker, and the web console run natively from the repo. Nothing is deployed and nothing leaves your machine.

What you need first

  • Docker, running. Postgres and Valkey are containers, and the app itself is not.
  • The Rust toolchain (cargo) and pnpm.
  • About 10 GB of free disk. The script checks and refuses to build below that.

Run it

scripts/demo-up.sh

The script preflights the tools, starts Postgres on 15432 and Valkey on 16379 (deploy/docker-compose.yml, the ports the local stack reference pins), applies the schema, seeds the demo org, builds and boots the three services, then prints the logins. The first run compiles the Rust workspace and the web app, so expect minutes. After that, re-runs take seconds.

Run it twice and the second run is a no-op that reports what is already up. The demo org is seeded once, so a second up never wipes changes you made during a demo.

What you get

ServiceURLNotes
web consolehttp://localhost:3000dev server, or --web prod for a build
core-apihttp://localhost:8080the single write surface, spec at /openapi.json; also serves the SSE live stream
workerhttp://127.0.0.1:8082/healthzbackground loops

Four logins, password demo-password-123:

  • owner@demo.local — full admin
  • dispatcher@demo.local — board and scheduling
  • tech@demo.local — field work
  • bookkeeper@demo.local — billing

The seeded org is deterministic. The tickets, the invoices, and the assets are the same every run, so a demo you rehearsed is the demo you give.

Before a prospect is in the room

Two pages to open first:

No key handy? Add --ai to the boot and the script starts the local Ollama slot alongside the stack:

scripts/demo-up.sh --ai

Ollama comes up on 127.0.0.1:11434, loopback only. The shipped compose file publishes nothing, on purpose. Pull the transcription model that matches its contract, then point the AI settings at http://127.0.0.1:11434:

docker compose -f deploy/docker-compose.yml --profile ai exec ollama ollama pull whisper

Embeddings need no pull: the profile's one-shot ollama-model job fetches EmbeddingGemma 2, the bundled default. The transcription pull is a few gigabytes, so the script prints the command instead of running it. The first inference after a pull is slow while the model loads, and every call after that is fast.

Reset and stop

scripts/demo-up.sh reset          # reseed the demo org, keep the stack
scripts/demo-up.sh reset --fresh  # destroy the database volume, rebuild, reseed
scripts/demo-up.sh stop           # stop the three app processes
scripts/demo-up.sh stop --infra   # ... and the Postgres/Valkey containers

reset reseeds from scratch and clears the volatile state: auth lockouts, onboarding progress, and tour dismissals. The next login is the first login. --fresh also destroys the database volume, and it counts down five seconds first so a stray Enter does not cost you the data.

Logs for every service land in .demo-up/logs/ inside the repo. Every failure the script can catch ahead of time stops the run with a message that says what to fix: Docker down, a port held by something else, low disk, a stray non-hex JWT_SECRET. A failure mid-boot leaves the half-up stack running with its logs, and stop clears the processes this script started. It never kills a listener it does not recognize.

The desktop alternative: no Docker, double-clickable

A second way to run a demo needs no Docker at all. The desktop test app (ADR 0005) bundles an embedded Postgres, core-api, and the web console into one application you launch by double-clicking. Same demo org, same four logins.

Choose it when:

  • Docker is not on the machine. The app carries its own Postgres, so the only prerequisites are the repo, node, and brew install pgvector.
  • You are handing the demo to someone else. A double-clickable artifact beats "clone the repo and run this script" when the person on the other end is a prospect, not an engineer.

Build it with scripts/build-desktop.sh; the vendored rebuild-local-test-app skill documents the rebuild loop. The first build on a machine takes roughly three minutes, because it downloads the Postgres binaries and compiles the app. Every rebuild after that is dominated by the web build. The app's data lives in ~/Library/Application Support/Meridian/ and survives rebuilds, migrations included. Delete that directory to start from a blank machine.

Where it gives ground, so you can choose honestly: the desktop app runs a local-mode config with external integrations off, and its port space is not the one demo-up.sh uses. Anything involving real email, Stripe, or shared testing is compose territory, so run scripts/demo-up.sh or the full compose stack for that.

If you outgrow the machine

The same demo runs as the full container stack with docker compose -f deploy/docker-compose.yml up --build, and on a server behind TLS. When you get there, how to upgrade covers the release path and watching your self-hosted services covers the health pane.

Documentation © 2026 Marula, licensed CC BY 4.0. Marula is a trademark. View source

For admin — you run Marula on your own machine to show it to.