how do I enable chrome sandboxing while deploying ...
# ask-ai
m
how do I enable chrome sandboxing while deploying egress using helm charts
t
Thanks for reaching out, arijit. 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 couple quick clarifying questions so I give the correct Helm changes: 1. Are you using the official LiveKit egress Helm chart or a custom chart/template? 2. Which egress container image and version are you deploying (image name:tag)? 3. Are you running on Linux nodes with user namespaces enabled (kernel config + userns_remap) or do you need to run Chrome with the rootless/no-sandbox mode? 4. Do you control Pod security settings (can you run privileged containers or add specific capabilities like
SYS_ADMIN
)? If you prefer, paste the relevant section of your values.yaml (egress container args/env and securityContext) and I’ll suggest the exact Helm values to change.
m
1. I am using the official helm chart 2. version is 1.8.4 3. user namespaces are not enabled 4. not sure
t
Thanks — I’ll help you enable Chrome sandboxing for LiveKit Egress v1.8.4. Because user namespaces are not enabled on your nodes, Chrome’s sandbox requires elevated privileges inside the container. The simplest supported option is to run the egress container as privileged. Add the following to your Helm values.yaml (under your existing
egress
block):
Copy code
egress:
  containers:
    egress:
      securityContext:
        privileged: true
After updating values.yaml, deploy the chart (example): 1. helm upgrade --install livekit ./charts/livekit -f values.yaml 2. kubectl rollout status deployment/livekit-egress Notes and checks: • Privileged containers grant broad rights. Confirm your cluster/security policy allows privileged pods. • If privileged is not allowed, an alternative is to enable user namespaces on the host kernel (recommended for least privilege) so Chrome can use its user sandbox instead of requiring privileged mode. • If you’re unsure whether privileged pods are permitted, tell me your cluster type (EKS/GKE/AKS/self-managed) and I’ll suggest cluster-specific steps or a non-privileged alternative. Sources: Server-side | livekit/livekit_composite | DeepWiki
m
these are the logs I am getting and recording is stopping as soon as it starts livekit-egress-sandbox-6d674f9bff-cspz8
t
Thanks — I can help troubleshoot the pod stopping immediately. Please provide the pod logs and a few pod details so I can pinpoint the cause: 1. Full logs from the failing pod: ◦ Run and paste output of: ▪︎
kubectl logs livekit-egress-sandbox-6d674f9bff-cspz8 --all-containers --timestamps
▪︎ If the pod restarts, include previous logs: add
--previous
. 1. Pod description and events: ◦
kubectl describe pod livekit-egress-sandbox-6d674f9bff-cspz8
1. The egress container security/privileges from your values.yaml (or what you applied): ◦ The
egress.containers.egress.securityContext
block you used. 1. The egress command/args or recording target (if customized): ◦ Any
args
,
env
, or
command
overrides for the egress container. If you prefer, paste the relevant excerpts (logs + describe + securityContext) and I’ll analyze them and give exact changes or fixes.
m
2025-12-27T125016.576Z INFO egress server/server_rpc.go:54 request received {"nodeID": "NE_QHudotxgsVtb", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy"} 2025-12-27T125016.583Z INFO egress server/server_rpc.go:71 request validated {"nodeID": "NE_QHudotxgsVtb", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy", "requestType": "room_composite", "outputType": "file", "room": "dispatch-audio-call-04447861-abb8-444f-823f-a587524b14fa", "request": {"RoomComposite":{"room_name":"dispatch-audio-call-04447861-abb8-444f-823f-a587524b14fa","audio_only":true,"Output":null,"Options":null,"file_outputs":[{"file_type":2,"filepath":"30175f6c-9219-40f9-893d-1ca4917d1c07/04447861-abb8-444f-823f-a587524b14fa.ogg","Output":{"S3":{"access_key":"{access_key}","secret":"{secret}","region":"us-east-1","bucket":"sandbox-featherhq-audio-recordings"}}}]}}} 2025-12-27T125016.610Z DEBUG egress server/main.go:177 handler launched {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy"} 2025-12-27T125016.610Z INFO egress redis/redis.go:142 connecting to redis {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy", "simple": true, "addr": "livekit-redis-sandbox-redis-headless.default.svc.cluster.local:6379"} 2025-12-27T125016.622Z DEBUG egress source/web.go:147 creating pulse sink {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy"} 2025-12-27T125016.636Z DEBUG egress source/web.go:171 creating X display {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy", "display": ":686099608", "dims": "1280x720x24"} 2025-12-27T125016.639Z DEBUG egress source/web.go:202 launching chrome {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy", "url": "http://localhost:7980/?layout=&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NjY5MjYyMTYsImlzcyI6IkFQSTI1NTEwZTI5ODJjMDMwZDciLCJraW5kIjoiZWdyZXNzIiwibmJmIjoxNzY2ODM5ODE2LCJzdWIiOiJFR18zbW1FOXBSM3FkdnkiLCJ2aWRlbyI6eyJjYW5QdWJsaXNoIjpmYWxzZSwiY2FuUHVibGlzaERhdGEiOmZhbHNlLCJjYW5TdWJzY3JpYmUiOnRydWUsImhpZGRlbiI6dHJ1ZSwicmVjb3JkZXIiOnRydWUsInJvb20iOiJkaXNwYXRjaC1hdWRpby1jYWxsLTA0NDQ3ODYxLWFiYjgtNDQ0Zi04MjNmLWE1ODc1MjRiMTRmYSIsInJvb21Kb2luIjp0cnVlfX0.d8_MtIMscFQESAVkg34PXR74kJIujN-fnlcmQVdZOsg&url=ws%3A%2F%2Flivekit.default.svc.cluster.local%3A7880", "sandbox": false, "insecure": true} 00000.652616597 32 0x55641e103040 WARN cudaloader gstcudaloader.c169gst_cuda_load_library: Could not open library libcuda.so.1, libcuda.so.1: cannot open shared object file: No such file or directory 00000.652662198 32 0x55641e103040 WARN nvcodec plugin.c94plugin_init: Failed to load cuda library 00000.784912180 32 0x55641e103040 WARN default gstsfelement.c97gst_sf_create_audio_template_caps: format 0x120000: 'AVR (Audio Visual Research)' is not mapped 00000.784946341 32 0x55641e103040 WARN default gstsfelement.c97gst_sf_create_audio_template_caps: format 0x180000: 'CAF (Apple Core Audio File)' is not mapped 00000.784954861 32 0x55641e103040 WARN default gstsfelement.c97gst_sf_create_audio_template_caps: format 0x100000: 'HTK (HMM Tool Kit)' is not mapped 00000.784969251 32 0x55641e103040 WARN default gstsfelement.c97gst_sf_create_audio_template_caps: format 0xc0000: 'MAT4 (GNU Octave 2.0 / Matlab 4.2)' is not mapped 00000.784977491 32 0x55641e103040 WARN default gstsfelement.c97gst_sf_create_audio_template_caps: format 0xd0000: 'MAT5 (GNU Octave 2.1 / Matlab 5.0)' is not mapped 00000.784985251 32 0x55641e103040 WARN default gstsfelement.c97gst_sf_create_audio_template_caps: format 0x210000: 'MPC (Akai MPC 2k)' is not mapped 00000.784992201 32 0x55641e103040 WARN default gstsfelement.c97gst_sf_create_audio_template_caps: format 0x230000: 'MPEG-1/2 Audio' is not mapped 00000.785000531 32 0x55641e103040 WARN default gstsfelement.c97gst_sf_create_audio_template_caps: format 0xe0000: 'PVF (Portable Voice Format)' is not mapped 00000.785008581 32 0x55641e103040 WARN default gstsfelement.c97gst_sf_create_audio_template_caps: format 0x160000: 'SD2 (Sound Designer II)' is not mapped 00000.785019862 32 0x55641e103040 WARN default gstsfelement.c97gst_sf_create_audio_template_caps: format 0x190000: 'WVE (Psion Series 3)' is not mapped 2025-12-27T125019.072Z DEBUG egress uploader/s3.go:79 setting S3 config {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy", "maxRetries": 3, "maxDelay": "5s", "minDelay": "100ms"} 2025-12-27T125019.093Z DEBUG egress gstreamer/bin.go:66 adding src audio to pipeline {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy"} 2025-12-27T125019.097Z DEBUG egress gstreamer/bin.go:72 adding sink file to pipeline {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy"} 2025-12-27T125019.099Z DEBUG egress pipeline/controller.go:214 waiting for start signal {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy"} 2025-12-27T125019.130Z INFO egress source/web.go:283 chrome: END_RECORDING {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy"} 2025-12-27T125019.130Z DEBUG egress pipeline/controller.go:386 sending EOS {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy"} 2025-12-27T125019.131Z DEBUG egress gstreamer/state.go:75 pipeline state building -> stopping {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy"} 2025-12-27T125019.131Z DEBUG egress pipeline/controller.go:439 closing source {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy"} 2025-12-27T125019.131Z DEBUG egress source/web.go:113 closing chrome {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy"} 2025-12-27T125019.132Z DEBUG egress gstreamer/state.go:75 pipeline state stopping -> finished {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy"} 2025-12-27T125019.217Z DEBUG egress source/web.go:119 closing X display {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy"} 2025-12-27T125019.223Z DEBUG egress source/web.go:126 unloading pulse module {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy"} 2025-12-27T125019.230Z DEBUG egress sink/file.go:73 removing temporary directory {"nodeID": "NE_QHudotxgsVtb", "handlerID": "EGH_858wrQotULaZ", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy", "path": "/home/egress/tmp/EG_3mmE9pR3qdvy/"} 2025-12-27T125019.237Z INFO egress service/io.go:75 egress updated {"nodeID": "NE_QHudotxgsVtb", "clusterID": "", "egressID": "EG_3mmE9pR3qdvy", "requestType": "room_composite", "outputType": "file", "status": "EGRESS_ABORTED"}
t
Thanks for the logs — I’m looking at the sequence where Chrome is launched and immediately an END_RECORDING is emitted, causing the pipeline to stop and the egress to abort. What's obvious from the logs • Chrome is launched with
sandbox: false, insecure: true
in the launch info. • GStreamer starts audio pipeline work, then the code logs
chrome: END_RECORDING
almost immediately and sends EOS, which closes the pipeline and aborts the egress. • There are warnings about missing
libcuda.so.1
(video hardware accel; not fatal for audio). • The rapid END_RECORDING indicates Chrome (or the headless page it loads) is exiting or failing to start a working media session. Next steps to troubleshoot and fix 1. Confirm securityContext you applied ◦ Show me the
egress.containers.egress.securityContext
from your values.yaml (or confirm if you set
privileged: true
). If not set, Chrome’s sandbox may be failing and the egress may be forcing end of recording. 1. Check container stdout for Chrome/renderer errors ◦ Run:
kubectl logs livekit-egress-sandbox-6d674f9bff-cspz8 --container egress --timestamps
◦ Look for Chrome-specific errors (renderer crash, missing sandbox files, or permission errors). Paste any lines that mention "crash", "sandbox", "render", or "DevTools listening". 1. If privileged is not enabled ◦ Set privileged true in values.yaml:
Copy code
egress:
       containers:
         egress:
           securityContext:
             privileged: true
• Re-deploy the chart and try again. Privileged resolves Chrome sandbox issues when user namespaces are not enabled. 1. If you cannot use privileged pods ◦ Two alternatives: ▪︎ Enable user namespaces (userns) on host kernel so Chrome can use user sandbox (recommended least-privilege). ▪︎ Launch Chrome with no-sandbox flags (less secure). If you want this quick workaround, tell me and I’ll provide the exact values.yaml snippet that sets the egress container args to include
--no-sandbox --disable-setuid-sandbox
. 1. Check the page URL/connectivity the egress loads ◦ The log shows it loads a local URL with a token. Confirm the internal web server at port 7980 is healthy and serving the expected page. From inside the pod you can curl that URL to confirm it returns HTML:
kubectl exec -it pod -- curl -sS <http://localhost:7980/>
1. Share any Chrome error lines ◦ If you paste Chrome-specific stderr lines from the logs I’ll pinpoint whether it’s a sandbox permission error, an immediate page failure, or a networking/auth token issue. What I recommend now • First, confirm whether you set
privileged: true
. If not, try enabling it and redeploy. If you can’t, paste the egress container logs (full Chrome stderr lines) and I’ll give the exact values.yaml change for
--no-sandbox
or further diagnostics. Tell me which option you want to try and paste the
securityContext
or Chrome error lines and I’ll provide the exact helm values and commands.
m
k get pod livekit-egress-sandbox-6d674f9bff-cspz8 -o jsonpath='{.spec.containers[0].securityContext}' □ ai-texting △ feat/web-ch⋯⎪◃◈⎥ pkg ◨ 0.1.0 py ⌉⌊ 3.14.0on ☁️ rezodec@gmail.com 18:2k '{.spec.containers[0].securityContext}' using remote kubeconfig {"capabilities":{"add":["SYS_ADMIN"]}}%
t
Thanks — that capability output is useful. I can see the container currently only has
SYS_ADMIN
added, not full privileged mode. That likely still prevents Chrome’s sandbox from working, which explains the immediate END_RECORDING. Next steps (choose one): 1) Enable privileged mode (recommended when userns is off) • Set this in your values.yaml:
Copy code
egress:
  containers:
    egress:
      securityContext:
        privileged: true
