How do I trace a TypeScript AI agent running in a Kubernetes deployment?
Rius traces a TypeScript agent in a Kubernetes deployment when each container process calls init() once, awaits client.ready, and awaits client.shutdown() on a SIGTERM before it exits. The SDK installs no exit hook and no signal handlers in TypeScript, so that shutdown call is what exports your last spans. Each replica appears as its own instance under one agent name, and you configure everything through RIUS_* environment variables in the pod spec.
How it works
- One init per process. Each container runs one Node process, and each process calls
init()once, as early as possible. The global OpenTelemetry provider is write-once, so a second globalinit()logs a warning and returns the existing client. - Integrations attach asynchronously.
init()returns immediately. Awaitclient.readybefore your first model call so the instrumentation is in place. It never rejects. The Integrations page lists which libraries attach automatically. - Spans export in the background. Spans are queued in memory and sent in batches every 5 seconds by default (
OTEL_BSP_SCHEDULE_DELAY, in milliseconds). The queue holds 2048 spans (OTEL_BSP_MAX_QUEUE_SIZE), and when it is full new spans are dropped with a warning. - No exit hook in TypeScript. Nothing flushes for you when the process ends. A SIGTERM or SIGKILL with no handler loses the spans still in the queue and sends no final
stoppedping, so the platform shows that instance asgone. - Each process is an instance. Every process that calls
init()gets its own instance ID. Three replicas show as three instances of one agent, and each restart starts a fresh instance under the same name.
Set it up
-
Install the SDK and the integration packages you use, as covered in Installation and configuration. In your image build, for an OpenAI agent:
npm install @glassflow-ai/rius @opentelemetry/api npm install @arizeai/openinference-instrumentation-openai -
Initialize once at your entry point, wait for the integrations, and shut down on SIGTERM:
import { init } from '@glassflow-ai/rius' const client = init({ serviceName: 'my-agent', }) await client.ready process.on('SIGTERM', async () => { await client.shutdown() // drains queued spans and sends the final stopped ping process.exit(0) }) -
Put the settings in the container’s environment. Take the key from a Kubernetes Secret rather than the image. Both names under
envare the reader’s own Secret and image tag; theRIUS_*names are what the SDK reads:env: - name: RIUS_API_KEY valueFrom: secretKeyRef: name: rius-key key: api-key - name: RIUS_SERVICE_NAME value: my-agent - name: RIUS_SERVICE_VERSION value: "2026.10.26" - name: RIUS_HEARTBEAT_INTERVAL value: "15" -
Keep
RIUS_SERVICE_NAMEidentical across replicas and deploys, and put the release or image tag inRIUS_SERVICE_VERSION. Renaming an agent starts a new empty name, and the old name’s instances reportgone. -
Leave
RIUS_HEARTBEATon and keep the interval at 15 seconds or below. The platform marks an instancestaleafter 30 seconds andgoneafter 60 seconds without a ping. Heartbeats need an API key, so withoutRIUS_API_KEYthe agent has no instances. -
For a batch job that runs to completion, await
client.flush()before the process ends, orclient.shutdown()if you also want the clean-stop ping.flush()returnsfalseif the latest export failed, so check it. Never callflush()inside a request handler, because it blocks until the export completes. Details are in Reliability. -
Do not set
RIUS_SESSION_IDin a deployment that serves many users. It stamps one session on every span in the process. Use a session scope in code instead.
What you get
- Every model call, tool call and agent step from each pod, with cost and tokens, under one agent name.
- One instance per replica in the Agents view, with running, stale, gone or stopped status. A pod that shuts down through your SIGTERM handler reports a clean stop, and one killed without it reports
gone. - A version tag on every span, so you can tell which image produced a trace.
- Spans from the final seconds of a pod’s life, because the handler drains the queue before exit.
- Export failures that never reach your agent code. If billing blocks ingest (
402) or the workspace is over its plan’s ingest rate (429), spans are dropped rather than raising errors.
When something else fits
- If you run a short-lived function that is frozen between calls, set
RIUS_HEARTBEAT=falseand flush at the end of every invocation, as Reliability describes. - If your volume is very high, raise
OTEL_BSP_MAX_QUEUE_SIZEbefore loweringRIUS_SAMPLE_RATE.