---
title: "Deploy Laminar | Open-Source Observability & Evals for AI Agents"
description: "Tracing, evals and SQL over agent traces, on ClickHouse, self-hosted"
category: "Observability"
url: https://railway.com/deploy/laminar-observability
---

# Deploy Laminar | Open-Source Observability & Evals for AI Agents

Tracing, evals and SQL over agent traces, on ClickHouse, self-hosted

**[Deploy Laminar | Open-Source Observability & Evals for AI Agents on Railway](https://railway.com/template/laminar-observability)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/laminar-observability/manifest.json

- **Creator:** Two Workspaces
- **Category:** Observability
- **Total deploys:** 1

## Template content

### Frontend https://raw.githubusercontent.com/nomideusz/laminar-railway/main/icon.svg

- **Image:** ghcr.io/lmnr-ai/frontend:v0.3.0
- **Health check:** /sign-in

### AppServer https://raw.githubusercontent.com/nomideusz/laminar-railway/main/icon.svg

- **Image:** ghcr.io/lmnr-ai/app-server:v0.3.0
- **Health check:** /health

### Postgres https://devicons.railway.app/i/postgresql.svg

- **Image:** postgres:16.15

### ClickHouse https://devicons.railway.app/i/clickhouse.svg

- **Image:** clickhouse/clickhouse-server:26.5.7.64
- **Start command:** `/bin/sh -c 'printf "<clickhouse><profiles><default><date_time_input_format>best_effort</date_time_input_format></default></profiles></clickhouse>" > /etc/clickhouse-server/users.d/laminar.xml && R=; for t in trace_log text_log part_log metric_log asynchronous_metric_log background_schedule_pool_log query_metric_log query_thread_log query_views_log session_log crash_log opentelemetry_span_log zookeeper_log blob_storage_log processors_profile_log asynchronous_insert_log error_log latency_log; do R="$R<$t remove=\"1\"/>"; done && printf "<clickhouse><logger><level>warning</level><console>1</console><size>10M</size><count>1</count></logger>%s<query_log><ttl>event_date + INTERVAL 3 DAY DELETE</ttl></query_log></clickhouse>" "$R" > /etc/clickhouse-server/config.d/laminar.xml && exec /entrypoint.sh'`

### Laminar https://raw.githubusercontent.com/nomideusz/laminar-railway/main/icon.svg

- **Image:** caddy:2.11.7-alpine
- **Start command:** `/bin/sh -c 'HASH=$(caddy hash-password --plaintext "$LAMINAR_PASSWORD") && printf ":%s {\n handle /healthz {\n  respond 200\n }\n handle /v1/* {\n  reverse_proxy %s:8000\n }\n handle {\n  basic_auth {\n   %s %s\n  }\n  reverse_proxy %s:5667 {\n   header_up -Authorization\n  }\n }\n}\n" "$PORT" "$APP_SERVER_HOST" "$LAMINAR_USERNAME" "$HASH" "$FRONTEND_HOST" > /etc/caddy/Caddyfile && exec caddy run --config /etc/caddy/Caddyfile --adapter caddyfile'`
- **Health check:** /healthz
- **Public domain:** Yes

## Documentation

# Deploy and Host Laminar on Railway

[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/new/template/laminar-observability?referralCode=sLjDXb&amp;utm_medium=integration&amp;utm_source=button&amp;utm_campaign=laminar-observability)

[Laminar](https://laminar.sh/) is the open-source observability and evals platform for AI agents: OpenTelemetry-native tracing of every LLM call, tool call and agent step, evaluations, datasets, labeling queues and a SQL editor over all of your traces. This template runs Laminar's own app server and dashboard with ClickHouse and Postgres, behind one public URL.

## About Hosting Laminar

The stack is five services: Laminar (the public gateway), Frontend, AppServer, ClickHouse and Postgres.

- **Upstream's own images, pinned.** The dashboard and the Rust app server run from the official `ghcr.io/lmnr-ai` images at release v0.3.0, with ClickHouse 26.5 and Postgres 16, as in upstream's self-host compose. Nothing moves until you bump a tag.
- **One URL for the dashboard and the SDKs.** The Laminar service is a small Caddy gateway. It sends `/v1/*` (SDK and OpenTelemetry ingestion, authenticated with project API keys) to the app server and everything else to the dashboard. The app server itself has no public address: its internal dashboard API has no authentication of its own, so it stays on the private network.
- **A login in front of the dashboard.** Self-hosted Laminar signs anyone in with just an email address, no password. The gateway puts a password prompt in front of the dashboard, with a password generated for your deploy. Only SDK traffic with a valid project API key gets past it without one.
- **Migrations on every boot.** The dashboard runs the Postgres and ClickHouse migrations when it starts, and the app server waits for it. Upgrading is bumping the image tags.
- **Every secret generated.** Postgres and ClickHouse passwords, the session secret and the 32-byte key that encrypts stored LLM provider keys are generated per deploy. None of upstream's sample values from its `.env` file are used.
- **A quiet ClickHouse.** Upstream's ClickHouse settings are applied at start, the internal metric tables that write every few seconds are off, and logs go to Railway's log view instead of gigabytes of files on the container disk.

## Common Use Cases

- Tracing agents and LLM apps built with OpenAI, Anthropic, Gemini, LangChain, the Vercel AI SDK, browser agents or any OpenTelemetry exporter
- Filtering traces by status, latency, cost or tags, then replaying and debugging the failing step
- Running evaluations on datasets and tracking scores across prompt and model changes
- Ad-hoc analysis of cost, latency and token usage with SQL over the raw spans

## Dependencies for Laminar Hosting

- ClickHouse 26.5 for spans and analytics (included, private network only)
- Postgres 16 for users, projects and settings (included, private network only)
- Caddy 2 as the public gateway (included)

### Deployment Dependencies

- [Laminar self-hosting documentation](https://laminar.sh/docs/hosting-options)
- [Laminar on GitHub](https://github.com/lmnr-ai/lmnr)
- [Template source on GitHub](https://github.com/nomideusz/laminar-railway)

### Implementation Details

**First boot takes about five minutes.** The dashboard applies more than 70 ClickHouse migrations before it goes healthy, and the app server starts after it. Later boots take seconds.

**Sign in.** Open the Laminar service's Railway domain. The browser asks for a username and password first: `admin` and the generated `LAMINAR_PASSWORD`, both in the Laminar service's Variables tab. Then Laminar's own sign-in page asks only for an email address. Everyone who knows the gateway password can sign in as any email address, so share it only with people you'd give the whole instance to. Each email gets its own account and workspaces; invite teammates to a workspace from its settings.

**Send traces.** Create a project, copy a project API key from its settings, and point the SDK at your Railway URL over HTTPS. Railway's public domain carries HTTP only, so use the SDK's HTTP exporter:

```python
from lmnr import Laminar
Laminar.initialize(
    project_api_key="",
    base_url="https://.up.railway.app",
    http_port=443,
    force_http=True,
)
```

```ts
import { Laminar } from "@lmnr-ai/lmnr";
Laminar.initialize({
  projectApiKey: "",
  baseUrl: "https://.up.railway.app",
  httpPort: 443,
  forceHttp: true,
});
```

Any other OpenTelemetry exporter can send OTLP/HTTP (protobuf or JSON) to `https://.up.railway.app/v1/traces` with the header `Authorization: Bearer `.

**gRPC.** The app server's OTLP/gRPC port (8001) is only on the private network, where other services in the same Railway project can reach it on AppServer's private domain. It is not exposed publicly: Railway's domains carry HTTP only, and a TCP proxy would carry gRPC without TLS. HTTP ingestion covers both SDKs.

**Custom domain.** Add the domain to the Laminar service, then set `NEXTAUTH_URL`, `NEXT_PUBLIC_URL` and `PUBLIC_API_BASE_URL` on Frontend to `https://your-domain`.

**AI features (optional).** Chat with a trace and SQL with AI need an LLM: set `LLM_PROVIDER` (for example `openai`) and `LLM_API_KEY` on Frontend. LLM provider keys for other features can be added per workspace in the dashboard; they are encrypted with `AEAD_SECRET_KEY`, so never change that key after the first boot.

**What the gateway doesn't open.** Public trace share links and the `lmnr` CLI's browser login go through the dashboard, so they also hit the password prompt. Email, Slack and RabbitMQ are left out, as in upstream's lightweight compose. Upstream's anonymous usage telemetry is on by default; set `LAMINAR_TELEMETRY_DISABLED=true` on Frontend to turn it off.

**Full-text search is off.** Upstream's compose includes Quickwit for searching span inputs and outputs, but the v0.3.0 dashboard image doesn't ship the index definitions it needs to create the search index, so search stays empty even there. This template leaves Quickwit out (`QUICKWIT_ENABLED=false`) and saves the memory. Filters, SQL and everything else work. It will come back once upstream's image creates the index.

**Resources.** Measured on Railway after first boot and some use, the idle stack sits at about 1.8 GB of RAM: ClickHouse about 1.1 GB, the dashboard about 300 MB, the app server about 210 MB, Postgres about 150 MB and the gateway under 20 MB. That needs the Hobby plan or higher. Back up the ClickHouse and Postgres volumes; ClickHouse holds every span.

## Why Deploy Laminar on Railway?

Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.

By deploying Laminar on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.


## Similar templates

- [Rootprint](https://railway.com/deploy/rootprint-1) — Open-source logs and traces with full-text search on object-storage.
- [Pyroscope profiling](https://railway.com/deploy/pyroscope-profiling) — Protected continuous profiling with durable Pyroscope storage.
- [SigOnly](https://railway.com/deploy/sigonly) — Deploy SigNoz with a working demo app & config in one click

Open this page in a browser: https://railway.com/deploy/laminar-observability