• Then redeploy:
helm upgrade --install livekit ./charts/livekit -f values.yaml
• Verify pod comes up and try the recording. 2) If you cannot use privileged pods, run Chrome without the sandbox (less secure) • Add Chrome args to the egress container to disable sandbox:
Copy code
egress:
  containers:
    egress:
      args:
        - "--no-sandbox"
        - "--disable-setuid-sandbox"
• Redeploy and test. 3) Longer-term secure option • Enable user namespaces on your hosts so Chrome can use its user sandbox (I can give host-specific steps if you tell me your cluster type: EKS/GKE/AKS/self-managed). If you want, try option 1 first and paste the new pod logs if it still aborts. If you prefer option 2, tell me and I’ll provide the exact Helm snippet to merge into your chart (or the combined snippet that sets both
privileged: true
and the args).
m
I have set --no-sandbox still there is no audio recording
Copy code
2025-12-27T13:06:31.246Z        DEBUG   egress  server/main.go:177      handler launched        {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632"}
2025-12-27T13:06:31.247Z        INFO    egress  redis/redis.go:142      connecting to redis     {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632", "simple": true, "addr": "livekit-redis-sandbox-redis-headless.default.svc.cluster.local:6379"}
2025-12-27T13:06:31.260Z        DEBUG   egress  source/web.go:147       creating pulse sink     {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632"}
2025-12-27T13:06:31.282Z        DEBUG   egress  source/web.go:171       creating X display      {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632", "display": ":1296087267", "dims": "1280x720x24"}
2025-12-27T13:06:31.283Z        DEBUG   egress  source/web.go:202       launching chrome        {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632", "url": "<http://localhost:7980/?layout=&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NjY5MjcxOTEsImlzcyI6IkFQSTI1NTEwZTI5ODJjMDMwZDciLCJraW5kIjoiZWdyZXNzIiwibmJmIjoxNzY2ODQwNzkxLCJzdWIiOiJFR19IdXg0dWJodko2MzIiLCJ2aWRlbyI6eyJjYW5QdWJsaXNoIjpmYWxzZSwiY2FuUHVibGlzaERhdGEiOmZhbHNlLCJjYW5TdWJzY3JpYmUiOnRydWUsImhpZGRlbiI6dHJ1ZSwicmVjb3JkZXIiOnRydWUsInJvb20iOiJkaXNwYXRjaC1hdWRpby1jYWxsLTU1MWUxYTAzLTYxYTgtNGJmNS1hYWZjLTlhNzdiYTk1MTRlNiIsInJvb21Kb2luIjp0cnVlfX0.Bcu0oKHuPSA2oWjMn3nvC69uGrRZCDa1YlwGjJvEnf8&url=ws%3A%2F%2Flivekit.default.svc.cluster.local%3A7880>", "sandbox": false, "insecure": true}
0:00:00.965413721    30 0x557bd038f040 WARN              cudaloader gstcudaloader.c:169:gst_cuda_load_library: Could not open library libcuda.so.1, libcuda.so.1: cannot open shared object file: No such file or directory
0:00:00.965458192    30 0x557bd038f040 WARN                 nvcodec plugin.c:94:plugin_init: Failed to load cuda library
0:00:01.072865794    30 0x557bd038f040 WARN                 default gstsfelement.c:97:gst_sf_create_audio_template_caps: format 0x120000: 'AVR (Audio Visual Research)' is not mapped
0:00:01.072921954    30 0x557bd038f040 WARN                 default gstsfelement.c:97:gst_sf_create_audio_template_caps: format 0x180000: 'CAF (Apple Core Audio File)' is not mapped
0:00:01.072931075    30 0x557bd038f040 WARN                 default gstsfelement.c:97:gst_sf_create_audio_template_caps: format 0x100000: 'HTK (HMM Tool Kit)' is not mapped
0:00:01.072945915    30 0x557bd038f040 WARN                 default gstsfelement.c:97:gst_sf_create_audio_template_caps: format 0xc0000: 'MAT4 (GNU Octave 2.0 / Matlab 4.2)' is not mapped
0:00:01.072953135    30 0x557bd038f040 WARN                 default gstsfelement.c:97:gst_sf_create_audio_template_caps: format 0xd0000: 'MAT5 (GNU Octave 2.1 / Matlab 5.0)' is not mapped
0:00:01.072960925    30 0x557bd038f040 WARN                 default gstsfelement.c:97:gst_sf_create_audio_template_caps: format 0x210000: 'MPC (Akai MPC 2k)' is not mapped
0:00:01.073036956    30 0x557bd038f040 WARN                 default gstsfelement.c:97:gst_sf_create_audio_template_caps: format 0x230000: 'MPEG-1/2 Audio' is not mapped
0:00:01.073047076    30 0x557bd038f040 WARN                 default gstsfelement.c:97:gst_sf_create_audio_template_caps: format 0xe0000: 'PVF (Portable Voice Format)' is not mapped
0:00:01.073062126    30 0x557bd038f040 WARN                 default gstsfelement.c:97:gst_sf_create_audio_template_caps: format 0x160000: 'SD2 (Sound Designer II)' is not mapped
0:00:01.073072776    30 0x557bd038f040 WARN                 default gstsfelement.c:97:gst_sf_create_audio_template_caps: format 0x190000: 'WVE (Psion Series 3)' is not mapped
2025-12-27T13:06:33.897Z        DEBUG   egress  uploader/s3.go:79       setting S3 config       {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632", "maxRetries": 3, "maxDelay": "5s", "minDelay": "100ms"}
2025-12-27T13:06:33.928Z        DEBUG   egress  gstreamer/bin.go:66     adding src audio to pipeline    {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632"}
2025-12-27T13:06:33.930Z        DEBUG   egress  gstreamer/bin.go:72     adding sink file to pipeline    {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632"}
2025-12-27T13:06:33.937Z        DEBUG   egress  pipeline/controller.go:214      waiting for start signal        {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632"}
2025-12-27T13:06:33.978Z        INFO    egress  source/web.go:283       chrome: END_RECORDING   {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632"}
2025-12-27T13:06:33.980Z        DEBUG   egress  pipeline/controller.go:386      sending EOS     {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632"}
2025-12-27T13:06:33.982Z        DEBUG   egress  gstreamer/state.go:75   pipeline state building -> stopping     {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632"}
2025-12-27T13:06:33.982Z        DEBUG   egress  gstreamer/state.go:75   pipeline state stopping -> finished     {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632"}
2025-12-27T13:06:33.983Z        DEBUG   egress  pipeline/controller.go:439      closing source  {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632"}
2025-12-27T13:06:33.983Z        DEBUG   egress  source/web.go:113       closing chrome  {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632"}
2025-12-27T13:06:34.059Z        DEBUG   egress  source/web.go:119       closing X display       {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632"}
2025-12-27T13:06:34.072Z        DEBUG   egress  source/web.go:126       unloading pulse module  {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632"}
2025-12-27T13:06:34.087Z        DEBUG   egress  sink/file.go:73 removing temporary directory    {"nodeID": "NE_7prTTkP5gzkX", "handlerID": "EGH_PjX6y7onLrtH", "clusterID": "", "egressID": "EG_Hux4ubhvJ632", "path": "/home/egress/tmp/EG_Hux4ubhvJ632/"}
2025-12-27T13:06:34.096Z        INFO    egress  service/io.go:75        egress updated  {"nodeID": "NE_7prTTkP5gzkX", "clusterID": "", "egressID": "EG_Hux4ubhvJ632", "requestType": "room_composite", "outputType": "file", "status": "EGRESS_ABORTED"}
t
Thanks — I see you added
--no-sandbox
but the egress still immediately logs
chrome: END_RECORDING
and aborts. Quick next checks so I can pinpoint the failure: 1) Paste the egress container environment and args: • Run and share output: ◦ kubectl get pod livekit-egress-sandbox-6d674f9bff-cspz8 -o jsonpath='{.spec.containers[0].env}' ◦ kubectl get pod livekit-egress-sandbox-6d674f9bff-cspz8 -o jsonpath='{.spec.containers[0].args}' 2) Fetch Chrome stdout/stderr lines after launch (they often show why the page closed): • kubectl logs livekit-egress-sandbox-6d674f9bff-cspz8 --container egress --timestamps | sed -n '1,200p' ◦ If logs rotate/are truncated, include
--previous
. 3) Confirm Pulse/ALSA access inside the pod: • Exec into the pod and check Pulse modules and devices: ◦ kubectl exec -it livekit-egress-sandbox-6d674f9bff-cspz8 -- ls -l /dev/snd || true ◦ kubectl exec -it livekit-egress-sandbox-6d674f9bff-cspz8 -- pactl list short modules || true 4) As a test, try privileged mode (if allowed) — this resolves many sandbox/audio permission issues: • In values.yaml set:
Copy code
egress:
  containers:
    egress:
      securityContext:
        privileged: true
Redeploy and retry; if you prefer I can supply the exact helm command snippets. Tell me the outputs from steps 1–3 and whether you can enable privileged mode; I’ll analyze and give the exact fix.
m
◎ k get pod livekit-egress-sandbox-75587cc5c5-sxxgr -o jsonpath='{.spec.containers[0].env}' □ ai-texting △ feat/web-ch⋯⎪◃◈⎥ pkg ◨ 0.1.0 py ⌉⌊ 3.14.0on ☁️ rezodec@gmail.com 18:38 using remote kubeconfig [{"name":"EGRESS_CONFIG_BODY","valueFrom":{"configMapKeyRef":{"key":"config.yaml","name":"livekit-egress-sandbox"}}}]% ◄ 5s ◎ kk get pod livekit-egress-sandbox-75587cc5c5-sxxgr -o jsonpath'{.spec.containers[0].args}' □ ai-texting △ feat/web-ch⋯⎪◃◈⎥ pkg ◨ 0.1.0 py ⌉⌊ 3.14.0on ☁️ rezodec@gmail.com 18:39 using remote kubeconfig no output there are no logs related from chrome stdout/err apart from the ones I shared earlier
t
Thanks — helpful details. From the logs and your outputs it looks like Chrome is starting but immediately sending END_RECORDING. Before I can pinpoint the cause, please provide these exact items (paste outputs here): 1) The egress ConfigMap contents referenced by EGRESS_CONFIG_BODY: • kubectl get configmap livekit-egress-sandbox -o yaml 2) The HTTP response from the internal page Chrome loads: • kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- curl -sS -D - "http://localhost:7980/" | sed -n '1,120p' • And (with the token from your logs, redact secrets): kubectl exec -it <pod> -- curl -sS "http://localhost:7980/?token=&lt;trimmed-token&gt;" | sed -n '1,120p' 3) Full egress container logs (no truncation): • kubectl logs livekit-egress-sandbox-75587cc5c5-sxxgr --container egress --timestamps | sed -n '1,400p' • If pod restarted, add --previous 4) Verify Pulse/ALSA and audio devices inside the pod: • kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- ls -l /dev/snd || true • kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- pactl list short modules || true • kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- pactl list short sinks || true 5) Confirm room audio activity: • Are there active publishers in the room at the time of the egress request? (yes/no) 6) Where you added `--no-sandbox`: • Paste the exact Helm values snippet or the container args you used to set
--no-sandbox
(values.yaml egress container block). If you prefer, paste the ConfigMap and the first ~400 lines of the egress container logs and I’ll start analyzing immediately. Sources: Examples | livekit/livekit_composite | DeepWiki | Universal Egress | Server-side | livekit/livekit_composite | DeepWiki
m
Copy code
apiVersion: v1
data:
  config.yaml: |
    api_key: [redacted]
    api_secret: [redacted]
    containers:
      egress:
        args:
        - --no-sandbox
        - --disable-setuid-sandbox
        securityContext:
          privileged: true
    health_port: 8080
    insecure: true
    log_level: debug
    prometheus_port: 9090
    redis:
      address: [redacted]
      db: 0
      password: [redacted]
      username: default
    s3:
      access_key: [redacted]
      bucket: [redacted]
      secret: [redacted]
    ws_url: [redacted]
kind: ConfigMap
metadata:
  annotations:
    <http://meta.helm.sh/release-name|meta.helm.sh/release-name>: livekit-egress-sandbox
    <http://meta.helm.sh/release-namespace|meta.helm.sh/release-namespace>: default
  creationTimestamp: "2025-12-23T06:22:16Z"
  labels:
    <http://app.kubernetes.io/managed-by|app.kubernetes.io/managed-by>: Helm
  name: livekit-egress-sandbox
  namespace: default
  resourceVersion: "10008282"
  uid: b0dca0f7-b154-4a5d-ba77-949131a94c78
t
Thanks — I can see your ConfigMap now (Chrome args and
privileged: true
are set). Good. Next I’ll focus on confirming whether the page Chrome loads can connect and whether PulseAudio/audio devices are available. Please run these from your machine and paste the outputs: 1) Check the page HTML Chrome loads (redact token): kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- curl -sS "http://localhost:7980/?token=&lt;redacted-token&gt;" | head -n 60 2) Verify the egress pod can reach the LiveKit websocket (replace with your ws_url from the ConfigMap, redact secrets): kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- curl -v "<ws_url>" 3) Confirm Pulse/ALSA devices and Pulse modules: kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- ls -l /dev/snd || true kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- pactl list short modules || true kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- pactl list short sinks || true 4) Full egress container logs (300–500 lines) in case Chrome stderr appears later: kubectl logs livekit-egress-sandbox-75587cc5c5-sxxgr --container egress --timestamps | sed -n '1,500p' Also confirm: is there an active audio publisher in the room when you start egress? (yes/no) Send those outputs and I’ll analyze them and give the exact next fix.
m
egress@livekit-egress-sandbox-75587cc5c5-sxxgr:/$ curl -sS "http://localhost:7980/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NjY5MjcxOTEsImlzcyI6IkFQSTI1NTEwZTI5ODJjMDMwZDciLCJraW5kIjoiZWdyZXNzIiwibmJmIjoxNzY2ODQwNzkxLCJzdWIiOiJFR19IdXg0dWJodko2MzIiLCJ2aWRlbyI6eyJjYW5QdWJsaXNoIjpmYWxzZSwiY2FuUHVibGlzaERhdGEiOmZhbHNlLCJjYW5TdWJzY3JpYmUiOnRydWUsImhpZGRlbiI6dHJ1ZSwicmVjb3JkZXIiOnRydWUsInJvb20iOiJkaXNwYXRjaC1hdWRpby1jYWxsLTU1MWUxYTAzLTYxYTgtNGJmNS1hYWZjLTlhNzdiYTk1MTRlNiIsInJvb21Kb2luIjp0cnVlfX0.Bcu0oKHuPSA2oWjMn3nvC69uGrRZCDa1YlwGjJvEnf8" | head -n 60 <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Recording interface for LiveKit"/><link rel="apple-touch-icon" href="/logo.png"/><link rel="manifest" href="/manifest.json"/><title>LiveKit Egress</title><script defer="defer" src="/static/js/main.4c56458c.js"></script><link href="/static/css/main.2fa0e32d.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>egress@livekit-egress-sandbox-75587cc5c5-sxxgr:/$
t
Thanks — the page loads fine, which rules out a bad token/page. Next checks to find why Chrome immediately ends recording: Please run and paste outputs (or confirm results): 1) Verify websocket connectivity from the pod to LiveKit (replace with your ws_url from the ConfigMap): • kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- curl -v "ws://livekit.default.svc.cluster.local:7880" (If your ws_url uses wss, use that host/port.) 2) Confirm PulseAudio and ALSA devices inside the pod: • kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- ls -l /dev/snd || true • kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- pactl list short modules || true • kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- pactl list short sinks || true 3) Verify there is active audio in the room at egress start (yes/no). 4) Pull full recent logs (500 lines) to catch any delayed Chrome errors: • kubectl logs livekit-egress-sandbox-75587cc5c5-sxxgr --container egress --timestamps | sed -n '1,500p' If websocket fails or PulseAudio devices are missing, that will explain END_RECORDING. Paste the outputs and I’ll tell you the exact fix.
m
Copy code
egress@livekit-egress-sandbox-75587cc5c5-sxxgr:/$ ls -l /dev/snd || true
ls: cannot access '/dev/snd': No such file or directory
egress@livekit-egress-sandbox-75587cc5c5-sxxgr:/$ pactl list short modules || true
0       module-device-restore
1       module-stream-restore
2       module-card-restore
3       module-augment-properties
4       module-switch-on-port-available
5       module-switch-on-connect
6       module-udev-detect
7       module-native-protocol-unix
8       module-default-device-restore
9       module-always-sink
11      module-intended-roles
12      module-suspend-on-idle
13      module-systemd-login
14      module-position-event-sounds
15      module-role-cork
16      module-snap-policy
17      module-filter-heuristics
18      module-filter-apply
20      module-null-sink        sink_name=auto_null sink_properties='device.description="Dummy Output"'
egress@livekit-egress-sandbox-75587cc5c5-sxxgr:/$ pactl list short sinks || true
2       auto_null       module-null-sink.c      s16le 2ch 44100Hz       SUSPENDED
egress@livekit-egress-sandbox-75587cc5c5-sxxgr:/$
t
Thanks — the outputs show no ALSA devices under /dev/snd and PulseAudio only has a null sink. That explains the immediate END_RECORDING: Chrome + the virtual audio pipeline start, but there’s no real audio device/input to capture, so the egress closes. What you can do next (pick one): 1) Easiest / least-effort — provide the host's ALSA devices to the pod (requires cluster permission) • In your values.yaml add a device mount and privileged or appropriate capabilities:
Copy code
egress:
  containers:
    egress:
      securityContext:
        privileged: true
      volumeMounts:
        - name: dev-snd
          mountPath: /dev/snd
  volumes:
    - name: dev-snd
      hostPath:
        path: /dev/snd
        type: Directory
