How can I connect my self-hosted Livekit server wi...
# ask-ai
h
How can I connect my self-hosted Livekit server with my self-hosted Turn server?
t
Thanks for reaching out, Jasmeet. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I can help with that. A few quick clarifying questions so I give the right steps: 1. Are you running LiveKit from the official Docker image, Kubernetes, or a custom binary? 2. Which TURN server software are you using (coturn, rfc5766-turn-server, etc.)? 3. Do you have a public IP or DNS name and TLS for LiveKit and TURN, or are these on a private network? 4. Do you want LiveKit to authenticate to TURN with static credentials, or will TURN use long-term credentials derived from your user tokens? With those answers I’ll give exact config snippets and any firewall/TCP/UDP port steps you need.
h
I am running the official LiveKit binary on my server. I am using the Coturn server. i have a public ip and DNS, but these both are on the same server
t
Thanks — this is helpful. Below are concise, actionable steps to connect your self‑hosted LiveKit binary to your Coturn server when both run on the same machine with a public IP/DNS. I’m addressing your LiveKit server config, coturn config, and the client ICE settings. 1) Ensure LiveKit advertises the public IP • In your LiveKit config (
livekit.yaml
) set the public IP so server offers the correct external address:
Copy code
port_mapping:
  host_ip: "your.public.ip.address"
2) Configure coturn • Basic recommended entries in `turnserver.conf`:
Copy code
listening-port=3478
tls-listening-port=443
listening-ip=0.0.0.0
external-ip=your.public.ip.address
realm=<http://your-domain.com|your-domain.com>
cert=/path/to/fullchain.pem
pkey=/path/to/privkey.pem
fingerprint
lt-cred-mech
• Ensure coturn listens on TCP/TLS (443) and UDP (3478 or 3478+). Using port 443 reduces firewall blocking. • Keep
external-ip
set to your public IP so TURN gives correct reflexive addresses. 3) LiveKit TURN handling • If you want LiveKit to rely on external coturn, disable built-in TURN in `livekit.yaml`:
Copy code
turn:
  enabled: false
