Most “the self-hosted engine is broken” reports trace back to four things that work differently on your hardware than they do on managed cloud: the engine owns its NIC (so there is no ordinary listening socket and config changes are reboot cutovers), DNS and per-tenant provisioning route calls to the right place, credentials are sealed at rest and must be unsealable by the engine, and self-hosted inference has to be explicitly attached. This page is the triage map for all four.
If you have not stood the deployment up yet, start with On-Prem / Self-Hosted — it describes what runs where and the shape of the install. This page assumes it is already deployed and something is wrong.

Triage first

Match the symptom, then jump to the section that explains the cause and the fix.
SymptomLikely causeSection
ss shows no UDP :443 listener, but the box is “up”Expected — the NIC is owned by the kernel-bypass fast pathNIC ownership
Deployed a new build / config, nothing changedConfig and builds are reboot cutovers, not hot reloadsReboot cutover
Carrier INVITEs never arrive, or land on the wrong tenantMissing SRV/CNAME records, or wrong subdomain labelDNS & provisioning
QUIC/WebTransport handshake fails from the browserMissing/expired TLS cert for the brand domain (SNI)DNS & provisioning
Calls reach the agent but trunk auth or provider keys failEngine cannot unseal sealed credentials (KEK mismatch)Sealed credentials
Agent answers but turns fail / fall back to a cloud modelOn-prem model server not attached, or egress blockedInference not attached
Reports and recordings stay emptyControl-plane services down, or auth to the event plane misconfiguredControl plane quiet

NIC ownership: there is no normal socket

The engine takes full ownership of its network interface with a kernel-bypass zero-copy fast path — SIP, RTP, and QUIC all ride one interface without going through the kernel’s UDP stack. The single most common false alarm is an operator running ss -ulpn (or netstat), seeing no UDP :443 and no UDP :5060 listener, and concluding the engine is down.
A missing UDP :443 / UDP :5060 socket in ss is normal and expected when the engine is running. Those packets are steered to the process below the kernel socket layer, so they never show up as bound sockets. Do not use ss/netstat as your liveness check.
Check liveness the right way instead:
1

Confirm the process and its interface

Verify the engine process is running and the fast-path filter is attached to the interface (your deploy runbook names both). If the process is up and the filter is loaded, the data plane is live even with no visible socket.
2

Hit the health endpoint

The control-plane API exposes /health and /healthz. A green health check plus a live process is the correct “engine is up” signal — not a socket table.
3

Place a real probe call

Register a softphone against the tenant SIP domain and dial in, or send a test INVITE from a trunk. A call that lands and answers is the ground truth. See Call not connecting if it doesn’t.
The fast-path filter is loaded before the engine starts (as a pre-start step) so that a crash-loop cannot black-hole the interface. If you ever need to hand the NIC back to the kernel (to debug with ordinary tooling), stop the engine and unload the filter — you will then see normal sockets again, but no calls will flow until the engine reclaims the interface.

Reboot cutover: changes need a reboot

Because the process owns the NIC, applying a new build or a config change is a reboot cutover, not a hot reload. If you shipped a change and behavior did not move, the overwhelmingly likely cause is that the engine host was never rebooted onto it.
SymptomCauseFix
New build shipped, old behavior persistsRunning process still holds the NIC on the previous imageReboot the engine host to bring the data plane up on the new build
Trunk / dialplan / agent config edit ignoredNIC-owning modules read owned state at start; not all knobs hot-reloadReboot; treat engine-side config as a windowed change
Brief call outage during a deployExpected — the interface quiesces while the engine is downDeploy inside a maintenance window; drain traffic first
Plan a maintenance window for every engine change. While the engine is down the interface is quiesced and in-flight calls drop. This is the single biggest operational difference from a typical service — budget for it in your runbook.
Some runtime tunables (agent timeouts, dialplan rules that are hydrated from the config store) do reload without a reboot — the dialplan hot-reloads its rules, and agent timeouts respond to a signal. But anything touching the transport, the NIC, or module load order needs the reboot cutover. When in doubt, reboot.

DNS and tenant provisioning

The engine multiplexes every tenant onto one interface and tells them apart by the leftmost DNS label of the inbound SIP INVITE (and by SNI on the QUIC plane). If DNS is wrong, calls either never arrive or land on the wrong workspace.
SymptomCauseFix
Carrier INVITEs never reach the engineMissing SIP SRV record, or it points at the wrong hostCreate the SRV for the tenant SIP domain → engine host; verify with dig SRV
Calls arrive but resolve to the wrong tenant (or none)Wrong leftmost label — <workspace-id>.sip.telequick.dev mismatchFix the per-tenant CNAME so the label matches the provisioned workspace id
Browser QUIC/WebTransport handshake failsNo valid TLS cert for the brand domain, or expiredInstall a valid cert; the transport does SNI across brand domains
Console / relay unreachableportal.telequick.dev, agent.telequick.dev, relay.telequick.dev not pointedPoint each hostname at the correct box (engine vs control plane)
Onboarding a workspace provisions the DNS records for you, but on a self-hosted estate you own the zone, so verify they actually resolve:
1

Verify the per-tenant SIP subdomain

Confirm <workspace-id>.sip.telequick.dev resolves to your engine host and that a SIP SRV record routes INVITEs to it. The engine reads the leftmost label (<workspace-id>) as the tenant, so the label must match the workspace id exactly.
2

Verify the WebRTC / browser subdomain

Confirm <workspace-id>.webrtc.telequick.dev resolves to the engine for browser-legged calls.
3

Verify the console, portal, and relay hostnames

portal.telequick.dev and agent.telequick.dev point at the control-plane box; relay.telequick.dev points at the relay (same host single-site, or the relay mesh multi-site).
4