• Redeploy the chart and verify
/dev/snd
exists inside the pod and Pulse shows real sinks. 2) If you cannot mount host devices / do not want privileged: • Use a virtual sound source that PulseAudio exposes and ensure the egress captures it. You must run a real audio producer inside the pod or route room audio into Pulse (complex). • Alternative: run a small sidecar that connects to LiveKit and feeds audio into Pulse in the egress pod (I can provide a sample sidecar config if you want this route). 3) Quick test to confirm device fix • After applying option 1 and redeploying, confirm: ◦ kubectl exec -it <pod> -- ls -l /dev/snd ◦ kubectl exec -it <pod> -- pactl list short sinks ◦ Start egress and check logs — END_RECORDING should not occur immediately. Notes and safety: • Mounting /dev/snd and running privileged grants host device access; ensure this fits your security policy. • If you need least-privilege, enabling user namespaces on hosts + privileged capability or a more restricted set of capabilities may be possible but is more involved. Tell me which option you want to try (mount host device or sidecar/virtual source) and I’ll give the exact Helm values snippet tailored to your chart and the verification commands.
m
even after doing device mount and privileged or appropriate capabilities, the same issue persists
t
Thanks — I appreciate you trying the device mount and privileged mode. From the previous checks, PulseAudio only has a null sink and /dev/snd was previously absent. That means there may be no Pulse source (monitor) for the egress pipeline to capture, which causes Chrome/egress to end recording immediately. Please run these three quick checks inside the egress pod and paste results: 1. Is PulseAudio running? kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- pgrep -f pulseaudio || echo "no pulseaudio" 1. List Pulse sources (show monitor sources): kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- pactl list short sources || true 1. List sinks (to see monitor names): kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- pactl list short sinks || true If there is no monitor source (e.g., no *.monitor entry), create a null sink to provide a monitor source and set the egress to use it: A) Create null sink (temporary test) kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- pactl load-module module-null-sink sink_name=egress_sink sink_properties="device.description='Egress Sink'" B) Verify monitor created kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- pactl list short sources | grep egress_sink || true C) Configure egress to use that source by adding an env var in your ConfigMap values (example): egress: containers: egress: env: • name: PULSE_SOURCE value: "egress_sink.monitor" Apply the config and redeploy the chart, then start egress and share the first ~200 lines of egress logs if it still aborts. If PulseAudio is not running at all, run: kubectl exec -it livekit-egress-sandbox-75587cc5c5-sxxgr -- pulseaudio --start --log-level=debug Send the outputs of the three checks above and I’ll provide the exact helm/values.yaml snippet to persist the null sink and env var in your deployment.
m
pgrep -f pulseaudio || echo "no pulseaudio" 11 2 auto_null.monitor module-null-sink.c s16le 2ch 44100Hz SUSPENDED 2 auto_null module-null-sink.c s16le 2ch 44100Hz SUSPENDED
t
Thanks — you’ve done great troubleshooting. From the data you shared: • PulseAudio is running, but only a null sink/monitor exists and they are in SUSPENDED state. • There are no ALSA devices under /dev/snd inside the pod. • With no active audio source (monitor not active), the egress captures no audio and immediately logs END_RECORDING. To move forward, please try the steps below and share results: 1) Confirm there is an active audio publisher in the room when you start egress (yes/no). 2) Reactivate/create an active monitor source (quick test) • Run inside the egress pod: ◦ pactl load-module module-null-sink sink_name=egress_sink sink_properties=device.description="Egress_Sink" ◦ pactl set-default-source egress_sink.monitor ◦ pactl list short sources • Then start the egress and paste the first 200 lines of logs. 3) If the sink remains SUSPENDED, generate audio to wake it: • Play a short test sound inside the pod: ◦ paplay /usr/share/sounds/alsa/Front_Center.wav || echo "paplay failed" • Re-check sinks and sources: pactl list short sinks; pactl list short sources 4) If you want the pod to always have an active monitor, add an init command (values.yaml) to create the null sink at startup:
Copy code
egress:
  containers:
    egress:
      securityContext:
        privileged: true
      args: []
      lifecycle:
        postStart:
          exec:
            command:
              - /bin/sh
              - -c
              - >
                pactl load-module module-null-sink sink_name=egress_sink sink_properties=device.description="Egress_Sink" || true &&
                pactl set-default-source egress_sink.monitor || true
