---
title: "Deploy Latitude + Mailpit"
description: "LLM and AI agent observability: OTLP traces, evals, MCP. Zero-setup login"
category: "Observability"
url: https://railway.com/deploy/latitude-mailpit
---

# Deploy Latitude + Mailpit

LLM and AI agent observability: OTLP traces, evals, MCP. Zero-setup login

**[Deploy Latitude + Mailpit on Railway](https://railway.com/template/latitude-mailpit)**

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

- **Creator:** Auromations
- **Category:** Observability
- **Total deploys:** 1

## Template content

### Postgres

- **Image:** pgvector/pgvector:0.8.6-pg16
- **Start command:** `/usr/local/bin/docker-entrypoint.sh postgres -c wal_level=logical`

### Ingest

- **Image:** latitudedata/ingest:0.3.118
- **Health check:** /health
- **Public domain:** Yes

### Mailpit

- **Image:** axllent/mailpit:v1.31.2
- **Health check:** /livez
- **Public domain:** Yes

### Web https://latitude.so/latitude-assets/l5c1DNVxQ3iAvTDihvg9pFw2l2k-orig.png

- **Image:** latitudedata/web:0.3.118
- **Health check:** /api/health
- **Public domain:** Yes

### API

- **Image:** latitudedata/api:0.3.118
- **Health check:** /health
- **Public domain:** Yes

### Redis

- **Image:** redis:7.4.11
- **Start command:** `/bin/sh -c "rm -rf /data/lost+found && exec docker-entrypoint.sh redis-server --maxmemory-policy noeviction --appendonly yes"`

### Temporal

- **Image:** temporalio/auto-setup:1.27.2

### ClickHouse

- **Image:** clickhouse/clickhouse-server:26.2.19.43

### Workflows

- **Image:** latitudedata/workflows:0.3.118
- **Health check:** /health

### Migrations

- **Image:** latitudedata/migrations:0.3.118
- **Start command:** `/bin/sh -c "cd /app/packages/platform/db-postgres && node -e 'const {Client}=require(`pg`);const c=new Client({connectionString:process.env.LAT_ADMIN_DATABASE_URL});(async()=>{await c.connect();await c.query(`DO $$BEGIN CREATE ROLE latitude_app LOGIN; EXCEPTION WHEN duplicate_object THEN NULL; END$$`);await c.query(`ALTER ROLE latitude_app WITH LOGIN PASSWORD ${c.escapeLiteral(process.env.POSTGRES_RUNTIME_PASSWORD)}`);await c.query(`GRANT CONNECT ON DATABASE ${c.escapeIdentifier(c.database)} TO latitude_app`);await c.end();console.log(`latitude_app role ready`)})().catch(e=>{console.error(e);process.exit(1)})' && pnpm pg:migrate && pnpm --filter @platform/db-clickhouse ch:up"`

### Workers

- **Image:** latitudedata/workers:0.3.118
- **Health check:** /health

## Buckets

- **latitude-storage**

## Documentation

# Deploy and Host Latitude on Railway

Latitude is open-source observability for AI agents. Your apps send OpenTelemetry traces, and Latitude turns them into searchable sessions with token and cost accounting, flags where agents fail, runs evaluations and monitors, and exposes all of it over a REST API and an MCP server.

## About Hosting Latitude

Latitude is not one container. This template runs the full production stack from upstream's `docker-stack.yml` on the pinned 0.3.118 release: the web app, the public API and MCP server, the OTLP ingest endpoint, background workers, Temporal workflow workers and a one-shot migrations job. Behind them sit Postgres with pgvector, ClickHouse for span analytics, Redis for queues and caching, Temporal, and a Railway Storage Bucket for trace payloads.

Latitude signs you in with a magic link sent by email, so an instance with no mail setup can't be logged into. This template bundles a private Mailpit inbox so you can sign in right away. Add SMTP or Mailgun whenever you want real email delivery.

## Common Use Cases

- Trace LLM calls and multi-step agents from any OpenTelemetry-instrumented app, with prompts, completions, tokens and cost per session
- Find failing agent behavior (loops, refusals, jailbreaks, empty replies) with built-in flaggers, then track it as issues and monitors
- Keep sensitive prompt and user data inside your own infrastructure instead of a hosted observability SaaS
- Give coding agents (Claude Code, Cursor, Codex) access to production traces through Latitude's MCP server

## Dependencies for Latitude Hosting

- Postgres 16 with pgvector (bundled)
- ClickHouse 26.2 (bundled)
- Redis 7.4 (bundled)
- Temporal 1.27 (bundled)
- S3-compatible object storage (bundled Railway Storage Bucket)
- Email delivery: bundled Mailpit by default, optionally your own SMTP server or Mailgun
- Optional: an LLM provider key (OpenAI, Anthropic, Google, Amazon Bedrock or any OpenAI-compatible endpoint) for the AI features

### Deployment Dependencies

- Latitude source: https://github.com/latitude-dev/latitude-llm
- Self-hosting docs: https://docs.latitude.so/deployment/single-host
- Configuration reference: https://docs.latitude.so/deployment/configuration

### Implementation Details

**First boot.** The deploy asks you nothing. Every password and key is generated for your project. First boot takes two to three minutes. The Migrations service creates the database role, runs the Postgres and ClickHouse migrations, then exits. It shows as completed, not crashed, and that is expected. The app services retry on their own until the schema exists.

**Signing in the first time.**

1. Open the **Web** service's URL and enter your email under "Continue with email".
2. Open the **Mailpit** service's URL. Log in as `admin` with the `MAILPIT_PASSWORD` value from the Mailpit service's Variables tab.
3. Open the "Continue to Latitude" email and click the link. Then set your name and organization.

The Google and GitHub buttons on the login page only work after you add OAuth credentials (see below).

**Sending your first trace.** In Latitude, open Settings, then Keys, and copy the API key. Any OpenTelemetry exporter works. Point it at your Ingest service:

```
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https:///v1/traces
OTEL_EXPORTER_OTLP_TRACES_HEADERS=Authorization=Bearer ,X-Latitude-Project=
```

The project slug is in the project URL (`/projects/`). Traces show up under Sessions within a few seconds.

**Where settings live.** Every app setting is defined once, on the **Web** service. API, Ingest, Workers, Workflows and Migrations reference Web's values. To change something, add or edit the variable on Web, then redeploy Web, API, Ingest, Workers and Workflows so they all pick it up.

**Real email (recommended once you invite teammates).** Mailpit keeps every email inside your project and never delivers it. To send real email, add these variables to Web:

- SMTP: `LAT_SMTP_HOST`, `LAT_SMTP_PORT` (587 or 465), `LAT_SMTP_USER`, `LAT_SMTP_PASS`, `LAT_SMTP_FROM`
- or Mailgun: `LAT_MAILGUN_API_KEY`, `LAT_MAILGUN_DOMAIN`, `LAT_MAILGUN_FROM`

Mailgun takes precedence over SMTP, and either one takes precedence over Mailpit. After that you can delete the Mailpit service.

**AI features.** Tracing and trace search work without any keys. Flaggers, evaluations, summaries and semantic search need a provider. For example, set `LAT_OPENAI_API_KEY`, `LAT_AI_GENERATION_PROVIDER=openai` and `LAT_AI_GENERATION_MODEL` on Web. For embeddings, set `LAT_VOYAGE_API_KEY`, or `LAT_AI_EMBEDDING_PROVIDER` plus `LAT_AI_EMBEDDING_MODEL`. The upstream configuration reference lists per-feature overrides.

**Sign in with Google or GitHub.** Add `LAT_GOOGLE_CLIENT_ID` and `LAT_GOOGLE_CLIENT_SECRET`, or `LAT_GITHUB_CLIENT_ID` and `LAT_GITHUB_CLIENT_SECRET`, to Web.

**MCP.** MCP clients connect to `https:///v1/mcp` and sign in with OAuth against your Web domain. Discovery is already wired to your own domains.

**Custom domains.** If you add custom domains, update `LAT_WEB_URL`, `LAT_API_URL`, `LAT_INGEST_URL`, `LAT_TRUSTED_ORIGINS` and `LAT_CORS_ALLOWED_ORIGINS` on Web to match, then redeploy the app services. If the web origin is missing from the origin lists, sign-in fails.

**Resources.** Expect roughly 3.5 GB of memory across the stack at idle. ClickHouse, Workers and Workflows are the largest.

**Upgrading.** Change the image tag (for example `0.3.118`) to the same new version on Web, API, Ingest, Workers, Workflows and Migrations. The Migrations service applies new migrations when it redeploys.

**What differs from upstream's compose file.** The cache and queue share one Redis with no eviction and append-only persistence. The cache client doesn't support a password, so Redis has no public endpoint and is only reachable inside your project. A Railway Storage Bucket replaces SeaweedFS. Temporal UI is not included.

**Troubleshooting.**

- *No email arrives.* With no SMTP or Mailgun set, mail never leaves the project. Look in the Mailpit inbox.
- *An app service logs database authentication errors right after the first deploy.* It booted before Migrations created its role. It recovers on its own within a minute.
- *API logs "Invalid address for Temporal gRPC endpoint".* Redeploy the API service once. That makes Railway re-resolve its variables.
- *Sign-in bounces back to the login page after a domain change.* The origin lists on Web don't include the new web URL.

## Why Deploy Latitude 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 Latitude 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/latitude-mailpit
