Most “the self-hosted engine is broken” reports trace back to four things that work differently on your hardware than on managed cloud:
  • The engine owns its NIC. 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 the engine must be able to unseal them.
  • Self-hosted inference must be explicitly attached.
This page is the triage map for all four.
If you did not stand the deployment up yet, start with On-Prem / Self-Hosted. That page describes what runs where, and the shape of the install. This page assumes the system is already deployed and something is wrong.

Triage first

Match the symptom. Then go to the section that explains the cause and the fix.

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 the kernel’s UDP stack. The most common false alarm: an operator runs ss -ulpn (or netstat), sees no UDP :443 and no UDP :5060 listener, and concludes the engine is down.
A missing UDP :443 / UDP :5060 socket in ss is normal and expected when the engine runs. The fast path steers those packets to the process below the kernel socket layer, so they never show as bound sockets. Do not use ss/netstat as your liveness check.
Check liveness the correct way instead:
1

Confirm the process and its interface

Check that the engine process runs, and that 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. A socket table is not.
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 the call does not land.
The fast-path filter loads before the engine starts (as a pre-start step), so a crash-loop cannot black-hole the interface. If you must hand the NIC back to the kernel (to debug with ordinary tools), stop the engine and unload the filter. You then see normal sockets again, but no calls flow until the engine reclaims the interface.

Reboot cutover: changes need a reboot

Because the process owns the NIC, 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 most likely cause is that the engine host was never rebooted onto it.
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 biggest operational difference from a typical service. Budget for it in your runbook.
Some runtime tunables do reload without a reboot. The dialplan hot-reloads its rules, and agent timeouts respond to a signal. These values are hydrated from the config store. But anything that touches 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. It tells tenants apart by the leftmost DNS label of the inbound SIP INVITE, and by SNI on the QUIC plane. If DNS is wrong, calls never arrive, or they land on the wrong workspace. Onboarding a workspace provisions the DNS records for you. But on a self-hosted estate, you own the zone. Check that the records resolve:
1

Verify the per-tenant SIP subdomain

Check that <workspace-id>.sip.clutchcall.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. The label must match the workspace id exactly.
2

Verify the WebRTC / browser subdomain

Check that <workspace-id>.webrtc.clutchcall.dev resolves to the engine for browser-legged calls.
3

Verify the console, portal, and relay hostnames

portal.clutchcall.dev and agent.clutchcall.dev point at the control-plane box. relay.clutchcall.dev points at the relay (the same host for single-site, or the relay mesh for 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 *.clutchcall.dev record proves nothing about a specific label. Check that the explicit sip/webrtc/SRV records exist. Do not stop at a wildcard answer. If trunk INVITEs are the problem, cross-check SIP trunk issues.

Sealed credentials won’t unseal

Tenant secrets — trunk digest passwords and 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 makes the deployment HIPAA / SOC 2 defensible. But if the two sides do not share the same KEK, the engine holds ciphertext it cannot open.
1

Confirm both hosts share one KEK

The engine host and the control-plane host must have 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. Enter the trunk secrets and provider keys again in the console, and save them. This re-seals them 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 shows as a fleet-wide auth failure (every tenant, every provider), not as 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), if the engine has egress to them. Your own models on-prem are a separate, explicit step. You must register the model server with the inference control plane, and the agent’s provider must point at your endpoint over QUIC. If either step is missing, the agent silently uses whatever provider its config still names.
1

Register the model server

Register your GPU-backed model server with the inference control plane, so the engine can discover it 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 uses whatever provider the agent config names — often a cloud one from an earlier test.
3

Confirm reachability, then place a call

Check that the engine can reach the model host. Then dial in and watch a turn complete. Use Latency breakdown to check that turns hit 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 is not yet 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, not 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 sends every call-lifecycle event, QoS/MOS record, and recording to the control-plane services. If those services are down or unauthenticated, the engine produces the data, but the data never lands.
1

Check the control-plane units

The admin API, config database, event pipeline, and object store all run as systemd units (no containers). Check that each unit 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. Align 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 clearly works, check that the underlying pipeline is up before you assume a bug. See Observability overview for what feeds each panel.

There is no sandbox

A self-hosted deployment is a single production environment. There is intentionally no separate sandbox toggle. Do your tests in a dedicated workspace (or a second install). Never test against a “sandbox” that does not exist. Treat prod as prod. This prevents a class of “why does my test call bill / page 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 — how to split 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 does not land or answer.