5) If you prefer using host ALSA instead of null sink, confirm /dev/snd is mounted and that Pulse picks up hw devices (ls -l /dev/snd ; pactl list short sinks). If ALSA devices appear but Pulse still shows SUSPENDED, try restarting PulseAudio: • pulseaudio --kill || true • pulseaudio --start Send me: • Answer to (1), • Output of
pactl list short sources
after step 2, • First ~200 lines of egress logs if it still aborts. I’ll analyze those and give the exact values.yaml snippet or alternative sidecar approach as needed. Sources: Universal Egress | Universal Egress
m
egress@livekit-egress-sandbox-85d4dcb5bb-l7b2r:/$ pactl load-module module-null-sink sinkname=egresssink sinkproperties=device.description="EgressSink" Failure: Module initialization failed
t
Thanks — that failure explains why audio stays unavailable. When
pactl load-module module-null-sink ...
fails it’s usually because PulseAudio cannot create/load modules due to one of these reasons: missing module binary, insufficient permissions for the Pulse daemon, or Pulse running in a restricted mode (system vs per-user). Let’s narrow and fix it. First, quick checks inside the pod — paste outputs (one-liners are fine): 1) pulseaudio process info: ps -ef | sed -n '1,120p' | grep -i pulseaudio 2) Pulse daemon mode and config: pulseaudio --version pulseaudio --check && echo running || echo not-running 3) Pulse logs (show recent lines): journalctl --no-pager -u pulseaudio --since "5 minutes ago" 2>/dev/null || true if journalctl not available, show daemon stderr if present: ls -l /tmp/pulse* /root/.config/pulse || true Based on results, try these fixes (pick applicable): A — If Pulse runs as a system-wide limited user (module loading blocked) • Restart Pulse in per-user mode inside the container and then load null sink: pulseaudio --kill || true pulseaudio --start --log-level=debug pactl load-module module-null-sink sink_name=egress_sink sink_properties=device.description="EgressSink" pactl set-default-source egress_sink.monitor B — If Pulse is running but module load fails due to missing module support • Confirm modules exist: pactl list short modules || true ls /usr/lib/pulse-*/modules || ls /usr/lib/pulse/modules || true • If modules are missing in the image, you must use an image with PulseAudio packages (or install them). Use an image that includes PulseAudio (or build one) so
module-null-sink
exists. C — If permissions cause failure (common with non-privileged containers) • Ensure the container runs privileged or with required capabilities and that /dev/shm and /dev/snd are accessible: ◦ securityContext.privileged: true (or at least add SYS_ADMIN + CAP_IPC_LOCK) ◦ mount /dev/snd (hostPath) and add emptyDir mount for /dev/shm: volumes: • name: dev-snd hostPath: path: /dev/snd • name: dshm emptyDir: medium: Memory sizeLimit: 1Gi volumeMounts: • name: dev-snd mountPath: /dev/snd • name: dshm mountPath: /dev/shm D — Workaround: pre-create null sink from an init container (ensures Pulse has module loaded before egress starts) • Add an initContainer that starts a short-lived Pulse instance and loads module, or runs
pactl
against the pod’s socket. Example lifecycle postStart (values.yaml):
Copy code
egress:
  containers:
    egress:
      lifecycle:
        postStart:
          exec:
            command:
              - /bin/sh
              - -c
              - |
                pulseaudio --start --exit-idle-time=-1 || true
                sleep 1
                pactl load-module module-null-sink sink_name=egress_sink sink_properties=device.description="Egress_Sink" || true
                pactl set-default-source egress_sink.monitor || true
