# Node, Bun and Deno servers

> Run a pipedog voice agent as a long-lived server process, with SmallWebRTC or WebSocket sessions.

Source: https://polvallverdu.github.io/pipedog/docs/deployment/servers/



A server process is the most flexible host: it runs both transports, local turn detection models,
and any framework. The [voice bot guide](https://polvallverdu.github.io/pipedog/docs/voice-bot.md) builds one with SmallWebRTC, and
[Frameworks](https://polvallverdu.github.io/pipedog/docs/frameworks.md) mounts the same endpoints in SvelteKit, Hono, Next.js and others.

## The process [#the-process]

Each session runs in the same process, as its own pipeline and `PipelineWorker`. Start one per
connection, from the endpoint handler, and end it when the client disconnects:

```ts
transport.eventHandler("on_client_disconnected", async () => {
  await runner.cancel();
});
```

The shared agent in
[`examples/framework-snippets/shared/agent.ts`](https://github.com/polvallverdu/pipedog/blob/main/examples/framework-snippets/shared/agent.ts)
does exactly this. Pass `handleSigint: false` and `handleSigterm: false` to each session's
`WorkerRunner`, so one session doesn't take the process's signals; handle shutdown in the server.

## Runtimes [#runtimes]

* **Bun** runs the agent and upgrades WebSockets in `Bun.serve`: pass the server Bun gives `fetch`
  to `websocketFetchHandler`'s handler and serve with `bunWebSocket`. See [Bun](https://polvallverdu.github.io/pipedog/docs/frameworks/bun.md).
* **Node.js** 22 or later. Use a framework's Node adapter (Express, Hono's `@hono/node-server`,
  SvelteKit's `adapter-node`), or `nodeRequestListener` from
  `@pipedog/transport-smallwebrtc/http` for a plain `node:http` server. For WebSocket endpoints,
  `serveFetch()` from `@pipedog/transport-websocket/serve` serves a fetch handler with upgrades
  (with the `ws` package).
* **Deno** serves both endpoints with `Deno.serve`. See [Deno](https://polvallverdu.github.io/pipedog/docs/frameworks/deno.md).

`serveFetch(handler, { port })` listens with the same fetch handler on all three runtimes, so one
`server.ts` runs on Bun, Node and Deno; `examples/twilio-phone-bot` is served this way.

## The development runner [#the-development-runner]

[`@pipedog/runner`](https://polvallverdu.github.io/pipedog/docs/runner.md) is Pipecat's development runner. `main()` serves your `bot()`
with the prebuilt browser client, SmallWebRTC signalling and the telephony webhooks and WebSockets,
from flags (`bun bot.ts -t twilio --proxy ...`). For a server you deploy, build the same app with
`createRunnerApp()` and listen with `serve()`, or mount its `fetch` in your own server:

```ts
import { createRunnerApp } from "@pipedog/runner/run";
import { serve } from "@pipedog/runner/serve";

const runner = await createRunnerApp({ bot, transport: "twilio", proxy: "bot.example.com" });
await serve(runner, { host: "0.0.0.0", port: 7860 });
```

`serve()` runs on Bun, Node (with `ws`) and Deno. The runner's routes don't authenticate, as
upstream's don't; put your checks in front of them. See
[Mounting the runner](https://polvallverdu.github.io/pipedog/docs/runner/#mounting-the-runner-in-your-own-server).

## SmallWebRTC in production [#smallwebrtc-in-production]

SmallWebRTC is a peer-to-peer WebRTC connection between the browser and your server. The media
runs over UDP, so:

* the server needs UDP reachable from clients, on the ports the WebRTC stack picks;
* behind NAT, give the transport ICE servers (STUN, and TURN for restrictive networks);
* one server holds every session's media, so scale by adding servers and routing sessions to them.

See [SmallWebRTC](https://polvallverdu.github.io/pipedog/docs/transports/smallwebrtc.md) for the transport's options.

## WebSocket in production [#websocket-in-production]

WebSocket sessions need only TCP, so they pass through ordinary load balancers and proxies. Keep
idle timeouts on the proxy longer than a call, or send keepalives. Each socket stays on the server
that accepted it for its lifetime. See [WebSocket](https://polvallverdu.github.io/pipedog/docs/transports/websocket.md).

## Containers [#containers]

A container image needs the runtime and your app. If you use local turn detection
(`@pipedog/audio-silero`, `@pipedog/audio-smart-turn`), the ONNX models load at startup from a
path or URL you give them; bake them into the image or download them on start. See
[Turn detection](https://polvallverdu.github.io/pipedog/docs/concepts/turn-detection.md).

## Sizing [#sizing]

Each session decodes, resamples and encodes audio, and keeps provider connections open. Measure
CPU and memory per session under real load, and cap concurrent sessions per process. Provider rate
limits often bind before CPU does.
