<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: dceniac</title>
    <description>The latest articles on DEV Community by dceniac (@dceniac).</description>
    <link>https://dev.to/dceniac</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4219201%2Fdc60dee7-d996-4268-b926-ff5df9b632c8.jpg</url>
      <title>DEV Community: dceniac</title>
      <link>https://dev.to/dceniac</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dceniac"/>
    <language>en</language>
    <item>
      <title>An 8-vCPU, 32 GB server choked on a 3-person 1080p LiveKit call — the bottleneck was outbound bandwidth</title>
      <dc:creator>dceniac</dc:creator>
      <pubDate>Sun, 01 Nov 2026 08:08:52 +0000</pubDate>
      <link>https://dev.to/dceniac/an-8-vcpu-32-gb-server-choked-on-a-3-person-1080p-livekit-call-the-bottleneck-was-outbound-2a48</link>
      <guid>https://dev.to/dceniac/an-8-vcpu-32-gb-server-choked-on-a-3-person-1080p-livekit-call-the-bottleneck-was-outbound-2a48</guid>
      <description>&lt;p&gt;I recently added self-hosted video meetings to an internal product using &lt;a href="https://github.com/livekit/livekit" rel="noopener noreferrer"&gt;LiveKit&lt;/a&gt; on Docker.&lt;/p&gt;

&lt;p&gt;The VM looked generous on paper: &lt;strong&gt;8 vCPUs, 32 GB RAM, and a fixed 10 Mbps public bandwidth plan&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In practice, 1080p worked fine with two people in a room. The moment a third person joined with their camera on, video froze.&lt;/p&gt;

&lt;p&gt;Here is what happened, why I picked &lt;strong&gt;LiveKit **over **Jitsi&lt;/strong&gt;, and the bandwidth math I should have done before buying a fixed-bandwidth VM.&lt;/p&gt;

&lt;p&gt;Why LiveKit instead of Jitsi&lt;/p&gt;

&lt;p&gt;Before deploying, I compared Jitsi + Jibri against LiveKit Server + Egress, specifically looking at protocol design and recording:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Jitsi + Jibri&lt;/strong&gt;: Jibri records a room by spinning up a headless Chrome instance to render the meeting page and capturing it with &lt;code&gt;ffmpeg&lt;/code&gt;. The output looks just like a real meeting, but one Jibri instance only records one meeting at a time—scaling concurrent recordings means running one Jibri per active recorded room on top of the XMPP stack.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LiveKit&lt;/strong&gt;: Uses WebSocket signaling + WebRTC SFU, separates media routing (&lt;code&gt;livekit-server&lt;/code&gt;) from recording/streaming (&lt;code&gt;livekit/egress&lt;/code&gt;), and has an official &lt;code&gt;agents&lt;/code&gt; framework if you later add voice AI. Note that LiveKit's full-room composite Egress &lt;em&gt;also&lt;/em&gt; runs headless Chrome inside its container (requiring &lt;code&gt;SYS_ADMIN&lt;/code&gt; capability and roughly 2–6 vCPUs per active recording), and one Egress instance still records one room at a time—so you still size Egress per concurrent recorded room, without carrying the rest of Jitsi's stack.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deployment was smooth (if you stick to official templates)
&lt;/h2&gt;

&lt;p&gt;Getting LiveKit Server and Egress running via Docker was straightforward. One takeaway if you use coding agents to help wire up configs: &lt;strong&gt;anchor strictly on LiveKit's official config generator (&lt;code&gt;livekit/generate&lt;/code&gt;) and sample templates&lt;/strong&gt;. Letting an LLM improvise WebRTC/TURN/Caddy configs from scratch is a fast way to break UDP media routing (&lt;code&gt;50000-60000/udp&lt;/code&gt;, &lt;code&gt;3478/udp&lt;/code&gt;, &lt;code&gt;7881/tcp&lt;/code&gt;) or TLS certificates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it actually broke: SFU outbound math
&lt;/h2&gt;

&lt;p&gt;LiveKit is a Selective Forwarding Unit (SFU). Every participant uploads their camera stream &lt;strong&gt;once&lt;/strong&gt; to the server, and the server forwards separate copies down to everyone else in the room.&lt;/p&gt;

&lt;p&gt;If everyone has a 1080p camera on at ~4 Mbps (ignoring simulcast downscaling for a worst-case ceiling), the server's &lt;strong&gt;outbound (egress)&lt;/strong&gt; bandwidth grows as &lt;code&gt;N × (N - 1) × 4 Mbps&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Participants (all cameras on)&lt;/th&gt;
&lt;th&gt;Server outbound bandwidth&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 publishing, 1 watching&lt;/td&gt;
&lt;td&gt;~4 Mbps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 people (1-on-1)&lt;/td&gt;
&lt;td&gt;~8 Mbps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 people&lt;/td&gt;
&lt;td&gt;~24 Mbps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 people&lt;/td&gt;
&lt;td&gt;~48 Mbps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10 people&lt;/td&gt;
&lt;td&gt;~360 Mbps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On a cloud VM with a &lt;strong&gt;10 Mbps outbound cap&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A 1-on-1 call uses ~8 Mbps—just under the ceiling.&lt;/li&gt;
&lt;li&gt;A 3rd person pushes outbound demand to ~24 Mbps—instant packet loss and frozen frames, while the 8 vCPUs and 32 GB RAM sit mostly idle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before going to production, LiveKit's CLI includes &lt;code&gt;lk load-test&lt;/code&gt; to simulate publishers and subscribers against your actual box instead of guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway: bill video servers by metered traffic, not fixed bandwidth
&lt;/h2&gt;

&lt;p&gt;For self-hosted video meetings, &lt;strong&gt;outbound network cap matters long before CPU or RAM&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Traffic for meetings is spiky: heavy while rooms are active, near zero the rest of the day. Paying for a high fixed-Mbps pipe 24/7 is expensive, while a cheap 10 Mbps fixed pipe chokes on the third caller. Using &lt;strong&gt;pay-per-GB (metered egress traffic) with a high burst bandwidth ceiling&lt;/strong&gt; fits SFU workloads much better than a flat low-bandwidth cap.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I maintain a plain-English checklist of self-hosting requirements, license terms, and real server/cloud costs for &lt;a href="https://dceniac.com/oss/livekit/" rel="noopener noreferrer"&gt;LiveKit&lt;/a&gt; and 18 other open-source business apps at &lt;a href="https://dceniac.com" rel="noopener noreferrer"&gt;BuilderStack&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webrtc</category>
      <category>opensource</category>
      <category>selfhosted</category>
    </item>
  </channel>
</rss>