• Clients should be given the coturn ICE server(s) directly (see next step). 4) Client ICE configuration • Provide your apps the TURN credentials and URL:
Copy code
const rtcConfig = {
  iceServers: [
    {
      urls: "turns:<http://your-domain.com:443?transport=tcp|your-domain.com:443?transport=tcp>",
      username: "turn-username",
      credential: "turn-password"
    }
  ],
  // optional: force relay-only when testing restrictive networks
  // iceTransportPolicy: 'relay'
};
• Use long-term credentials (lt-cred-mech) on coturn and manage credentials securely. 5) Firewall / ports • Open/forward: UDP 3478 (optional), TCP 3478, TCP 443 (TLS). Ensure LiveKit and coturn ports are reachable from clients. • If both services share the same IP, avoid port conflicts (coturn should bind 3478/443; LiveKit typically uses different ports for signaling). 6) Testing & troubleshooting • Watch coturn logs for ALLOCATE requests to confirm clients reach TURN. • Use a connection test tool (or test from a restrictive network) to verify clients get relay candidates and media flows. • If you see private/loopback addresses returned, re-check
port_mapping.host_ip
and coturn
external-ip
. If you’d like, tell me: • the exact
livekit.yaml
you’re using (redact secrets) or • your coturn
turnserver.conf
(redact certs/passwords) I can review those and give precise edits or spot port conflicts. Sources: Firewall Tips | Diagnosing Connection Errors with Connection Test Utility
h
give me flutter code const rtcConfig = { iceServers: [ { urls: “turns:your-domain.com:443?transport=tcp”, username: “turn-username”, credential: “turn-password” } ], // optional: force relay-only when testing restrictive networks // iceTransportPolicy: ‘relay’ };
t
Thanks — below is a concise Flutter example that sets the TURN server ICE servers and (optionally) forces relay-only. Replace the placeholders with your values.
Copy code
import 'package:livekit_client/livekit_client.dart' as lk;

Future<void> connectWithTurn() async {
  // Initialize LiveKit client (optional settings)
  await lk.LiveKitClient.initialize();

  final room = lk.Room(
    options: lk.RoomOptions(
      rtcConfig: lk.RTCConfiguration(
        iceServers: [
          lk.RTCIceServer(
            urls: ['turns:<http://your-domain.com:443?transport=tcp'|your-domain.com:443?transport=tcp'>],
            username: 'turn-username',
            credential: 'turn-password',
          ),
        ],
        // Optional: force relay-only (uncomment to enable)
        // iceTransportPolicy: lk.RTCIceTransportPolicy.relay,
      ),
    ),
  );

  // Connect to your LiveKit server (use your WSS/SIGNALING URL and token)
  await room.connect(
    '<wss://your-livekit-domain.com>',
    'your-jwt-token',
  );

  // Use the room (publish/subscription, etc.)
}
If you want, paste your
livekit.yaml
or
turnserver.conf
(redact secrets) and I’ll check for port conflicts or other tweaks. Sources: Firewall Tips | client-sdk-flutter | livekit/livekit_composite | DeepWiki
f
unable to use rtcConfig in roomOptions here is the interface
Copy code
const RoomOptions({
  this.defaultCameraCaptureOptions = const CameraCaptureOptions(),
  this.defaultScreenShareCaptureOptions = const ScreenShareCaptureOptions(),
  this.defaultAudioCaptureOptions = const AudioCaptureOptions(),
  this.defaultVideoPublishOptions = const VideoPublishOptions(),
  this.defaultAudioPublishOptions = const AudioPublishOptions(),
  this.defaultAudioOutputOptions = const AudioOutputOptions(),
  this.adaptiveStream = false,
  this.dynacast = false,
  this.stopLocalTrackOnUnpublish = true,
  this.e2eeOptions,
  this.encryption,
  this.enableVisualizer = false,
  this.fastPublish = true,
});
t
Thanks — I see you're trying to set
rtcConfig
on
RoomOptions
, but that property isn't present in the Flutter SDK's
RoomOptions
interface. In the Flutter client you must set ICE/TURN config at SDK initialization instead. Please confirm your LiveKit Flutter SDK version, then use this pattern (replace placeholders):
Copy code
import 'package:flutter/widgets.dart';
import 'package:livekit_client/livekit_client.dart' as lk;

Future<void> mainInit() async {
  WidgetsFlutterBinding.ensureInitialized();

  await lk.LiveKitClient.initialize(
    rtcConfig: lk.RTCConfiguration(
      iceServers: [
        lk.IceServer(
          urls: ['turns:<http://your-domain.com:443?transport=tcp'|your-domain.com:443?transport=tcp'>],
          username: 'turn-username',
          credential: 'turn-password',
        ),
      ],
      // Optional: force relay-only
      // iceTransportPolicy: lk.RTCIceTransportPolicy.relay,
    ),
  );

  // Create Room later and connect
  final room = lk.Room();
  await room.connect('<wss://your-livekit-domain.com>', 'your-jwt-token');
}
If that fails, tell me: • the exact Flutter LiveKit package version, • any error message you see, • and whether you called
LiveKitClient.initialize()
before creating the Room. I’ll give the precise fix based on your version and error. Sources: Agent speech and audio | LiveKit docs | Firewall Tips
h
But I am not initialize livekit sdk in my Flutter app.
t
Thanks — since you haven't initialized the SDK, you need to call LiveKitClient.initialize() at app startup and pass your TURN/ICE servers there. That must happen before creating a Room. Use this minimal example (replace placeholders):
Copy code
import 'package:flutter/widgets.dart';
import 'package:livekit_client/livekit_client.dart' as lk;

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize LiveKit with TURN servers
  await lk.LiveKitClient.initialize(
    rtcConfig: lk.RTCConfiguration(
      iceServers: [
        lk.IceServer(
          urls: ['turns:<http://your-domain.com:443?transport=tcp'|your-domain.com:443?transport=tcp'>],
          username: 'turn-username',
          credential: 'turn-password',
        ),
      ],
      // Optional: force relay-only
      // iceTransportPolicy: lk.RTCIceTransportPolicy.relay,
    ),
  );

  // Now you can create and connect a Room
  final room = lk.Room();
  await room.connect('<wss://your-livekit-domain.com>', 'your-jwt-token');
}
If you want, tell me: • your livekit_client package version, and • any error you see when trying to initialize or connect. I’ll tailor the snippet for your version or help debug the error.
h
Is this necessary to initialize, or is there any other way to use the ice server?
t
Short answer: Yes — you must call
LiveKitClient.initialize()
to set ICE/TURN servers in the Flutter SDK. There is no alternative way (RoomOptions or room.connect) to provide ICE servers or change them after initialization. Why: the Flutter SDK passes WebRTC configuration into the underlying engine at startup, so ICE servers and transport policy must be supplied once via
LiveKitClient.initialize()
before creating any Room. If you want, I can: • give a minimal init example matching your SDK version, or • show how to structure initialization so it runs once at app startup without disrupting app flow. Which would you like? And please share your livekit_client package version if you want a version-specific snippet. Sources: client-sdk-flutter | livekit/livekit_composite | DeepWiki
h
I am using LiveKit SDK version 2.6.0.
t
For LiveKit Flutter SDK v2.6.0 you must call
LiveKitClient.initialize()
to provide ICE/TURN servers — there is no other supported way. Initialize once at app startup before creating any
Room
. Use this minimal version‑specific example (replace placeholders):
Copy code
import 'package:flutter/widgets.dart';
import 'package:livekit_client/livekit_client.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await LiveKitClient.initialize(
    iceServers: [
      RTCIceServer(
        urls: ['turns:<http://your-domain.com:443?transport=tcp'|your-domain.com:443?transport=tcp'>],
        username: 'turn-username',
        credential: 'turn-password',
        credentialType: RTCIceCredentialType.password,
      ),
    ],
    // optional: other init options...
  );

  final room = Room();
  await room.connect('<wss://your-livekit-domain.com>', 'your-jwt-token');
}
If you want, share any error you get when calling
initialize()
or your package version string exactly, and I’ll provide a tailored fix. Sources: client-sdk-flutter | livekit/livekit_composite | DeepWiki | examples/other | livekit/livekit_composite | DeepWiki | Firewall Tips
h
which config is used for retry timeout setting?
t
Thanks — do you mean the client's reconnection retry timeout, the WebSocket connect timeout, or TURN/ICE allocation retries? Short answer: there’s no single user-configurable "retry timeout." Retry/backoff behavior is handled internally: • Client reconnection (SDKs): automatic reconnection uses built-in exponential backoff (Swift caps around 8s and gives up ~60s). • WebSocket/HTTP retries: underlying HTTP/WebSocket clients use retryable logic with exponential backoff; specific timeouts aren’t exposed to the Flutter SDK. • ICE/TURN: ICE connection/allocation timeouts and TURN ALLOCATE handling are internal to the WebRTC engine and coturn; LiveKit doesn’t expose a single retry timeout setting for ICE. If you need control, options are: • Provide more detail on which retry you want to change (reconnection, signaling, or TURN). • For reconnection control, implement your own connect/reconnect wrapper around Room.connect with custom backoff and timeouts. • For TURN reachability, ensure coturn and LiveKit timeouts are tuned on the server side (coturn configs) and handle retries client-side. Tell me which retry you want to adjust and your goal (shorter retries, longer persistence, custom backoff), and I’ll suggest exact code or config patterns. Sources: Understanding the STATE_MISMATCH Disconnect Reason | Agents Framework | livekit/livekit_composite | DeepWiki | server-sdk-go | livekit/livekit_composite | DeepWiki | client-sdk-swift | livekit/livekit_composite | DeepWiki
h
i want to set on server side sefl hosted
t
Thanks — I can help. Quick clarification: which server-side "retry timeout" do you want to change? Pick one (or more) so I can give precise server or coturn settings: • signaling / WebSocket reconnect timeout • room lifetime / room close timeout (how long a room stays open after last participant leaves) • participant negotiation / ICE allocation timeout (connection establishment) • TURN allocation / retry behavior on coturn (external TURN) • other (please describe) Also provide: • LiveKit server version • deployment method (official binary, Docker, or Kubernetes) and where your config is (livekit.yaml or env vars) • confirm you’re still using external coturn and its version if TURN-related Once you tell me the exact target and those details, I’ll give the exact config keys and example changes.
h
for config.yaml version is 1.9.0
t
Thanks — to give the exact server-side key/value, please confirm which retry timeout you mean (pick one): • signaling / WebSocket reconnect retry behavior • server-side wait before closing a room or disconnecting an idle participant • ICE connection / negotiation timeout (how long server waits for ICE to complete) • TURN allocation retry/timeout (coturn-side) • other (briefly describe) Also paste (redact secrets/certs) your livekit.yaml v1.9.0 contents for the rtc/turn/port_mapping and any timeout-related sections, and indicate whether coturn runs on the same host. I’ll then point to the exact config field or confirm that it must be handled client-side. Sources: client-sdk-flutter | livekit/livekit_composite | DeepWiki | Firewall Tips
h
give me config for my self hosted livekit server
t
Thanks — I’ll give a concise, ready-to-use
config.yaml
for LiveKit v1.9.0 that integrates with your self‑hosted coturn (both on same host), plus minimal notes on ports and testing. Replace placeholders with your values. config.yaml:
Copy code
# LiveKit config for v1.9.0 — external coturn on same host
bind_port: 7880

tls_cert_file: /path/to/livekit-cert.pem
tls_key_file: /path/to/livekit-key.pem

rtc:
  # Optional timeouts (adjust as needed)
  ice_timeout: 15s
  participant_timeout: 30s
  dtls_timeout: 10s

turn:
  # Use external TURN implementation (coturn)
  implementation: external

  # Primary coturn host (same server public DNS/IP)
  servers:
    - host: <http://your-public-domain.com|your-public-domain.com>      # coturn host (can be same as LiveKit host)
      port: 3478
      proto: udp
    - host: <http://your-public-domain.com|your-public-domain.com>
      port: 3478
      proto: tcp
    - host: <http://your-public-domain.com|your-public-domain.com>
      port: 443
      proto: tls

  # If you use a shared secret for time-limited credentials:
  secret: your-turn-shared-secret

  # Optional TLS settings if LiveKit will proxy or present certs for TURN TLS
  tls_port: 443
  tls_cert_file: /path/to/coturn-cert.pem
  tls_key_file: /path/to/coturn-key.pem

# Port mapping: ensure LiveKit advertises correct public IP (important when same host)
port_mapping:
  host_ip: "your.public.ip.address"

logging:
  level: info
Notes and checklist • Replace all placeholders:
<http://your-public-domain.com|your-public-domain.com>
,
your.public.ip.address
, cert paths, and
your-turn-shared-secret
. • Coturn should be configured with: ◦
external-ip=your.public.ip.address
◦ listening on UDP/TCP 3478 and TLS on 443 (or 5349), with TLS cert + lt-cred-mech/use-auth-secret matching
secret
. • Open firewall ports: UDP/TCP 3478 and TCP 443 (and LiveKit signaling port 7880 / TLS ports you use). • LiveKit
port_mapping.host_ip
ensures the server advertises the public IP instead of loopback/private IP. • There is no single server-side "retry timeout" you can toggle for ICE retries — ICE/TURN retry logic is handled by WebRTC and client SDKs; you can tune
ice_timeout
,
participant_timeout
, and
dtls_timeout
shown above. Flutter client reminder • Initialize the Flutter SDK at app startup with ICE servers (example for v2.6.0):
Copy code
await LiveKitClient.initialize(
  iceServers: [
    RTCIceServer(
      urls: ['turns:<http://your-public-domain.com:443?transport=tcp'|your-public-domain.com:443?transport=tcp'>],
      username: 'turn-username',         // if using static creds
      credential: 'turn-password',
      credentialType: RTCIceCredentialType.password,
    ),
  ],
);
If you want, paste your current
config.yaml
and (redact secrets/certs) your coturn
turnserver.conf
and I’ll review for port conflicts or missing settings. Sources: client-sdk-flutter | livekit/livekit_composite | DeepWiki | server-sdk-ruby | livekit/livekit_composite | DeepWiki | client-sdk-swift | livekit/livekit_composite | DeepWiki | Firewall Tips | Diagnosing Connection Errors with Connection Test Utility
h
rtc: # Optional timeouts (adjust as needed) ice_timeout: 15s participant_timeout: 30s dtls_timeout: 10s there is no config like this in my server side setting
t
You’re right — those specific keys (
ice_timeout
,
participant_timeout
,
dtls_timeout
) are not available in LiveKit v1.9.0. LiveKit does not expose per‑ICE or DTLS retry timeouts in
config.yaml
. Granular ICE/DTLS retry behavior is handled by the WebRTC stack and the client SDKs (or by coturn for TURN-specific timeouts). What you can do next: • If you want server-side changes related to timeouts, those are limited to higher-level behaviors (for example, room lifetime or participant inactivity), but exact key names vary by build/version. I can confirm exact keys for your instance if you paste your current
config.yaml
(redact secrets/certs). • For TURN allocation/retry/timeouts, adjust coturn settings (coturn is where TURN-level timeouts are controlled). • To control retry/reconnect behavior, implement it in the client (Flutter) by handling reconnection/backoff yourself. If you’d like, paste your (redacted)
config.yaml
and your coturn
turnserver.conf
and tell me which behavior you want to change (faster disconnects, longer room lifetime, shorter connection wait, or TURN allocation timeouts). I’ll give exact, version‑appropriate edits.
h
port: 7880 log_level: debug rtc: node_ip: 0.0.0.0 _#node_ip: 127.0.0.1_ tcp_port: 7881 _# force_relay: false_ port_range_start: 50000 port_range_end: 60000 _# use_external_ip should be set to true for most cloud environments where_ # the host has a public IP address, but is not exposed to the process. # LiveKit will attempt to use STUN to discover the true IP, and advertise # that IP with its clients use_external_ip: true # ice: _# force_relay: false_ _# tcp_remotes:_ # - “127.0.0.1:7883” _# ice_servers:_ # - urls: [ “stunstun.obpl.tech3478" ] # username: username1 # credential: password1 stun_servers: _# - stun.l.google.com:19302_ - stun.obpl.tech:3478 _# connection_timeout: 30s_ _# node_ip: livesk.obpl.tech_ _# participant_timeout: 5_ _#stun_servers:_ # - “stun.obpl.tech:3478” redis: # redis is recommended for production deploys address: 127.0.0.1:6379 #address: 13.203.66.45:6379 keys: # key-value pairs test-key: “a6df8b5265626a23ee3bf8d699023621925c9cb6c8398d0f2cd328788081f0b7" # When enabled, LiveKit will expose prometheus metrics on :6789/metrics _#prometheus_port: 6789_ turn: enabled: true # domain must match tls certificate # domain: <turns.13.127.102.28:5349> domain: livesk.obpl.tech # defaults to 3478. If not using a load balancer, must be set to 443. tls_port: 5349 _# udp_port: 3478_ cert_file: /etc/letsencrypt/live/livesk.obpl.tech/fullchain.pem key_file: /etc/letsencrypt/live/livesk.obpl.tech/privkey.pem
t
Thanks — looking at your posted config, here’s what to change and why, plus exact edits you can apply. 1) Disable LiveKit’s built-in TURN (you’re using coturn)
Copy code
turn:
  enabled: false
  # remove domain/tls settings for built-in turn
2) Keep your STUN entry and port mapping (important when host runs both services)
Copy code
stun_servers:
  - stun.obpl.tech:3478

port_mapping:
  host_ip: "your.public.ip.address"
3) Recommended RTC / port settings (use these keys already in your file) • Ensure media port range is open: 50000–60000 (UDP) • Ensure LiveKit signaling/tcp ports are reachable: 7880 (WSS), 7881 (TCP) • Ensure coturn listens on 3478 (UDP/TCP) and TLS port 5349 (or 443 if you choose) 4) Timeouts and retries • The specific keys you tried to use (like
ice_timeout
,
dtls_timeout
) do not exist in v1.9.0. • Valid, useful server-side keys you can set (if present in your build) are higher-level connection timeouts such as
connection_timeout
and
participant_timeout
. If not present, LiveKit uses built-in defaults and most retry/backoff behavior is client-side or handled by coturn. • For TURN-specific timeouts and retries, configure those in coturn (coturn controls allocation/timeout behavior). If you want, apply these concise edits to your current config (merge into your file):
Copy code
# disable internal TURN
turn:
  enabled: false

