Operate the telephony edge (TelPro)
Use this page to keep the TelPro edge healthy. TelPro is the public telephony entry point: it accepts SIP from carriers and browser voice from Janus, and it forwards calls to the Voice hosts over the private network.
Availability: From 0.9.18, unknown-trunk rejections are logged at warn level and SIP trunks have a maintenance mode; neither needs a flag. Two-edge operation is controlled by TELPRO_HA_ENABLED (default false) on each TelPro host and also needs HCLOUD_TOKEN. Without it, each edge runs alone. To plan a pair, read Run two edges.
Edge components
All TelPro containers use host networking and run from /opt/services/telpro.
| Container | Component | Role on the edge |
|---|---|---|
voiceai-telpro | Kamailio | SIP proxy: trunk identification, routing, drain gate, TLS for SIP |
voiceai-rtpengine | RTPEngine | Media relay for carrier calls |
voiceai-webrtc | Janus | Browser voice gateway; sends SIP to Kamailio |
voiceai-turn | coturn | TURN relay for browsers behind restrictive networks |
voiceai-otel-collector | Telemetry collector | Exports edge telemetry to SigNoz |
log-to-span | Log converter | Turns structured Kamailio log events into trace spans |
Configuration sources
| Source | Contents |
|---|---|
| Container images | Kamailio routing logic, RTPEngine and Janus builds, tied to ECR_TAG |
| TelPro configuration bundle | Compose file, collector settings, TURN start script, refresh reconciler |
| Parameter and secrets stores | Ports, addresses, TURN credentials, edge certificates, Redis connection |
| Redis | Trunk definitions, trunk order, maintenance settings, instance drain flags and heartbeats |
| Bootstrap file | PUBLIC_IP, PRIVATE_IP, floating addresses and the host identity |
Trunk and routing changes that you save in the administration interface reach Kamailio through Redis and need no restart. Changes to parameters, secrets or the bundle need a refresh of the edge. The Environment variable reference lists every TelPro variable.
Initialization sets the instance identity from TELPRO_INSTANCE_ID, then KAMAILIO_INSTANCE_ID, then the host name, and passes the same value to Kamailio and the reconciler. Keep it unique per edge.
Restart order
Compose starts the containers in dependency order:
- RTPEngine.
- Kamailio, after RTPEngine has started.
- coturn.
- Janus, after Kamailio is healthy and coturn has started.
A restart of Kamailio or RTPEngine interrupts calls on that edge. For a planned change, use the drain path in Drain an edge instance instead of restarting containers by hand.
Check edge health
| Container | Health check |
|---|---|
voiceai-rtpengine | RTPENGINE_LISTEN_PORT listens and rtpengine-ctl lists sessions |
voiceai-telpro | kamcmd core.uptime answers |
voiceai-webrtc | The container's main process is Janus |
voiceai-turn | TURN_PORT accepts a TCP connection |
Run this command on the edge to see the state of each container:
cd /opt/services/telpro
docker compose ps
A healthy container proves only that the process answers locally. To confirm call handling, follow Service health and place a test call.
Read edge logs
cd /opt/services/telpro
docker compose logs -f telpro
Replace telpro with rtpengine, webrtc, turn or otel-collector for the other components.
- Kamailio writes structured events when
STRUCTURED_LOGGING_ENABLEDistrue, which initialization sets by default. The log-to-span sidecar turns these events into trace spans in SigNoz. - Docker keeps
SIP_CAPTURE_TELPRO_LOG_MAX_FILESfiles ofSIP_CAPTURE_TELPRO_LOG_MAX_SIZEeach for Kamailio; see TelPro variables for the defaults. - The refresh reconciler logs to the systemd journal. Its lines start with
[config-refresh] component=telpro-reconcilerand include aphase=field.
journalctl -u telpro-config-refresh.service
For log levels, field meanings and searching, see Logs and Telemetry and monitoring.
Recognize unknown-trunk rejections
When an INVITE arrives from an address that matches no configured trunk, Kamailio answers 403 Forbidden - Unknown Trunk. It logs Rejecting call from unknown trunk with the source address, From header and user agent.
From 0.9.18, the structured trunk_identify_failed event for this case has level warn instead of error. The telemetry collector marks it as an authorization security event. Kamailio treats these as expected rejections of unwanted traffic, so they no longer appear among error-level events.
Identification failures on a configured trunk, such as TLS or authentication problems, stay at error level. Investigate those first. For the full response table, see Routing responses.
Put an inbound trunk into maintenance
From 0.9.18, you can put an inbound trunk into maintenance so that Kamailio rejects its new calls with a response code you choose; see Maintain an inbound trunk to set it up.
Drain an edge instance
Each edge runs telpro-config-refresh.timer, which starts the reconciler every 30 seconds. The reconciler picks up refresh commands that the platform queues for this instance from Service Refresh.
The reconciler stops new calls on the edge, waits for existing calls to end, restarts the edge with fresh configuration and reports the result.
While the drain flag is set, Kamailio answers new INVITEs with 503 Service Unavailable and logs Rejecting new INVITE during TelPro maintenance drain. Existing calls continue. The drain-restart strategy needs at least two ready edges, so that new calls have somewhere to go.
To follow a drain, read the reconciler journal. The phases are acknowledged, draining, waiting, fetching_env, restarting, verifying and completed, or failed with a detail= reason.
Run two edges
Two-edge operation runs Kamailio, RTPEngine, Janus and coturn on both TelPro hosts and moves two floating addresses between them.
- SIP floating address: Only the host that owns it accepts carrier signaling, browser signaling and carrier registrations. Point the SIP, SIP-over-TLS and browser WebSocket DNS names at it.
- Media floating address: RTPEngine, Janus and coturn advertise it in SDP and ICE.
- Host-local values: Each host needs its own
TELPRO_INSTANCE_IDandTELPRO_PROVIDER_SERVER_ID, the peer's identities, the shared floating address IDs and the sharedTELPRO_INITIAL_ACTIVE_INSTANCE_ID. TelPro variables describes each one.
Both hosts configure both addresses locally so that services can bind before ownership moves. The cloud provider's assignment decides which host receives the traffic.
| Event | What happens |
|---|---|
| Kamailio maintenance | SIP ownership moves to the peer; existing media stays on the old host. |
| RTPEngine maintenance | New sessions move away; the engine restarts when its sessions reach zero. |
| Full-host maintenance | Both addresses move after dialogs and media sessions drain. |
| Active peer stops reporting | A healthy standby takes both addresses. Calls whose media engine failed cannot be recovered. |
| Edge certificate reload fails | SIP moves to the peer and Kamailio restarts. |
A Redis lease serializes maintenance, so only one edge changes at a time. Carrier registrations stay off until Redis holds an elected owner.
After a certificate reload, the edge opens a new TLS connection to TELPRO_TLS_VERIFY_ENDPOINT and checks the served fingerprint. The default endpoint is DOMAIN_TELPRO on SIPS_PORT. For certificate preparation, see Verify platform TLS.
Common failures
| Symptom | Likely cause | Action |
|---|---|---|
Initialization stops with PUBLIC_IP not found | The bootstrap file lacks the public address | Add PUBLIC_IP to /opt/deployment/.env and rerun initialization. |
All new calls get 503 from one edge | The drain flag is still set after a refresh | Read the reconciler journal for a failed phase, then rerun the refresh or recover the edge. |
Many 403 Forbidden - Unknown Trunk responses | Scanners, or a carrier sending from an address you have not configured | Compare the source address with the trunk's configured addresses. |
| Calls connect without audio | Media ports blocked, or a wrong advertised address | Check the RTP port ranges in Network ports and NAT and media addresses. |
| Browser voice connects signaling but no media | TURN or Janus media ports blocked | Check TURN_PORT, the TURN relay range and the Janus RTP range. |
voiceai-webrtc never starts | Kamailio is not healthy, so Janus waits | Fix Kamailio first; check docker compose logs telpro. |
Reconciler reports peer_not_ready or peer_identity_not_configured | The two-edge peer is missing or not ready | Check the peer's health and its host-local identity values. |
For caller-facing SIP responses and call traces, use SIP diagnostics.
Verify the edge
- Run
docker compose psin/opt/services/telproand confirm that all containers are running and healthy. - Confirm that the reconciler journal shows no
failedphase since the last change. - Place an inbound test call through each configured trunk and confirm that it reaches the expected flow.
- If browser voice is enabled, start a browser call and confirm audio in both directions.
Related
- SIP diagnostics: to interpret a SIP response that a caller or carrier reports.
- SIP routing: to change trunks, identification and trunk maintenance.
- Operate the voice services: to check the Voice hosts that receive calls from the edge.
- Deploy, verify and update Delphi: to queue an edge refresh after a configuration change.