Finally, ensure LiveKit picks the right Pulse source: • Add env var so egress uses the monitor:
Copy code
egress:
  containers:
    egress:
      env:
        - name: PULSE_SOURCE
          value: "egress_sink.monitor"
After trying A or D, verify: • pactl list short sinks • pactl list short sources • pactl list short modules If you run one of the fixes, paste the outputs of those three
pactl
commands and the first ~200 lines of the egress logs. I’ll use them to confirm Pulse is active and the egress should record audio.
m
Copy code
pulseaudio process info: ps -ef | sed -n '1,120p' | grep -i pulseaudio
pulseaudio: invalid option -- 'e'
E: [pulseaudio] main.c: Failed to parse command line.
Copy code
pulseaudio --version pulseaudio --check && echo running || echo not-running
E: [pulseaudio] main.c: Too many arguments.
not-running
t
Thanks — those outputs show the pulseaudio commands were run with combined arguments by mistake. Let’s run the correct checks and then restart PulseAudio properly so you can load the null sink. Please run these exact commands (one at a time) inside the egress pod and paste outputs: 1) Check pulseaudio process: ps aux | grep -i pulseaudio | grep -v grep || echo "no pulseaudio process" 2) PulseAudio version: pulseaudio --version 2>/dev/null || true 3) Is PulseAudio running (check exit code): pulseaudio --check && echo "running" || echo "not-running" If PulseAudio is not running, start it and then load the null sink: 4) Start PulseAudio in per-user mode: pulseaudio --start --log-level=debug && sleep 1 5) Load null sink and set default source: pactl load-module module-null-sink sink_name=egress_sink sink_properties=device.description="Egress_Sink" || echo "load-module failed" pactl set-default-source egress_sink.monitor || echo "set-default-source failed" 6) Verify sinks/sources/modules: pactl list short sinks pactl list short sources pactl list short modules | sed -n '1,120p' If any command fails, paste its exact output. I’ll use that to give the next precise action (init container snippet, permissions fix, or image change).
m
egress@livekit-egress-sandbox-85d4dcb5bb-l7b2r:/$ ps aux | grep -i pulseaudio | grep -v grep || echo "no pulseaudio process" egress 11 0.0 0.2 321832 10988 ? Sl 13:18 0:00 pulseaudio -D --verbose --exit-idle-time=-1 --disallow-exit egress@livekit-egress-sandbox-85d4dcb5bb-l7b2r:/$ pulseaudio --version 2>/dev/null || true pulseaudio 16.1 egress@livekit-egress-sandbox-85d4dcb5bb-l7b2r:/$ pulseaudio --version 2>/dev/null || true pulseaudio 16.1 egress@livekit-egress-sandbox-85d4dcb5bb-l7b2r:/$ pulseaudio --check && echo "running" || echo "not-running" running egress@livekit-egress-sandbox-85d4dcb5bb-l7b2r:/$ pulseaudio --start --log-level=debug && sleep 1 D: [pulseaudio] conf-parser.c: Parsing configuration file '/etc/pulse/client.conf' D: [pulseaudio] conf-parser.c: Failed to open configuration file '/etc/pulse/client.conf.d/01-enable-autospawn.conf': No such file or directory I: [pulseaudio] main.c: Daemon startup successful. egress@livekit-egress-sandbox-85d4dcb5bb-l7b2r:/$ pactl load-module module-null-sink sinkname=egresssink sinkproperties=device.description="EgressSink" || echo "load-module failed" pactl set-default-source egress_sink.monitor || echo "set-default-source failed" Failure: Module initialization failed load-module failed pactl set-default-source egress_sink.monitor egress@livekit-egress-sandbox-85d4dcb5bb-l7b2r:/$
t
Thanks — you’re very close. The module load kept failing because the earlier command used wrong property names (
sinkname
vs
sink_name
,
sinkproperties
vs
sink_properties
). Let’s confirm the module exists and then load it with the correct syntax. Please run these exact commands inside the egress pod and paste outputs: 1) List PulseAudio modules to confirm module-null-sink is available: pactl list short modules 2) Load null sink (correct syntax) and set default source: pactl load-module module-null-sink sink_name=egress_sink sink_properties=device.description="EgressSink" || echo "load-module failed" pactl set-default-source egress_sink.monitor || echo "set-default-source failed" 3) Verify sinks/sources after loading: pactl list short sinks pactl list short sources If load-module still fails, paste the output of command (1) and the exact error from (2). I’ll provide the persistent Helm values.yaml snippet (postStart lifecycle + PULSE_SOURCE env var) once we confirm the null sink can be created.
m
egress@livekit-egress-sandbox-85d4dcb5bb-l7b2r:/$ pactl list short modules 0 module-device-restore 1 module-stream-restore 2 module-card-restore 3 module-augment-properties 4 module-switch-on-port-available 5 module-switch-on-connect 6 module-udev-detect 7 module-native-protocol-unix 8 module-default-device-restore 9 module-always-sink 11 module-intended-roles 12 module-suspend-on-idle 13 module-systemd-login 14 module-position-event-sounds 15 module-role-cork 16 module-snap-policy 17 module-filter-heuristics 18 module-filter-apply 20 module-null-sink sink_name=auto_null sink_properties='device.description="Dummy Output"' egress@livekit-egress-sandbox-85d4dcb5bb-l7b2r:/$ pactl load-module module-null-sink sinkname=egresssink sinkproperties=device.description="EgressSink" || echo "load-module failed" pactl set-default-source egresssink.monitor || echo "set-default-source failed" Failure: Module initialization failed load-module failed pactl set-default-source egresssink.monitor egress@livekit-egress-sandbox-85d4dcb5bb-l7b2r:/$
t
Thanks — good info. The failure was because of incorrect
pactl
option names. I can see a
module-null-sink
already exists as
auto_null
but is suspended. Please run these exact commands (one-by-one) inside the egress pod and paste the outputs: 1) Unload the existing auto_null module (if present) and confirm modules: pactl list short modules pactl unload-module module-null-sink || true pactl list short modules 2) Load a properly named null sink and set its monitor as default source: pactl load-module module-null-sink sink_name=egress_sink sink_properties=device.description="Egress_Sink" || echo "load-module failed" pactl set-default-source egress_sink.monitor || echo "set-default-source failed" 3) Verify sinks/sources and module state: pactl list short sinks pactl list short sources pactl list short modules If any command prints an error, paste that exact error output. Once the sink/monitor show up and are not SUSPENDED, retry the egress and share the first ~200 lines of logs if it still aborts.
m
pactl load-module module-null-sink sinkname=egresssink sinkproperties=device.description="EgressSink" || echo "load-module failed" pactl set-default-source egress_sink.monitor || echo "set-default-source failed" Failure: Module initialization failed load-module failed pactl set-default-source egress_sink.monitor egress@livekit-egress-sandbox-85d4dcb5bb-l7b2r:/$
t
Thanks — you’re almost there. The repeated failure is caused by using the wrong option names. Please run these exact commands (copy/paste each line separately) inside the egress pod: 1) Unload the existing null sink (safe if none): pactl list short modules pactl unload-module module-null-sink || true 2) Load a new null sink (correct option names) and set its monitor as the default source: pactl load-module module-null-sink sink_name=egress_sink sink_properties=device.description="Egress_Sink" pactl set-default-source egress_sink.monitor 3) Verify results: pactl list short sinks pactl list short sources pactl list short modules If any command prints an error, paste that exact error output. If the sink/monitor appear and are not SUSPENDED, start the egress and share the first ~200 lines of logs — I’ll confirm the next steps.
m
egress@livekit-egress-sandbox-85d4dcb5bb-l7b2r:/$ pactl load-module module-null-sink sinkname=egresssink sinkproperties=device.description="EgressSink" Failure: Module initialization failed
t
Thanks — I know this has been frustrating. The repeated "Module initialization failed" suggests PulseAudio cannot create a new null sink in the running daemon (not a pactl syntax problem anymore). Let’s narrow the real cause and get you a working sink. Please run these exact commands (one at a time) inside the egress pod and paste outputs here: 1) Confirm existing null-sink module details: pactl list short modules | sed -n '1,120p' 2) Show full info for the auto_null module (replace the module index from step 1 if different): pactl list modules | sed -n '1,200p' | sed -n '/module-null-sink/,/index/p' 3) Try unloading the existing null-sink module, then load a new one (run each line separately): pactl unload-module module-null-sink || true pactl load-module module-null-sink sink_name=egress_sink sink_properties=device.description="Egress_Sink" || echo "load-module failed: $?" pactl list short sinks If load still fails, run these to capture Pulse daemon errors: 4) Show Pulse startup & runtime log messages: pulseaudio --log-level=debug --log-target=file:/tmp/pulse.log --kill || true pulseaudio --start --log-level=debug --log-target=file:/tmp/pulse.log sleep 1 tail -n 200 /tmp/pulse.log || true 5) Show filesystem permissions that Pulse uses: ls -la /run/user || true ls -la /root/.config/pulse || true ls -la /tmp | grep pulse || true Why these checks: • If an existing null-sink (auto_null) is present and SUSPENDED, unloading it and reloading can succeed. • If module loading fails due to missing module files, permission issues, or Pulse running as a system-limited instance, the Pulse debug log (/tmp/pulse.log) will show the exact error. • Permission/namespace issues (missing /run/user/$UID, inaccessible cookie) commonly prevent module loading. Next steps I will give once you paste results: • If unload/load succeeds: I’ll provide a persistent lifecycle/postStart snippet to create the sink at pod start and the env var to force egress to use it. • If load fails and pulse.log shows permission or missing-module errors: I’ll provide the minimal Helm changes (privileged/volume mounts or image changes) required to fix them. Run the commands and paste outputs; I’ll take it from there.