On this page
Video conferencing load is extremely uneven. A school's Jitsi deployment might carry hundreds of participants at 10 a.m. and nobody at 10 p.m. Sizing for the peak means paying for idle bridges most of the day; sizing for the average means meetings fall apart at the peak. Auto scaling is how you avoid both, and on AWS the building blocks are standard. The Jitsi-specific details are what make or break it.
This replaces our 2020 overview of the same topic with a more concrete design, using current Jitsi configuration and AWS features.
What scales and what doesn't
| Component | How it scales | Why |
|---|---|---|
| Videobridges | Auto Scaling group per shard, on load | They do almost all the media work, and join a shard by themselves |
| Jibri | Auto Scaling group, keep a few idle | One Jibri records one meeting |
| Prosody and Jicofo | One per shard; add shards | They hold room state and can't be split within a shard |
| HAProxy | Fixed, or a pair for redundancy | Routes rooms to shards; light load |
Most of the saving comes from the bridges, so start there. The shard layer, several complete deployments behind HAProxy routing by room name, is covered in Jitsi Meet load balancing.
Jitsi Meet adaptive bitrate and resolution scaling
Before adding a single instance, it helps to know that Jitsi already sheds load by itself. Each sender publishes several resolutions at once with simulcast, and the bridge forwards whichever one suits each receiver's measured bandwidth, dropping someone to a lower resolution rather than freezing them. The channelLastN setting caps how many video streams any participant receives at all, which is what makes a 200-person meeting possible on one bridge.
That changes what a "full" bridge means. A bridge under stress is already trading quality away from its users, so scaling out at a stress target buys back resolution as well as headroom.
Videobridge Auto Scaling groups
A bridge in an Auto Scaling group has to configure itself at boot, because it can't be hand-edited. Build a launch template from an AMI with jitsi-videobridge2 installed and the shard's XMPP credentials in place, then let user data fill in the two values that must be unique per instance.
A unique nickname in the brewery room. Two bridges with the same muc_nickname fight over one slot. The instance ID is a convenient unique value.
Its own public address. Bridges in a public subnet only see their private IP, and participants must be told the public one. Read both from instance metadata:
#!/bin/bash
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 300")
md() { curl -s -H "X-aws-ec2-metadata-token: $TOKEN" "http://169.254.169.254/latest/meta-data/$1"; }
INSTANCE_ID=$(md instance-id)
LOCAL_IP=$(md local-ipv4)
PUBLIC_IP=$(md public-ipv4)
sed -i "s/muc_nickname = .*/muc_nickname = \"$INSTANCE_ID\"/" /etc/jitsi/videobridge/jvb.conf
sed -i "s/local-address = .*/local-address = \"$LOCAL_IP\"/" /etc/jitsi/videobridge/jvb.conf
sed -i "s/public-address = .*/public-address = \"$PUBLIC_IP\"/" /etc/jitsi/videobridge/jvb.conf
systemctl restart jitsi-videobridge2 This assumes the AMI's jvb.conf already contains the shard block and a static-mappings block with placeholder values, as shown in the load balancing guide. The security group needs UDP 10000 open to everyone, and outbound TCP 5222 to the shard's Prosody.
Enable the REST API in the AMI
enabled = true in the videobridge.apis.rest block of jvb.conf when you build the AMI, and keep port 8080 closed in the security group. Only processes on the instance should use it.
Scaling on the right metric
CPU works as a rough signal, but the bridge reports something better: a stress level derived from its actual media load, the same value Jicofo uses to decide where to place conferences. It is part of the bridge's statistics at /colibri/stats, and newer versions also expose Prometheus metrics at /metrics.
Publish it to CloudWatch every minute from each bridge, for example from a systemd timer:
STRESS=$(curl -s http://localhost:8080/colibri/stats | jq '.stress_level')
aws cloudwatch put-metric-data \
--namespace Jitsi \
--metric-name BridgeStress \
--dimensions AutoScalingGroupName=jitsi-jvb-shard1 \
--value "$STRESS" Then use a target tracking policy on the group's average BridgeStress. Pick a target that leaves room: a new bridge takes a few minutes to boot and join, and a meeting that grows during those minutes still lands on the existing bridges. Keep a minimum of at least one bridge so the first meeting of the day doesn't wait for a cold start.
Scale in without dropping meetings
This is where naive setups hurt users. When the group scales in, AWS terminates an instance, and every participant on that bridge loses audio and video mid-sentence. The fix is a lifecycle hook that holds the instance in a waiting state while meetings drain.
aws autoscaling put-lifecycle-hook \
--auto-scaling-group-name jitsi-jvb-shard1 \
--lifecycle-hook-name drain-bridge \
--lifecycle-transition autoscaling:EC2_INSTANCE_TERMINATING \
--heartbeat-timeout 3600 \
--default-result CONTINUE On each bridge, a small service watches for the termination. Instance metadata reports the Auto Scaling target state, so no API polling is needed:
STATE=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/autoscaling/target-lifecycle-state)
# "Terminated" means this instance has been chosen for scale-in When it sees that, it runs the videobridge's graceful_shutdown.sh script. The script tells the bridge to shut down gracefully over the REST interface, so Jicofo stops sending it new conferences, then polls /colibri/stats until the participant count reaches zero before stopping the process. Find it with dpkg -L jitsi-videobridge2 | grep graceful_shutdown; it needs jq. When it returns, release the instance:
aws autoscaling complete-lifecycle-action \
--auto-scaling-group-name jitsi-jvb-shard1 \
--lifecycle-hook-name drain-bridge \
--instance-id "$INSTANCE_ID" \
--lifecycle-action-result CONTINUE Set the heartbeat timeout close to your longest normal meeting, and for long workshops extend it with record-lifecycle-action-heartbeat while participants are still connected. The instance role needs permission for those two lifecycle calls and for publishing the metric.
A Jibri pool
Jibri scales differently. Each one records a single meeting, so the question is not load but availability: is there an idle Jibri when someone presses record? Scale on the number of idle instances instead of stress.
- Each Jibri reports whether it is busy through its health API, on port 2222 by default. Publish an idle count to CloudWatch the same way as bridge stress.
- Scale out when idle Jibris drop below a small buffer, typically one or two, and scale in when there are more idle than you want to pay for.
- Use the same lifecycle hook approach so a Jibri in the middle of a recording is never terminated. A busy Jibri should simply refuse to complete the lifecycle action until the recording finishes and uploads.
How Jibri recording works explains why one Jibri can only take one recording, which is the whole reason the pool exists.
Monitoring and testing
Put the Jitsi metrics next to the infrastructure ones on one CloudWatch dashboard: bridge count, average and maximum stress, participants and conferences per shard, idle Jibris, and instance CPU and network. When users report a bad meeting, the first question is whether scaling reacted in time, and that dashboard answers it.
Then prove it works before users do. Load test the deployment with a ramp that forces scale-out, and end the test while bridges are loaded so you watch scale-in drain meetings instead of cutting them. If load mostly follows office hours, combine auto scaling with scheduled off-hours for the fixed servers.
What about jitsi-autoscaler?
Frequently Asked Questions
Can Jitsi Meet auto scale on AWS?
Yes. Videobridges and Jibri run well in EC2 Auto Scaling groups, because each bridge joins its shard through the brewery room at boot. The care goes into a unique identity per instance, a public IP mapping per bridge, and draining meetings before termination.
What metric should trigger Jitsi videobridge scaling?
The bridge's own stress level, published from its colibri statistics as a CloudWatch custom metric, is a better signal than CPU alone because it reflects the bridge's actual media load. Target a level that leaves headroom for a meeting to grow before a new bridge finishes starting.
How do I stop scale-in from dropping live meetings?
Add a lifecycle hook for instance termination. When a bridge is chosen for removal, run the videobridge's graceful shutdown script, which stops new conferences and waits until participants leave, then complete the lifecycle action so the instance terminates.
Does Jitsi's own jitsi-autoscaler support AWS?
Not as a built-in cloud. jitsi-autoscaler manages groups on Nomad, Oracle Cloud, DigitalOcean or a custom deployment model through a sidecar on each instance. On AWS, native Auto Scaling groups with the pattern in this guide are the more common approach.
Should Prosody and Jicofo auto scale too?
Not within a shard. There is one Prosody and one Jicofo per shard. To grow that layer, add whole shards behind HAProxy, which can also be automated but changes much less often than the bridge count.
Does Jitsi Meet scale video quality automatically?
Yes. Senders publish several resolutions with simulcast, and the bridge forwards the one each receiver's bandwidth supports, while channelLastN limits how many streams anyone receives. Quality adapts before capacity runs out, which is why bridge stress beats CPU as a scaling signal.
Auto-Scaling Jitsi, Already Engineered
Terraform-based Jitsi deployments on AWS with auto-scaling videobridges, Jibri pools and Octo, for 1000 to 2000 concurrent users.
See Jitsi Infrastructure as Code