Run it locally
Two different things run on one machine, and they are not the same product. Conflating them is what made the old instructions unfollowable, so this page keeps them apart. Pick one before you copy a command.
| Open source server | Customer-local bundle | |
|---|---|---|
| Source | PioneerSquareLabs/metergraph, Apache-2.0 | A signed release bundle from Metergraph |
| Start it with | docker compose up | ./bin/start |
| Serves on | http://localhost:8787 | http://127.0.0.1:8080 |
| Database | postgres:16 | postgres:17 |
| You get | The performance dashboard | Dashboard, alerts, and optimization analysis |
| Content | Stripped at ingest, always | Captured unless you turn it off |
| Account needed | None | A registry credential from Metergraph |
If you are still choosing between running it yourself and the hosted service, read Choosing a deployment first.
The open source server
Section titled “The open source server”Apache-2.0, no account, no key minting, and no egress. It is the genuinely one-command path: a FastAPI server with the React dashboard served by the same process, in front of Postgres 16.
It records and prices traffic. It does not run alerts or optimization analysis, and it never stores content: the ingest handler projects rows through a column allowlist, so prompts and completions cannot reach the database whatever the SDK is configured to send.
-
Start it
Section titled “Start it”git clone https://github.com/PioneerSquareLabs/metergraphcd metergraphMG_TOKENS=dev-token docker compose upThe compose file already defaults
MG_TOKENStodev-token, so a baredocker compose upworks too. Set the variable when you want a token of your own:MG_TOKENS=$(openssl rand -hex 16) docker compose up -dTokens are whatever you say they are.
MG_TOKENStakes a comma-separated list, so you can give each producer its own and rotate them independently. -
Point your app at it
Section titled “Point your app at it”export METERGRAPH_INGEST_URL=http://localhost:8787export METERGRAPH_APP_TOKEN=dev-tokenMoving between a self-hosted server and the hosted service later is
METERGRAPH_INGEST_URLin either direction, and nothing else. See Install the SDK. -
Open the dashboard
Section titled “Open the dashboard”http://localhost:8787, and enter the same token. The server runs its migrations on startup, so there is no separate schema step. -
Optional: seed some traffic
Section titled “Optional: seed some traffic”python -m pip install 'metergraph>=0.4,<1'MG_TOKEN=dev-token python scripts/seed_demo.pyThe demo wraps local OpenAI-, Anthropic- and Google-shaped clients with the published SDK, so it exercises the real session exchange and batched ingest without a provider key and without spending anything. Seeded calls land in the
demoenvironment, which you can deselect in the dashboard.
Configuring the open source server
Section titled “Configuring the open source server”Each of these is read from the environment by the server process.
| Variable | Default | What it does |
|---|---|---|
DATABASE_URL | postgresql://metergraph:metergraph@localhost:5432/metergraph | Postgres DSN. The compose file points it at the postgres service. |
MG_TOKENS | empty, which rejects every request | Comma-separated static bearer tokens |
MG_PRICES_PATH | the bundled prices.yaml | Mount a newer price catalog without rebuilding |
MG_REGION | global | Preferred price region, falling back to * then global |
MG_MAX_BODY_BYTES | 8388608 | Ingest body cap |
MG_MAX_ROWS | 5000 | Rows per ingest request |
MG_SESSION_TTL_SECONDS | 300, clamped to 60 through 3600 | Ingest session lifetime |
MG_DASHBOARD_DIST | /app/static in the image | Where the built dashboard lives |
Authentication is a static bearer token, and the dashboard keeps its token in browser local storage. Run the server inside a network boundary you trust, and treat dashboard access as trusted-network access.
The customer-local bundle
Section titled “The customer-local bundle”The full product on one machine: dashboard, alerts, and optimization analysis, with traces that never leave the machine. This is a signed release you receive from Metergraph, not a repository you clone. Six services come up together.
| Service | Role |
|---|---|
postgres | Your data, on the pgdata volume |
localstack | The S3 and SQS APIs the application calls, kept local |
bootstrap | One shot: migrates the schema, creates the admin, exits |
api | Dashboard and HTTP API on http://127.0.0.1:8080 |
worker | Ingestion and scheduled work |
local_runner | Runs analyses. The only service given a provider key |
Before you start
Section titled “Before you start”- Docker with Compose, on macOS or Linux. On Windows, run the bundle inside WSL with Docker’s WSL integration on.
cosign.bin/startverifies the release with it and refuses to run without it.- A pull-only registry credential for the private images, which comes with your invitation.
A model provider key is needed only to run an analysis. Sign-in, ingestion, the dashboard, the readiness check and the demo ingestion all work without one.
Bring it up
Section titled “Bring it up”Verify the bundle before you extract it. That procedure is on
Verifying a release, and it has to
happen first: bin/start ships inside the archive being checked, so it cannot
check itself.
-
Sign in to the image registry
Section titled “Sign in to the image registry”echo "$METERGRAPH_PULL_TOKEN" | docker login ghcr.io \-u "$METERGRAPH_PULL_USER" --password-stdin -
Create your settings file
Section titled “Create your settings file”cp .env.example .envThree values in
.envmust be set before the first boot.local/.env METERGRAPH_LOCAL_ADMIN_EMAIL=admin@example.comMETERGRAPH_LOCAL_ADMIN_PASSWORD=<choose a strong password>METERGRAPH_DB_PASSWORD=<choose a strong password>Compose needs two more variables,
METERGRAPH_APP_IMAGEandMETERGRAPH_RUNNER_IMAGE, and neither is yours to set.bin/startresolves both from the signed release manifest into a generated.images.env, and passes that file to Compose last so a stray value in.envcannot win. A third generated file,.runtime.env, holdsMETERGRAPH_RUNTIME_DB_PASSWORD: the least-privilege database login the API and worker use. It is machine-generated on first boot and reset by every migration, so it is never your input either. -
./bin/start
It verifies the manifest and both image digests with
cosign, resolves those digests, runs Compose, and waits until every service is healthy andbootstraphas exited zero. Then it prints a JSON readiness report, and on the first boot of a new database it sends one demo trace through the real ingestion API under environmentlocal-demo.If
bootstrapfails, the API, worker and runner never start, andbin/startexits non-zero naming the log to read. -
Sign in
Section titled “Sign in”Open
http://localhost:8080and use the admin email and password from.env.
Send your own traces
Section titled “Send your own traces”Create an ingest key under Keys in the dashboard, then point an application at the machine:
export METERGRAPH_INGEST_URL=http://localhost:8080export METERGRAPH_APP_TOKEN=mg_...To load traffic you already record elsewhere, the same two variables drive Importing existing traffic.
Running an analysis
Section titled “Running an analysis”An analysis makes two kinds of model call, and they are configured separately.
Only local_runner ever receives a key.
Its own calls, which classify workloads, build the evaluations and judge
the results, go out on the channel you set in .env:
METERGRAPH_ANALYSIS_CONTROL_CHANNEL | Key it reads |
|---|---|
vercel-ai-gateway (default) | AI_GATEWAY_API_KEY |
anthropic-api | ANTHROPIC_API_KEY |
openai-api | OPENAI_API_KEY |
Until that channel’s key is set, an analysis request is refused up front with a message naming it. No gateway account is required: set the channel to whichever provider you already have.
The candidate calls, which try your workload on other models, use every key
you have set. A gateway key compares the widest field, FIREWORKS_API_KEY adds
the models Fireworks publishes, and a direct provider key compares that
provider’s own models. GOOGLE_API_KEY routes no candidate on its own and is
read only by search-enabled workloads.
Automatic weekly analysis is off by default. Turn it on once a manual analysis
has succeeded, with METERGRAPH_AUTOMATIC_ANALYSIS_ENABLED=true in .env and
another ./bin/start.
Day to day
Section titled “Day to day”./bin/status prints the readiness report on demand. ./bin/verify runs it and
then sends a demo trace. Both are content-free and work with no provider key.
Use stop and start between sessions rather than down. Your database
survives down, but the raw ingested payloads held in local object storage do
not.
docker compose --env-file .env --env-file .runtime.env --env-file .images.env \ -f docker-compose.yml stopBackups, upgrades, workspace and key management, and the tenant purge are on Operations.