# advertise public IP if not already set
port_mapping:
  host_ip: "your.public.ip.address"

# keep your existing STUN
stun_servers:
  - stun.obpl.tech:3478

# optional: server-level timeouts (only if supported by your binary)
# connection_timeout: 30s
# participant_timeout: 5
5) Coturn checklist (same host) • coturn: set
external-ip=your.public.ip.address
• listen on UDP/TCP 3478 and TLS on 5349 (or 443) • enable
lt-cred-mech
or
use-auth-secret
to match your credential strategy • install valid TLS certs if using TLS ports 6) Flutter client • Initialize SDK before creating Room (v2.6.0):
Copy code
await LiveKitClient.initialize(
  iceServers: [
    RTCIceServer(
      urls: ['turns:<http://your-public-domain.com:5349?transport=tcp'|your-public-domain.com:5349?transport=tcp'>],
      username: 'turn-username',
      credential: 'turn-password',
      credentialType: RTCIceCredentialType.password,
    ),
  ],
);
If you want, paste your full (redacted)
config.yaml
and your
turnserver.conf
and I’ll: • confirm exact keys supported by your LiveKit binary, and • produce a merged
config.yaml
tuned for your deployment. Which would you prefer? Sources: Running LiveKit on AWS | Firewall Tips | client-sdk-flutter | livekit/livekit_composite | DeepWiki | How does end-of-utterance detection work in conversations?