Self-host
Run the Ear3 voice pipeline on your own Kubernetes cluster, in your own cloud account. Same Docker image as Ear3-managed, same SDK on the client — you only swap out where sessions spawn.
When this makes sense
- Compliance — audio must stay inside your VPC / GovCloud / on-prem
- Data residency — respondents in a region we don’t offer yet
- Custom Python processors — you need bespoke Pipecat processors (guardrails, own moderation LLM, custom recording sink)
- Cost floor at scale — > 100k interviews/month and you already have a voice-ops SRE
If none of the above applies, stay on Ear3-managed. Self-hosting real-time voice is a real ops commitment — see the honest cost math on the Server Overview.
What you’re standing up
┌────────────────────────────────────────────┐
│ Your Kubernetes cluster │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ ear3-pipecat-worker (Deployment) │ │
│ │ • Python 3.11 + pipecat-ai 1.3 │ │
│ │ • FastAPI /spawn endpoint │ │
│ │ • Warm pool of bot workers │ │
│ └─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ Redis (session state, warm pool) │ │
│ └─────────────────────────────────────┘ │
└────────────────────────────────────────────┘
│
│ HTTPS (spawn requests)
▼
app.ear3.ai
(session mint, dashboard, webhooks)The daemon is pipecat-cloud-server — an open FastAPI service that
exposes a POST / “spawn a bot” endpoint. Ear3’s app calls it the same
way it would call Pipecat Cloud.
Prerequisites
- Kubernetes ≥ 1.27 with an ingress controller
- Node pool sized for your peak concurrency (rough rule: 1 vCPU per concurrent session for CPU-only STT, or 1 GPU per ~10 sessions if you’re also self-hosting Whisper)
- API keys for the model providers you plan to use — Deepgram, Cartesia, OpenAI / Anthropic / Google, Daily. Set as env-vars on the worker. The worker never asks Ear3 for them.
- A Daily account (or a self-hosted SFU — Daily is the default; talk to us about custom SFU integration)
The Docker image
docker pull ghcr.io/ear3-ai/pipecat-cloud-server:v1.3.0Reference Dockerfile lives in the ear3-ai/pipecat-cloud-server repo — clone if you need to bake in custom Pipecat processors.
Deploying on Kubernetes
Minimal Deployment + Service manifest:
apiVersion: apps/v1
kind: Deployment
metadata:
name: ear3-pipecat
spec:
replicas: 3
selector:
matchLabels: { app: ear3-pipecat }
template:
metadata:
labels: { app: ear3-pipecat }
spec:
containers:
- name: bot-server
image: ghcr.io/ear3-ai/pipecat-cloud-server:v1.3.0
ports: [{ containerPort: 7860 }]
resources:
requests: { cpu: "2", memory: 4Gi }
limits: { cpu: "4", memory: 8Gi }
env:
- { name: DAILY_API_KEY, valueFrom: { secretKeyRef: { name: ear3-secrets, key: daily } } }
- { name: DEEPGRAM_API_KEY, valueFrom: { secretKeyRef: { name: ear3-secrets, key: deepgram } } }
- { name: OPENAI_API_KEY, valueFrom: { secretKeyRef: { name: ear3-secrets, key: openai } } }
- { name: CARTESIA_API_KEY, valueFrom: { secretKeyRef: { name: ear3-secrets, key: cartesia } } }
- { name: EAR3_BACKEND_URL, value: "https://app.ear3.ai" }
---
apiVersion: v1
kind: Service
metadata:
name: ear3-pipecat
spec:
selector: { app: ear3-pipecat }
ports: [{ port: 443, targetPort: 7860 }]
type: ClusterIPExpose it via your existing ingress with TLS — Ear3 only speaks to HTTPS endpoints.
Pointing Ear3 at your cluster
In your workspace dashboard → Settings → Server → Self-host, set:
- Pipecat endpoint —
https://pipecat.your-domain.com - Auth token — a shared secret you set on the worker’s ingress
From that moment forward, every session spawned in your workspace goes to your cluster instead of Pipecat Cloud. Nothing in the SDK, the respondent UI, or the webhook contract changes.
How your app talks to your worker
The client SDK doesn’t know which substrate it’s on. It always
POSTs to app.ear3.ai. Our backend decides where to spawn the bot
based on your workspace config.
Client SDK (browser)
│ POST /api/v2/sdk/connect
│ { interviewId, publishableKey, metadata }
▼
app.ear3.ai backend
│ 1. Verify pk → find workspace
│ 2. Read workspace.pipecatEndpoint (may be null)
│ 3. Route:
│ null → spawn on Pipecat Cloud (default)
│ set → spawn on your endpoint
▼
┌──────────────────────────────────┬────────────────────────────────┐
│ Ear3-managed (default) │ Self-host (your endpoint) │
│ │ │
│ POST api.pipecat.daily.co/... │ POST https://pipecat.you.com │
│ Authorization: Bearer PCC_KEY │ Authorization: Bearer │
│ │ workspace.pipecatToken │
│ │ (token encrypted at rest │
│ │ via KMS, decrypted per call) │
│ │ │
│ Response: { room_url, token } │ Response: { room_url, token } │
└──────────────────────────────────┴────────────────────────────────┘
│ │
└────────────► returned to client ──────────────────────────────┘
│
▼
Client joins the Daily room → conversationKey implication for your k8s setup: your worker ingress must be
reachable by app.ear3.ai over public HTTPS. That means:
- TLS terminated at your ingress (Let’s Encrypt / cert-manager or your own CA)
- A public DNS name your ingress resolves to
- Firewall / network policy that lets our egress reach it
If your compliance model requires the worker to be fully private (no public ingress at all), talk to us — we have a private-tunnel option (WireGuard between our egress worker and your cluster) but it needs manual setup per customer.
Security guarantees on our side
- Token at rest — your worker auth token is encrypted with our KMS key. We can’t read it out of the DB in plaintext; each spawn call decrypts on-the-fly
- Per-workspace rate limit — a misbehaving worker in your cluster can’t blow up spawn latency for other Ear3 customers
- Spawn timeout — 10 seconds budget. If your endpoint doesn’t respond, the SDK gets a clean error message (“Your worker is unreachable — check the endpoint status”) rather than a hang
- No secret exfiltration — we never send our Deepgram / Cartesia / OpenAI API keys to your worker. Your worker uses your vendor keys, set as k8s Secrets. This is why you own the vendor accounts on self-host
Warm pool sizing
Cold-starting a Python worker takes ~4 seconds on a warm image and ~15 seconds if the image is being pulled. Neither is acceptable for interactive voice.
Run a warm pool: replicas = P95 concurrent sessions + 20% headroom,
plus a HPA that kicks in on CPU > 70%.
For workloads that spike (marketing pushes, cohort emails), pre-warm by bumping replicas 5 min before the spike lands.
Observability
The server emits OpenTelemetry traces via
pipecat-ai[tracing]. Point them at Honeycomb / Datadog / your OTel
collector:
env:
- { name: OTEL_EXPORTER_OTLP_ENDPOINT, value: "https://otel.your-domain.com:4317" }
- { name: OTEL_SERVICE_NAME, value: "ear3-pipecat" }Key spans to watch:
session.start— cold-start budget (should be under 200 ms on a warm pod)llm.turn— LLM round-trip (main latency driver)tts.first_byte— TTS TTFB (should be under 400 ms with Cartesia Sonic)
Migrating back to Ear3-managed
Blow away the endpoint override in workspace settings and the next session spawns on Ear3-managed Pipecat Cloud again. No data migration required — the pipeline is stateless.
Support tier
Self-hosting is on a paid support tier (SLA, upgrade assistance,
incident escalation). Reach out via support@ear3.ai
or in the #self-host channel on our Discord.
Related
- Server Overview — the three substrates side by side
- pipecat-cloud-server repo — the source of the image