Skip to content

Run it locally

Choose a deployment before running a command. The two options use different images, ports, databases, and feature sets.

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 Apache-2.0 server needs no account, key minting, or outbound network access. It serves the React dashboard and FastAPI API from one process, backed by 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.

  1. git clone https://github.com/PioneerSquareLabs/metergraph
    cd metergraph
    MG_TOKENS=dev-token docker compose up

    The compose file already defaults MG_TOKENS to dev-token, so a bare docker compose up works too. Set the variable when you want a token of your own:

    MG_TOKENS=$(openssl rand -hex 16) docker compose up -d

    Tokens are whatever you say they are. MG_TOKENS takes a comma-separated list, so you can give each producer its own and rotate them independently.

  2. export METERGRAPH_INGEST_URL=http://localhost:8787
    export METERGRAPH_APP_TOKEN=dev-token

    Moving between a self-hosted server and the hosted service later is METERGRAPH_INGEST_URL in either direction, and nothing else. See Install the SDK.

  3. http://localhost:8787, and enter the same token. The server runs its migrations on startup, so there is no separate schema step.

  4. python -m pip install 'metergraph>=0.4,<1'
    MG_TOKEN=dev-token python scripts/seed_demo.py

    The 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 demo environment, which you can deselect in the dashboard.

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 full product on one machine: dashboard, alerts, and optimization analysis, with traces that never leave the machine. This is a signed release you download from the releases page, not a repository you clone. Each release has its own page there with the download, verification and start commands for that version. The files are public: no credential is needed to download them, only to pull the images. Eight services come up together.

Service Role
postgres Your data, on the pgdata volume
localstack The S3 and SQS APIs the application calls, kept local
clickhouse The analytics store: a copy of call metadata, no prompt or response content, rebuilt from postgres if lost
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
detector-worker Detector scans on a separate queue and worker process
local_runner Runs analyses. The only service given a provider key
  • Docker with Compose, on macOS or Linux. On Windows, run the bundle inside WSL with Docker’s WSL integration on.
  • cosign. bin/start verifies the release with it and refuses to run without it.
  • A pull-only registry credential for the private images, which comes with your invitation.

Automatic workload classification is enabled by default once a workspace has 10 usable traces, so a model provider key is needed for classification as well as manually started analyses. Sign-in, ingestion, the dashboard, the readiness check and demo ingestion all work without one.

Verify the bundle before extracting it. Follow Verifying a release: bin/start ships inside the archive and cannot verify itself.

  1. echo "$METERGRAPH_PULL_TOKEN" | docker login ghcr.io \
    -u "$METERGRAPH_PULL_USER" --password-stdin
  2. cp .env.example .env

    Three values in .env must be set before the first boot.

    local/.env
    METERGRAPH_LOCAL_ADMIN_EMAIL=admin@example.com
    METERGRAPH_LOCAL_ADMIN_PASSWORD=<choose a strong password>
    METERGRAPH_DB_PASSWORD=<choose a strong password>

    The Settings page hides the local setup card automatically because this bundle runs with the local deployment profile. No additional setting is needed.

    Compose needs two more variables, METERGRAPH_APP_IMAGE and METERGRAPH_RUNNER_IMAGE, and neither is yours to set. bin/start resolves both from the signed release manifest into a generated .images.env, and passes that file to Compose last so a stray value in .env cannot win. A third generated file, .runtime.env, holds METERGRAPH_RUNTIME_DB_PASSWORD: the least-privilege database login the API, workers and detector worker use. It is machine-generated on first boot and reset by every migration, so it is never your input either.

  3. ./bin/start

    It first runs ./bin/doctor, a preflight that checks Docker, Compose, cosign, curl, Python, free disk space, port 8080, the required .env settings, and which provider keys are set. It prints names only, never values, and stops before Compose starts if a check fails.

    It then verifies the manifest and both image digests with cosign, resolves those digests, checks that the registry serves both images, runs Compose, and waits until every service is healthy and bootstrap has 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 environment local-demo.

    If bootstrap fails, the API, workers and runner never start, and bin/start exits non-zero naming the log to read. On success it prints Metergraph is ready with the dashboard URL and the SSH tunnel command.

  4. Open http://localhost:8080/ and use the admin email and password from .env.

Create an ingest key under Keys in the dashboard, then point an application at the machine:

export METERGRAPH_INGEST_URL=http://localhost:8080
export METERGRAPH_APP_TOKEN=mg_...

To load traffic you already record elsewhere, the same two variables drive Importing existing traffic.

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 and runs separately from automatic workload classification. Turn it on once a manual analysis has succeeded, with METERGRAPH_AUTOMATIC_ANALYSIS_ENABLED=true in .env and another ./bin/start.

Automatic workload classification is enabled by default in the customer-local bundle. The worker checks hourly and starts a classification-only run after the workspace reaches 10 distinct usable traces, then waits until 24 hours after the last successful classification before refreshing. A usable trace contains captured, non-truncated request content and at least one non-soak call. The classification is saved as a reusable checkpoint and does not create an analysis report. Set METERGRAPH_AUTOMATIC_CLASSIFICATION_MIN_TRACES in .env to a value of 10 or higher to tune the threshold, or set METERGRAPH_AUTOMATIC_CLASSIFICATION_ENABLED=false to pause automatic classification. Apply .env changes with ./bin/start. The configured analysis control-channel provider key is required for classification calls.

./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 stop

Backups, upgrades, workspace and key management, and the tenant purge are on Operations.