steep-balloon-41261
02/17/2026, 7:44 AMtall-airline-16756
02/17/2026, 7:44 AMtall-airline-16756
02/17/2026, 7:44 AMtall-airline-16756
02/17/2026, 7:46 AMuser_input_transcribed event on AgentSession and write the final transcripts somewhere (console, file, or external service).
Below is a concise Python example using the current LiveKit Agents API pattern. It logs only final transcripts to console and appends them to a file.
from dotenv import load_dotenv
from livekit.agents import JobContext, AgentServer, cli, Agent, AgentSession
load_dotenv()
server = AgentServer()
@server.rtc_session()
async def entrypoint(ctx: JobContext):
session = AgentSession()
@session.on("user_input_transcribed")
def on_transcript(event):
# Only log final transcripts (avoid noisy interim updates)
if event.is_final:
text = event.transcript # final (possibly translated) transcript
# optional: raw transcript if provider includes it
# raw = getattr(event, "raw_transcript", None)
# Log to console
print(f"[STT] Final transcript: {text}")
# Append to a local log file
with open("transcriptions.log", "a", encoding="utf-8") as f:
f.write(text + "\n")
await session.start(
agent=Agent(instructions="You are a helpful assistant."),
room=ctx.room,
)
await ctx.connect()
if __name__ == "__main__":
cli.run_app(server)
Key notes and options
• Use event.is_final to avoid logging interim partial transcriptions unless you specifically want streaming updates.
• event.transcript contains the final (or translated) text. Some STT providers also provide raw_transcript or `alternative_transcripts`—check event fields if you need those.
• Where to store logs:
◦ Console for quick debugging.
◦ Local file (shown above) for local persistence.
◦ Push to an external logging/analytics service or a database for production (send text in an HTTP request or use your logging client).
• If you need per-user or per-room context, enrich the log entry with ctx.room, session metadata, or any identifiers available on the event.
If you’d like, tell me:
• Which language/SDK (Node, Python, or others) you prefer and I can provide an equivalent snippet.
• Whether you want interim (streaming) transcripts logged as well.
• Where you want to store transcripts (database, external logging, cloud storage) and I’ll give a short integration example. Sources: TTS Translator with Gladia STT | LiveKit Documentation | Transcriber | LiveKit Documentation | Voice AI quickstart | LiveKit Documentation | Pi Zero Transcriber | LiveKit Documentationtall-airline-16756
02/19/2026, 7:09 AMrtc port range in your LiveKit YAML needs to match your SIP RTP ports.
Short answer: No — the LiveKit rtc port range is for LiveKit’s WebRTC media (UDP) ports. SIP RTP uses its own set of UDP ports. They do not have to be the same, but you must ensure both services’ media port ranges are open and routed correctly on your network/NAT so media can flow.
What to check and do
• Keep LiveKit’s port_range_start / port_range_end dedicated to LiveKit’s WebRTC/RTC traffic. These are the UDP ports LiveKit will allocate for peer media.
• Configure your SIP server’s RTP port range independently for SIP calls. Do not reuse the exact same ports on the same host at the same time.
• Ensure your host firewall/NAT forwards the configured LiveKit UDP port range (10000–20000 in your YAML) to the LiveKit server IP (10.4.78.214). Likewise forward the SIP RTP range to your SIP server.
• If LiveKit and the SIP server run on the same host and both need ports in the same numeric range, avoid overlap. Either change one range or run one service on a different host/VM/container network so allocations don’t conflict.
• If you expect SIP <-> LiveKit media bridging (transcoding/gateway), the bridge component needs to be configured to use the proper source/destination ports for both sides — but that does not require the two ranges to be identical.
Notes about your Docker Compose
• You’re using host networking, so the LiveKit UDP port range you specified (10000–20000) will be bound directly on the host (10.4.78.214). Make sure those UDP ports are not consumed by another process (like a SIP server) on that host.
• Also confirm any intermediate firewall or cloud security group allows UDP traffic for that range and for the SIP RTP range.
If you want, I can:
• Help verify whether there’s a port conflict on your host (tell me if SIP is running on the same host and what its RTP range is).
• Suggest a safe LiveKit port-range size for your expected concurrent peers.
• Walk through how to test media flows (e.g., using simple WebRTC clients and observing port assignments).tall-airline-16756
02/19/2026, 7:16 AMrtc.use_external_ip: true in livekit.yaml (so LiveKit advertises an external IP/STUN behavior).
◦ Do not keep a hardcoded --node-ip 10.4.78.214 — instead pass the VM IP (static external or internal) via environment variable / startup script.
• For SIP service:
◦ Replace ws_url: '<ws://10.4.78.214:7880>' with the VM’s address (or hostname) at runtime; don’t hardcode it.
◦ Keep your SIP rtp_port range separate from LiveKit’s range to avoid port conflicts on the same host.
• Use environment variables or a startup script to inject the VM IP into the Compose file or into individual container envs. This prevents manual edits after VM recreation.
2) Ports & firewall rules to configure in GCP Create VPC firewall rules permitting the following to the VM (restrict source CIDRs where practical):
• TCP 22 (SSH) — restrict to your IP
• TCP 7880 (LiveKit HTTP/WS API) — allow client sources
• TCP 7881 (LiveKit gRPC) — allow as needed (internal or specific services)
• UDP 10000–10010 (LiveKit RTC media) — allow 0.0.0.0/0 for WebRTC clients (or restrict to known peers)
• UDP 10000–10010 (if you use same range for SIP transcoding) — ensure no overlap with FreePBX range
• UDP 5060 (SIP) — allow SIP peers or provider IPs; restrict source where possible
• UDP 10011–10020 (FreePBX RTP in your compose) — allow SIP peers/providers
• TCP 8080 (FreePBX UI) — restrict to your admin IP
• TCP 8081 (agent API) — restrict if it’s admin-only
Important: your Compose currently uses host networking. On GCP host networking is equivalent to binding ports directly on the VM, so those UDP ranges must be free on the host and permitted by the firewall.
3) Avoiding port conflicts on the single VM
• You already split RTP ranges: LiveKit uses 10000–10010; FreePBX uses 10011–10020 in your Compose — that’s correct. Keep them disjoint.
• Because all services use host network, ensure nothing else on the VM uses those UDP ports (e.g., don’t run another SIP server expecting 10000–10010).
• If you later run multiple instances on one VM, avoid host networking and use bridge networks with explicit port mappings.
4) VM size recommendations and resource planning (cost-conscious) Start small and scale up as needed. For testing with modest concurrency (tens of calls/sessions), recommended starting points:
• Test / small: e2-medium (2 vCPU, 4 GB) — possible for very light use only.
• Moderate testing (LiveKit + SIP + FreePBX + agent): e2-standard-4 (4 vCPU, 16 GB) — good balance and recommended baseline.
• If agent runs heavy STT/LLM workloads or expect many concurrent transcodes, consider a 4+ vCPU, 16–32 GB RAM machine.
Cost saving tips:
• Use preemptible (spot) VMs for non-production testing to lower compute cost (but they can be terminated).
• Shut down the VM when not testing (you can schedule stop/start).
• Monitor usage and scale up only if CPU/memory/network bottlenecks appear.
If you tell me expected concurrency (rooms / SIP calls / agent model load), I’ll refine sizing.
5) Deployment steps (high‑level)
1. Reserve a static external IP in GCP and attach it to the VM (recommended).
2. Create the VM (Ubuntu 22.04 or similar). Use machine type as recommended above.
3. Set up firewall rules in the VPC for the ports listed earlier.
4. SSH in and install Docker + docker-compose:
◦ apt update && apt install -y docker.io docker-compose
◦ sudo usermod -aG docker $USER (re-login)
1. Copy your docker-compose.yml, livekit.yaml, and agent code to the VM.
2. Modify compose/config to use the VM IP:
◦ Prefer injecting env vars or using a small startup script to write livekit.yaml with the correct external IP from metadata:
curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip
1. docker-compose up -d
2. Verify logs and services (docker-compose logs -f).
6) Startup automation (recommended)
• Put a startup script on the instance (Compute Engine → Management → Startup Script) that:
◦ Installs Docker if missing
◦ Fetches configs (or generates livekit.yaml) using VM metadata for IPs
◦ Runs docker-compose up -d
This avoids manual editing after restarts.
7) Turn/STUN considerations
• For reliable WebRTC across NATs, ensure Turn/STUN is accessible or let LiveKit detect/expose the external IP. If you see connection failures for remote clients, add a TURN server (coturn) or use a managed TURN service.
8) Resource monitoring & health checks
• Use docker stats, htop, and GCP Monitoring (Cloud Operations) to watch CPU/memory/network.
• Check LiveKit logs for port allocation and ICE candidates to confirm external IP and ports are being used.
• Test media flows with a browser client and a SIP call to confirm RTP packets use the configured ranges.
9) Tests to run after deployment
• Confirm services are listening on the expected ports (on the VM run: sudo ss -ulnp or sudo lsof -i -P -n).
• Start a LiveKit client and verify ICE candidates show your external IP and that media flows (check browser WebRTC internals).
• Make a SIP call from an external SIP trunk or a softphone to FreePBX; observe RTP ports and ensure audio passes.
• Check the SIP <-> LiveKit bridging (if you exercise transcoding) to ensure no port overlap and that transcoding works.
10) Security and cost best practices
• Restrict access to admin ports (FreePBX UI, agent API) to your IP only.
• Store secrets in an env file or secret manager rather than in compose files.
• Turn off the VM when not in use or automate scheduled stop/start.
• Keep logging rotation (you already have docker logging limits) and use GCP logging for long-term analysis.
Next steps I can help with
• Produce a startup script that automatically pulls the external IP and generates the correct livekit.yaml & compose envs.
• Suggest exact machine types based on expected concurrency if you provide expected number of concurrent LiveKit peers, concurrent SIP calls, and whether the agent uses heavy CPU for STT/LLMs.
• Provide a sample firewall rule set or a Terraform snippet to create the VM + firewall rules.
Which of the above would you like me to do next?
• Generate a startup script to inject the VM IP into your configs and run docker-compose
• Give an exact VM sizing recommendation if you share expected concurrency and agent workload
• Provide commands to verify ports and media flow after deployment
Tell me which one and any expected concurrency numbers, and I’ll produce the next artifact.
Sources: server-sdk-js | livekit/livekit_composite | DeepWiki | Self-hosted deployments | LiveKit Documentation | Running LiveKit on AWS | Diagnosing Connection Errors with the Connection Test Utility | LiveKit | Google AI and LiveKit | LiveKit Documentationtall-airline-16756
02/19/2026, 8:01 AMrtc.use_external_ip: true in livekit.yaml is correct for a cloud VM.
• Keeping RTP ranges disjoint:
◦ LiveKit: 10000–10010
◦ FreePBX: 10011–10020
◦ SIP service: 10021–10030
This avoids port conflicts on the host.
• Moving FreePBX off host networking to a bridge network (freepbx-net) is a good fix so it can bind 5060 inside the container while exposing 5061 on the host. This prevents direct conflicts with the SIP container binding 5060 on the host.
• Using Redis on 127.0.0.1 inside the compose (and host networking for redis) is fine as long as Redis runs on the same VM and you are OK with host networking for that service.
Important adjustments & checks
1. PUBLIC_IP injection
◦ Ensure you actually set PUBLIC_IP in the environment when launching Compose on the VM. Example approaches:
▪︎ Export in shell before docker-compose: export PUBLIC_IP=$(curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip)
▪︎ Or generate livekit.yaml at boot from a startup script replacing placeholders.
◦ If you reserve a static external IP in GCP, use that value for stability.
1. Docker Compose variable interpolation and quoting
◦ Make sure Compose interprets ${PUBLIC_IP} correctly in your multi-line YAML env (SIP_CONFIG_BODY). You may need to render that file via envsubst or build the SIP_CONFIG_BODY string in an env file or startup script. Multiline environment interpolation can be fragile inside YAML; consider templating at startup.
1. Host networking vs bridge networking mix
◦ You intentionally left redis/livekit/sip/agent on host networking and FreePBX on bridge network. That works, but keep these in mind:
▪︎ Host networking means containers bind directly to the host network stack. You must ensure the host firewall (GCP VPC rules + VM OS firewall) allows required UDP/TCP ports and no other processes are already using them.
▪︎ FreePBX ports that are published (e.g., 5061:5060/udp and 10011-10020) will be host ports, so you still need firewall rules for those host ports.
1. LiveKit --redis-host
◦ You set --redis-host 127.0.0.1:6379 in the livekit command. Because Redis is running with host networking, 127.0.0.1 refers to the VM host — that’s correct. Just ensure Redis is started before LiveKit (depends_on helps locally, but cloud boot ordering may differ; startup script with delays or healthchecks is robust).
1. SIP nat_1_to_1_ip/use_external_ip
◦ You set use_external_ip: true and nat_1_to_1_ip: '${PUBLIC_IP}' — good. Ensure your SIP peers expect that public IP (NAT mapping), and the firewall allows UDP RTP ranges you configured.
1. Firewall rules on GCP (must set these)
◦ Open these in your VPC firewall (restrict sources where possible):
▪︎ TCP 7880 (LiveKit WS/API)
▪︎ TCP 7881 (gRPC) if needed
▪︎ UDP 10000–10010 (LiveKit media)
▪︎ UDP 10011–10020 (FreePBX RTP)
▪︎ UDP 10021–10030 (SIP RTP for livekit/sip transcoding)
▪︎ UDP 5060 (SIP container binding on host) — your sip container uses 5060 on host, so open UDP 5060
▪︎ UDP 5061 (FreePBX exposed on host 5061) — open 5061 if you want external SIP to reach FreePBX
▪︎ TCP 8080 (FreePBX UI) — restrict to admin IP
▪︎ TCP 8081 (agent) — restrict as needed
◦ Confirm no overlapping firewall rules or unintended 0.0.0.0/0 exposure for admin UIs.
1. Port verification on the VM
◦ After deployment, check listeners with: sudo ss -ulnp or sudo lsof -i -P -n
◦ Ensure ports are bound as expected:
▪︎ Host UDP 10000–10010 bound by LiveKit
▪︎ Host UDP 10021–10030 bound by sip container
▪︎ Host UDP 5060 bound by sip container
▪︎ Host TCP 7880 bound by livekit
1. STUN/TURN consideration
◦ If clients behind restrictive NATs fail to connect or get no media, add TURN (coturn) or ensure LiveKit ICE candidates include a reachable relay. For testing, STUN might be enough; for real clients use TURN.
Short validation checklist to run after bringing VM up
1. Ensure PUBLIC_IP env var is set or templating produced correct configs.
2. Start docker-compose and inspect container logs: docker-compose logs -f livekit sip freepbx agent
3. Run sudo ss -ulnp to verify UDP ports bound as planned.
4. From an external client (web browser), open a LiveKit room and check ICE candidates in chrome://webrtc-internals — should show PUBLIC_IP and one of 10000–10010 UDP ports.
5. Place an external SIP call to FreePBX on the exposed host port (5061) and verify RTP uses 10011–10020 and audio flows.
6. Make a call via SIP → LiveKit (or a SIP trunk to sip container) and verify RTP is routed using configured ranges and transcoding works if used.
If you’d like, I can next:
• Provide a small startup script that:
a. Fetches the VM public IP from metadata
b. Exports PUBLIC_IP
c. Renders SIP_CONFIG_BODY and livekit.yaml (if you want dynamic values)
d. Starts docker-compose
• Or provide exact firewall rule commands (gcloud CLI) to create the required rules for the public IP and ranges.
Which would help most right now — a startup script or gcloud firewall commands?tall-airline-16756
02/19/2026, 8:28 AM