Verify TLS for the QUIC plane

The public QUIC/WebTransport listener needs a valid cert for each brand domain it serves (it selects by SNI). An expired or missing cert looks like a transport failure, not a DNS failure — check both.
A wildcard *.telequick.dev record proves nothing about a specific label — confirm the explicit sip/webrtc/SRV records exist, not just that the wildcard answers. If trunk INVITEs are the problem, cross-check SIP trunk issues.

Sealed credentials won’t unseal

Tenant secrets — trunk digest passwords, agent provider API keys — are sealed at rest. The engine and the control plane share one key-encryption key (KEK) so the engine can unseal a secret at call time; there are no plaintext secrets on disk. This is what makes the deployment HIPAA / SOC 2 defensible — but if the two sides don’t share the same KEK, the engine holds ciphertext it cannot open.
SymptomCauseFix
Trunk registration / digest auth fails for every tenantEngine cannot unseal the sealed trunk secret (KEK mismatch)Align the engine’s KEK with the control plane’s; reboot the engine
Every agent’s provider call returns 401 / auth errorSealed provider API key cannot be unsealedSame KEK mismatch — realign, then reboot
Works for old config, breaks for newly saved secretsKEK was rotated on one side onlyRe-seal secrets under the current KEK on both sides
1

Confirm both hosts share one KEK

The engine host and the control-plane host must be configured with the same key-encryption key. A mismatch is the number-one cause of “auth fails for everything at once.”
2

Re-save the affected secrets

If the KEK was rotated, secrets sealed under the old key are unreadable. Re-enter and save the trunk secrets and provider keys in the console so they are re-sealed under the current KEK.
3

Reboot the engine

The engine reads the KEK at start (NIC ownership → reboot cutover). Reboot the engine host after any KEK change so it picks up the new key.
A KEK mismatch presents as a fleet-wide auth failure (every tenant, every provider) rather than a single bad credential. If exactly one trunk or one agent fails, suspect that credential, not the KEK.

Inference not attached

On a self-hosted engine you can still use the managed-cloud speech providers (cloud realtime, ASR/LLM/TTS) as long as the engine is allowed egress to them. Running your own models on-prem is a separate, explicit step: the model server must be registered with the inference control plane and the agent’s provider must point at your endpoint over QUIC. If either is missing, the agent silently uses whatever provider its config still names.
SymptomCauseFix
Agent answers, but turns go to a cloud model you meant to replaceAgent provider still set to the cloud providerPoint the agent at your self-hosted endpoint
On-prem model attached, but turns error or time outModel server not registered with the inference control plane, or unreachable over QUICRegister the server; confirm the engine can reach it
Cloud providers fail on-premEgress to the provider is blocked by your firewallAllow egress, or move fully to self-hosted inference
1

Register the model server

Register your GPU-backed model server with the inference control plane so the engine can discover and route to it over QUIC. Full steps are in Self-Hosted Inference.
2

Point the agent at your endpoint

Set the agent’s provider/endpoint to your on-prem model. Until you do, the engine keeps using whatever provider the agent config names — often a cloud one from an earlier test.
3

Confirm reachability, then place a call

Verify the engine can reach the model host, then dial in and watch a turn complete. Use Latency breakdown to confirm turns are actually hitting your GPUs.
Honest status. The QUIC-based inference control plane that discovers and routes to your GPU pods is shipped, but the fully on-prem speech-to-speech model path has not yet been live-verified end-to-end in a customer deployment. If you need on-prem GPU inference from day one, plan it as a joint bring-up rather than a drop-in. The managed-cloud model providers work against a self-hosted engine today if egress is permitted.

Control plane or observability plane is quiet

If calls connect and sound fine but reports, call traces, and recordings never appear, the fault is on the control-plane host, not the engine. The engine emits every call-lifecycle event, QoS/MOS record, and recording to the control-plane services; if those services are down or unauthenticated, the data is produced but never landed.
SymptomCauseFix
Reports / call traces stay emptyEvent pipeline service on the control-plane host is downStart the control-plane systemd units; confirm they’re healthy
Recordings never surface in the consoleRecording service or object store down / misconfiguredCheck the recording service and object-store units
Events silently droppedThe event plane is always TLS + SASL authenticated — no plaintext modeFix the SASL credentials on both producer and broker sides
A service won’t start at allThere is no Docker — units run against a pinned Node runtime under /optConfirm the pinned runtime exists and the unit’s paths are correct
1

Check the control-plane units

The admin API, config database, event pipeline, and object store all run as systemd units (no containers). Confirm each is active and healthy — a stopped unit is the usual cause of empty dashboards.
2

Verify the event plane authentication

The internal event transport is always TLS + SASL authenticated; there is no plaintext fallback. A credential mismatch drops events silently — realign the SASL credentials.
3

Confirm the pinned runtime

Services bind to a pinned Node runtime under /opt. If a unit fails to start, check that the runtime path in the unit file exists on the box.
Some in-console dashboards are still being wired end-to-end on self-hosted installs — if a specific panel is blank while call audio is clearly working, verify the underlying pipeline is up before assuming a bug. See Observability overview for what each panel is fed by.

There is no sandbox

A self-hosted deployment is a single production environment — there is intentionally no separate sandbox toggle. Do your testing in a dedicated workspace (or a second install), never against a “sandbox” that doesn’t exist. Treating prod as prod avoids a class of “why is my test call billing / paging the on-call” surprises.

On-Prem / Self-Hosted

The install itself: what runs where, and the reboot-cutover deploy flow.

Deployment Models

Managed, self-hosted, and hybrid — splitting the media and control planes.

Self-Hosted Inference

Register your GPU model server and route turns to it over QUIC.

Call not connecting

When the probe call itself won’t land or answer.