---
title: "Production: API apps · coding.sh docs"
description: "Run coding.sh serve on SQLite or Postgres and send traces from API apps with the SDKs or OpenTelemetry."
canonical: https://coding.sh/docs/production
human_url: https://coding.sh/docs/production
markdown_url: https://coding.sh/docs/production.md
agents_url: https://coding.sh/agents.md
llms_url: https://coding.sh/llms.txt
updated: 2026-11-11
---

# Production: API apps

Run coding.sh serve on SQLite or Postgres and send traces from API apps with the SDKs or OpenTelemetry.

Run one `coding.sh serve` process, point it at a database, and send traces from your API apps: OpenAI, Anthropic, the AI SDK, an LLM gateway such as Vercel AI Gateway or OpenRouter, or any OpenTelemetry app. The viewer, CLI, MCP tools, datasets and experiments work on the same database.

For a team's coding agents on one machine, start with [Self-host for a team](https://coding.sh/docs/how-to/self-host-team): one `docker run`, no keys to make.

## Run serve (built)

Make one ingest key per app and one read key for people. Each key is printed once.

shell

```
coding.sh serve keygen --project myapp          # csh_ik_… for the app
coding.sh serve keygen --project myapp --read   # csh_rk_… for people
```

Then start serve with a store URL. It creates its tables on first start. With a volume on `/data` and no keys set, the first start makes a key pair there and prints it instead.

shell

```
docker run -d -p 4318:4318 \
  -e CODING_SH_STORE_URL='postgres://user:pass@db.example.com:5432/coding?sslmode=require' \
  -e CODING_SH_INGEST_KEYS='myapp:csh_ik_…' \
  -e CODING_SH_READ_KEYS='*:csh_rk_…' \
  ghcr.io/jackulau/coding.sh
curl -s http://127.0.0.1:4318/readyz
```

## Pick a store

| Setup | `CODING_SH_STORE_URL` | Blobs |
| --- | --- | --- |
| One box | `sqlite:///data/index.db` | `file:///data/blobs` |
| Real traffic | `postgres://…` | same database |
| Large payloads | `postgres://…` | `s3://bucket/blobs?region=…` (S3, R2, MinIO) |

Postgres and S3 come from plugin packages, so the core stays dependency-free. The Docker image includes both.

## Send traces

Node, with `@codingdotsh/sdk`:

ts

```
import OpenAI from "openai";
import { observe } from "@codingdotsh/sdk";

// CODING_SH_URL=https://obs.example.com  CODING_SH_INGEST_KEY=csh_ik_…
const openai = observe(new OpenAI()); // or new Anthropic()
```

Python: `coding_sh.observe(client)`. Any OpenTelemetry SDK, a Vercel AI Gateway trace drain, or a gateway's own OpenTelemetry export (LiteLLM, Cloudflare, Portkey):

shell

```
OTEL_EXPORTER_OTLP_ENDPOINT=https://obs.example.com
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20csh_ik_…"
```

The key decides the project. Group a conversation with `sessionId` or `gen_ai.conversation.id`.

## Use it

Open `https://obs.example.com/?t=csh_rk_…` for the viewer. The CLI and MCP server read the same store:

shell

```
coding.sh sessions --store postgres://… --project myapp
coding.sh search "timeout" --store postgres://…
```

## Operate it

- `/healthz` is up, `/readyz` is ready to write, `/metrics` is Prometheus.
- One writer replica. Add read-only replicas with `--role viewer`.
- A full queue answers 429 with `Retry-After`. Retries never duplicate.
- Secrets are redacted before writing. `CODING_SH_CAPTURE_MODE=metadata` drops message text.
- Recipes for Compose, Fly, Railway and Render are in [docs/production](https://github.com/jackulau/coding.sh/tree/main/docs/production).

Packages: `@codingdotsh/sdk` and the store plugins are on npm, and the image `ghcr.io/jackulau/coding.sh` is published for each release from 0.1.2 on. The Python SDK, `coding-sh`, is not on PyPI yet: install it from a checkout with `pip install -e packages/sdk-python`.

To set coding.sh up for a user, follow the agent workflow at [/agents.md](https://coding.sh/agents.md).
