Add voice servers
A Fluxer instance comes with one LiveKit voice server on the same machine, the bundled server. You can add more, or run without it. rexctl installs each one, gives it its own keys and ports, sets up HTTPS, registers it with your instance and checks that calls can connect to it.
When to add one
Section titled “When to add one”Add a voice server to:
- Put calls closer to people in another part of the world, in a voice region of its own.
- Spread calls over more machines when one machine runs out of CPU or bandwidth.
- Give a server its own public IP address, or keep it for some communities with restrictions in the admin panel.
- Keep large calls away from the rest.
A call always stays on one server. Fluxer picks the server when the first person joins, and everyone who joins later goes to the same one. A voice server on the same machine as Fluxer shares its CPU and adds no capacity. It helps when the machine has more than one public address, or when you want to keep some calls apart.
On the Fluxer machine
Section titled “On the Fluxer machine”rexctl voice addrexctl picks every value and shows them in one summary before it changes anything:
- ID. The region followed by
-server-and the first free number, such asdefault-server-2. rexctl skips IDs that another voice server on the machine uses, whatever instance it belongs to. While the bundled server is on,-server-1in the default region stays free for it, even after you delete it in the admin panel. - Region. The default region of your instance. Pass
--regionfor another existing region, or--new-regionwith--region-name,--latand--longto create one. - Ports. The next free TCP and UDP pair from 7881 and 7882 upwards, skipping the ports of every other server and instance on the machine.
- Endpoint. A path on your domain, such as
wss://chat.example.com/livekit-default-server-2. It needs no DNS record, no new certificate and no change to a proxy that forwards every path. - Keys. The key
fluxer-followed by the ID, and a new random secret. - Address, memory and image. The same public address, LiveKit release and memory limit as the bundled server.
- Soft limit. 75 connections per CPU. Placement prefers another server once a server holds this many.
Add several at once with --count.
rexctl voice add --count 3When the machine has more than one public IPv4 address, pass --ip to give a server its own. LiveKit gives that address to clients and Docker publishes the server's ports on it alone.
Behind 1:1 NAT, as on AWS, Google Cloud, Oracle Cloud or a home router, the public address isn't on the machine. Pass it with --ip all the same. LiveKit gives it to clients, and Docker publishes the ports on every address of the machine.
rexctl voice add --ip 198.51.100.8When network.ipv6 is on and the machine runs Docker 27 or later, rexctl voice add also gives the server the machine's public IPv6 address. Pass --ipv6 to give the address yourself, or --no-ipv6 to offer only IPv4. On a machine with only IPv6, it stops until you run rexctl config set network.ipv6 true.
A server added without --ip follows voice.public_ip. When you change that setting with rexctl config set, rexctl gives these servers the new address and restarts them one at a time, as an upgrade does. When Fluxer is stopped, they get it at the next rexctl start. A server added with --ip keeps its address.
After it adds a server, rexctl prints its TCP and UDP ports. Open them in your provider's firewall as well. The test at the end runs on the same machine and can't see that firewall.
Adding a server writes a route to caddy.d and reloads the edge. People connected to Fluxer reconnect once, and calls keep going.
When you change the domain or the public HTTPS port with rexctl config set, rexctl moves these servers to the new address in your instance. When Fluxer was stopped at the time, it moves them at the next rexctl start.
On another machine
Section titled “On another machine”On the Fluxer machine, create a join token.
rexctl voice tokenIt prints the command to run on the voice machine.
curl -fsSL https://fluxer.dev/install.sh | sh -s -- voice chat.example.com fvj_...With rexctl already on the voice machine, this does the same.
rexctl voice join chat.example.com fvj_...The token works once and expires after an hour. Pass --expires to rexctl voice token for up to 24 hours. The Add a voice server button on the admin panel's voice servers page creates the same token and shows the same command.
The join command does this:
- Reads your instance's public settings from its domain and stops when voice is off there.
- Installs Docker when the machine has neither Docker nor Podman, as
rexctl installdoes. On a machine with Podman older than 5.8.2 and no Fluxer instance, it stops. That Podman doesn't start voice servers again after a restart. - Finds the machine's public IPv4 and IPv6 addresses, its CPU count, its memory and its firewall.
- Asks your instance for the server's ID, region and webhook address, without using the token.
- Picks free ports and a hostname or IP address for the endpoint.
- Shows a summary and asks once. Pass
--yesto go ahead without asking. - Raises the UDP buffer limits in
/etc/sysctl.d/61-fluxer-voice.conf, which LiveKit needs for many calls. This needs root. Without root, rexctl shows the two commands to run with sudo. Then it starts an edge on ports 80 and 443 with Caddy, and the LiveKit server behind it. - Opens the server's ports, and ports 80 and 443, in ufw or firewalld.
- Waits for a certificate, then registers the server with your instance. The token stops working once it's registered.
- Checks that browsers on your instance can connect to the server, that calls connect over UDP and TCP on each address family the server has, and that your instance accepts the server's webhooks.
When a step fails, run the same command again with the same token. rexctl picks up where it stopped, because the token is used only once the server is registered.
A machine can run more than one voice server. Each one needs a token of its own and a rexctl voice join of its own. They share the machine's edge and get their own ports.
rexctl keeps the voice servers of a machine in the voice folder. That's /opt/fluxer-voice when rexctl runs as root, and ~/fluxer-voice for any other account. Pass --dir to use another folder.
The token is a command line argument, which other accounts on the machine can see while the command runs. rexctl also reads it from FLUXER_VOICE_JOIN_TOKEN, or from standard input with --token-stdin.
Join tokens and trust
Section titled “Join tokens and trust”A voice server handles the audio and video of every call placed on it, and whoever runs it can record those calls. A join token lets one machine add one server. Give it only to a machine you trust. The token gives no other access to your instance.
rexctl voice token listrexctl voice token revoke <id>A token can set the region, the server ID, the hostname, a soft limit and the restrictions of the new server. The machine that joins can't change those.
Hostnames and certificates
Section titled “Hostnames and certificates”With no hostname, the endpoint uses the machine's public IPv4 address, such as wss://203.0.113.7/livekit-fra-server-1. A machine with only IPv6 needs a hostname. See IPv6. Caddy gets a short-lived Let's Encrypt certificate for that address. The certificate lasts about 6 days, and Caddy renews it before it expires. This needs ports 80 and 443 open to the internet and no DNS record.
To use a hostname, add an A record that points at the machine first. On a machine with IPv6, you can add an AAAA record too.
voice-fra.chat.example.com. A 203.0.113.7voice-fra.chat.example.com. AAAA 2001:db8::7Then pass it when you join.
rexctl voice join chat.example.com fvj_... --hostname voice-fra.chat.example.comWhen voice- followed by the region and your domain already points at the machine, rexctl uses it without being asked. When the hostname doesn't point at the machine yet, rexctl prints the records and waits up to 10 minutes. After that it uses the IP address and prints the command to switch later. With --yes, or without a terminal, it uses the IP address straight away. A machine with only IPv6 has no IP address to fall back to. There rexctl waits up to 10 minutes for the AAAA record even with --yes, and stops when the record still doesn't point at the machine.
A token made with --hostname works only with that hostname. rexctl stops when you pass another one. It waits up to 10 minutes for the name to point at the machine, also with --yes, and stops before it starts anything when the name still doesn't. Add the record, then run the same command again.
rexctl voice hostname fra-server-1 voice-fra.chat.example.comOn Cloudflare, set this record to DNS only. rexctl voice join checks that the name points at the machine before the edge asks Let's Encrypt for a certificate, and a proxied name points at Cloudflare. Remove any AAAA record that points somewhere else, because Let's Encrypt checks IPv6 first.
A server from rexctl voice add uses your instance's domain, which can stay proxied. Only signalling goes through Cloudflare. Audio and video go straight to the machine's IP address, which LiveKit gives to clients.
rexctl voice join and rexctl voice add find the machine's public IPv6 address with STUN. When STUN gets no answer over IPv6, they use the address the machine sends from on its default route. They check that it's a global address on the machine, and skip temporary addresses, which change over time. Pass --ipv6 to give the address yourself, or --no-ipv6 to offer only IPv4.
A voice server on IPv6 needs Docker 27 or later, and not rootless Docker. With Podman, rootless Docker or an older Docker, a machine with IPv4 and IPv6 offers only IPv4, and rexctl stops on a machine with only IPv6.
Machines with IPv4 and IPv6
Section titled “Machines with IPv4 and IPv6”LiveKit gives clients both addresses for media. Callers first open a WebSocket to the endpoint, and that needs an address of their own family. With an A record and no AAAA record for the hostname, or with an IP address as the endpoint, callers with only IPv6 can't connect. An endpoint that is an IP address is always IPv4. To let callers with only IPv6 in, use a hostname with an AAAA record that points at the machine. When the hostname has an AAAA record, it has to point at the machine.
rexctl voice add uses your instance's domain as the endpoint. Callers with only IPv6 need an AAAA record for that domain too.
The bundled voice server gives clients the Fluxer machine's IPv6 address too when network.ipv6 is on.
When voice.public_ip is empty, servers added without --ip give clients the machine's IPv4 address as rexctl last found it. When the machine gets a new IPv4 address, they get it the next time rexctl starts Fluxer, with rexctl start or when the machine starts on a system with systemd, and at the next rexctl upgrade to a new release. rexctl doctor warns when a server gives clients an old address.
Machines with only IPv6
Section titled “Machines with only IPv6”- The server needs a hostname with an
AAAArecord that points at the machine. An endpoint can't be an IPv6 address, because browsers can't allow one in the content security policy of the web app. - The Fluxer machine needs IPv6 too. Your instance checks the server through its endpoint, and the server sends webhooks to your instance's public API address. Run
rexctl config set network.ipv6 trueon the Fluxer machine, and give the API address anAAAArecord. - Callers with IPv6 connect directly. Callers with only IPv4 need a TURN server you run and a machine that forwards ports 80 and 443, and
rexctl voice joinsays so once the server has joined. See Callers with only IPv4. install.shandrexctl upgradedownload rexctl from GitHub, which has no IPv6 address. Copy the Linux build of rexctl to the machine yourself, such as withscpfrom a machine with IPv4 and IPv6, and runrexctl voice joinwith it. Before you runrexctl upgradefor a new release, copy that release's rexctl there the same way.
Callers with only IPv4
Section titled “Callers with only IPv4”Without a TURN server, a caller with only IPv4 can't join a call on a server with only IPv6. With a TURN server you run, they can when all of the following is in place.
- Signalling. The browser or app opens a WebSocket to the endpoint before any audio flows, and a TURN server can't help with that. An
Arecord for the hostname has to point at a machine with IPv4 that you run, and that machine has to forward TCP ports 80 and 443 to the voice machine without ending TLS, such as with the nginxstreammodule. rexctl accepts such anArecord and says what the machine has to do. Without it, the caller's WebSocket fails and the call doesn't start. - Media. A TURN server with IPv4 and IPv6, set up as in TURN servers. The caller connects to it over IPv4, and it relays to the voice server over IPv6. For that the TURN server has to hand the caller an IPv6 relay address. A client can ask for one (RFC 6156), but browsers don't. coturn then hands out IPv4 relay addresses, its default, and the call doesn't connect. Set
allocation-default-address-family="ipv6"in coturn. Callers then get IPv6 relay addresses, and audio and video flow both ways, with the caller connected to coturn over UDP or, when UDP is blocked, over TCP.
With allocation-default-address-family="ipv6", the TURN server gives no useful relay for a voice server with only IPv4. Callers who need the TURN server can't connect to such a server with that setting. Your instance gives every voice server the same list of TURN servers. Use the setting only when every voice server of your instance has IPv6.
rexctl voice test <id> on a Fluxer machine with IPv4 tries IPv4 through TURN for a server with only IPv6, when your instance has TURN servers. It opens the WebSocket over IPv4, which goes to the machine the A record points at, and connects media only through the TURN servers the voice server gives clients over UDP and TCP, which it connects to over IPv4. Like a browser, it asks for no address family, so it passes only when the TURN server hands out IPv6 relay addresses. When the instance has no TURN servers, the test skips this.
Machines that joined before
Section titled “Machines that joined before”A voice machine that joined before rexctl supported IPv6 has a voice network without IPv6, and Docker can't add IPv6 to it. Its servers offer only IPv4, and a new rexctl voice join there says so. To offer IPv6, remove the servers with rexctl voice remove --all and join again with new tokens.
TURN servers
Section titled “TURN servers”A TURN server relays call media for callers who can't connect to a voice server directly. rexctl doesn't install or run one. When you run your own, give it to your instance on the Fluxer machine.
rexctl config set FLUXER_LIVEKIT_TURN_SERVERS '[{"host":"turn.example.com","port":3478,"protocol":"udp","secret":"a-long-random-secret"},{"host":"turn.example.com","port":3478,"protocol":"tcp","secret":"a-long-random-secret"}]'With secret, LiveKit gives each participant short-lived credentials in the form coturn checks with use-auth-secret. Set the same secret as static-auth-secret in coturn. ttl sets how many seconds the credentials last, as a whole number from 1. An entry can have username and credential in place of secret. Every text field has to be filled in. host has to be a hostname or an IPv4 address, and field names are lower case. The API doesn't start with a list it can't read, and rexctl config set refuses such a list before it changes anything.
Voice servers that rexctl manages give the list to clients that join a call on them.
- The bundled server gets it when Fluxer restarts with the new setting.
- Servers from
rexctl voice addget it when you change the setting withrexctl config set. rexctl restarts them one at a time, as an upgrade does. - Servers from
rexctl voice joinget it when they join, and again on eachrexctl upgradein their voice folder. When the instance runs an older release than the rexctl in that folder, the upgrade leaves the servers as they are. A server that uses the edge of the Fluxer instance on the same machine also gets it when you change the setting, and when you upgrade that instance to a new release. A changed list restarts them the same way. The voice machine keeps the list, with the secret, invoice/<id>/.env.
On Kubernetes, the chart doesn't pass the setting to the API. Servers that join an instance there get the list only when you put FLUXER_LIVEKIT_TURN_SERVERS in api.env in your values.
For a server you run by hand or added in the admin panel, copy the same entries into rtc.turn_servers in its LiveKit configuration. With voice.compose.yml, set FLUXER_LIVEKIT_TURN_SERVERS in its settings file.
Ports and firewalls
Section titled “Ports and firewalls”Each server needs these ports.
| Port | Use |
|---|---|
| TCP port of the server, 7881 for the first | Media over TCP, for networks that block UDP |
| UDP port of the server, 7882 for the first | Media |
| TCP 80 and 443, on a voice machine | The edge, for signalling and certificates |
rexctl opens them in ufw or firewalld when one of them is on. Your provider's firewall, such as a Hetzner Cloud Firewall or an AWS security group, is outside the machine, and you need to open the same ports there yourself. Docker publishes ports past ufw. An open port in ufw doesn't prove that the port is open from the internet.
A provider firewall that follows connections, such as a Hetzner Cloud Firewall, can let call audio over UDP through with the UDP port closed. It lets replies in once the server has sent packets to a client. TCP still fails. Open both ports.
After you change a firewall, run rexctl voice test <id> on the Fluxer machine for a server on another machine. It connects from outside that machine and reports UDP and TCP on their own. A server from rexctl voice add runs on the Fluxer machine, and its test can't see your provider's firewall. Check that firewall by hand, or join a call from another network.
Behind a router or another NAT, forward each port to the same port number on the machine. LiveKit tells clients the port it listens on, and a different outside port doesn't work.
Networks a voice server can't serve
Section titled “Networks a voice server can't serve”- Shared address NAT (CGNAT, 100.64.0.0/10). A machine with an address from 100.64.0.0/10 on its main network usually can't take incoming calls, and rexctl stops. Some providers use that range for private networks behind 1:1 NAT. When the machine has a public IPv4 address of its own, pass it with
--ip. CGNAT behind a home router doesn't show on the machine. Runrexctl voice test <id>on the Fluxer machine to find it. - Networks that allow only port 443. Some office and school networks block everything but HTTPS. Calls from there need TURN over TLS on port 443, which rexctl doesn't set up. Run your own and add it as in TURN servers, with
"protocol":"tls"and"port":443.
Machines with something else on ports 80 and 443
Section titled “Machines with something else on ports 80 and 443”When another rexctl Fluxer instance holds ports 80 and 443 on the voice machine, the server uses that instance's domain and edge, such as wss://other.example.com/livekit-fra-server-1. You can't pass --hostname then.
The server's files go in the voice folder, and the Fluxer instance stays the default folder. rexctl voice status, rexctl voice test and rexctl voice remove find the server by its ID. The server uses that instance's domain, and rexctl voice hostname doesn't apply to it. rexctl upgrade on the Fluxer instance upgrades it too. Other commands for it need --dir with the voice folder.
When nginx or another program holds them, rexctl stops. Pass --no-edge and the public address of your own proxy.
rexctl voice join chat.example.com fvj_... --no-edge --endpoint wss://voice.example.com/livekit-fra-server-1rexctl publishes the server's HTTP port on 127.0.0.1, from 7880 upwards, or at --http-port. Point your proxy at it with WebSocket upgrades and a read timeout of at least 120 seconds.
When your proxy runs in another network or on another machine, pass --http-bind with an address of the voice machine that the proxy can connect to, or 0.0.0.0 for every address. This port has no TLS of its own. Let only your proxy's network connect to it in your firewall. To change the address later, remove the server and join it again with a new token.
rexctl voice join chat.example.com fvj_... --no-edge --endpoint wss://voice.example.com/livekit-fra-server-1 --http-bind 10.0.0.5nginx needs a location block like this one.
location /livekit-fra-server-1/ { proxy_pass http://127.0.0.1:7880/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 120s;}Caddy needs a route like this one.
voice.example.com { handle_path /livekit-fra-server-1/* { reverse_proxy 127.0.0.1:7880 }}Regions and placement
Section titled “Regions and placement”On another machine, the server goes to the region in the token. Without one, it goes to the existing region nearest to the machine, when your instance has a GeoIP database, and to the default region otherwise. The summary shows the region and the distance. Pass --region to pick another existing region. Fluxer never creates a region by itself, because GeoIP data for cloud addresses is often wrong. Create a region with a token for it, or with rexctl voice add on the Fluxer machine. Give the region's location, because placement sends people to the nearest region.
rexctl voice token --new-region fra --region-name Frankfurt --lat 50.1 --long 8.7rexctl voice add --new-region fra --region-name Frankfurt --lat 50.1 --long 8.7A token without --lat and --long puts the new region where your instance's GeoIP database places the voice machine, or at the place you pass to rexctl voice join with --lat and --long. Without either, the region gets latitude 0 and longitude 0. Fix that in the admin panel.
A channel with a region set in its settings uses that region. Other calls go to the nearest region of the person who starts them. Within a region, Fluxer takes turns between the servers and prefers servers below their soft limit. Restrictions on a server or region in the admin panel apply to new calls on it.
Every API node checks every active server every 15 seconds. A server that fails two checks in a row gets no new calls, and calls that reconnect move to another server. When every server in a region is down or draining, new calls for that region go to a server in another region. When every server is down, Fluxer places calls as before.
Fluxer lists the address of every voice server in its discovery document, and the web app allows browsers to connect to them. Anyone can read that list, as anyone who joins a call already sees the address of its server.
The web app reads that list about once a minute. rexctl voice join and rexctl voice hostname wait until browsers can connect to a new address. A page opened before then can't join calls on that server until it's reloaded.
Run without the bundled voice server
Section titled “Run without the bundled voice server”Calls can go only to the servers you add, with no LiveKit next to Fluxer. Install that way from the start, or turn the bundled server off later.
rexctl install --no-bundled-voicerexctl config set voice.bundled falseFluxer then:
- Stops and removes the bundled LiveKit container, and no longer needs ports 7881 and 7882.
rexctl config setoffers to close them in the firewall. - Creates the default region with no server in it, or removes
default-server-1from it when that server is the bundled one. Servers you added stay as they are. - Places every call on the servers you add with
rexctl voice add,rexctl voice joinor the admin panel. Until you add one, calls can't start.
rexctl config set voice.bundled true starts the bundled server again, opens its ports and adds default-server-1 back to the default region.
With Docker Compose and no rexctl, add external-voice.compose.yml to COMPOSE_FILE and set FLUXER_LIVEKIT_BUNDLED=false in .env. To turn it back on, take both out again and add default-server-1 in the admin panel, with the key and secret from LIVEKIT_API_KEY and LIVEKIT_API_SECRET.
Check a server
Section titled “Check a server”rexctl voice listrexctl voice statusrexctl voice testrexctl doctorrexctl voice list on the Fluxer machine shows every server of your instance with its health, and marks the ones that run on that machine. On a voice machine it shows the servers there.
rexctl voice test joins a throwaway room and reports whether UDP and TCP connect, over IPv4 and IPv6 when the server and the machine running the test both have them. When the test machine lacks a family the server has, it says so. A test on the machine that runs the server can't see your provider's firewall or your router. On the Fluxer machine, rexctl voice test <id> tests a server on another machine from outside its network, with a token your instance signs. When the server rejects that token, your instance has another secret for it than the server has.
Upgrade
Section titled “Upgrade”rexctl upgrade on the Fluxer machine upgrades Fluxer first. Then it moves each voice server on the machine to the new LiveKit release, one at a time. It marks the server as draining. New calls then go elsewhere, and calls on it keep going. Docker then replaces the container, and LiveKit waits until those calls end, for up to 15 minutes. The server then takes new calls again, also when the upgrade fails or you stop it. A server you turned off or marked as draining in the admin panel stays that way.
On a voice machine, rexctl upgrade updates rexctl to the release your instance runs, then upgrades the servers the same way. It never goes past your instance's release, and it refuses --prefetch and --to. It also updates the edge.
rexctl upgrades a server when the release has a new LiveKit image, and also when the release changes the server's files or settings. A server added with --image keeps that image.
A voice machine doesn't upgrade with the Fluxer machine. Run rexctl upgrade there after you upgrade Fluxer, or run rexctl auto-upgrade on on it to upgrade it every day.
Stop, restart and back up
Section titled “Stop, restart and back up”rexctl stop on the Fluxer machine stops the voice servers there too, and calls on them end. rexctl down removes their containers as well. rexctl start starts them again. rexctl restart restarts Fluxer and leaves running voice servers alone.
rexctl backup keeps the voice servers of the instance, with their keys. rexctl restore writes their files again, starts them and opens their ports in the firewall. A server added after the backup is removed by the restore.
Changing network.ipv6 with rexctl config set rebuilds Fluxer's network. rexctl stops the voice servers on the machine first and starts them again afterwards, and calls on them end.
On a voice machine, rexctl stop, rexctl down, rexctl restart, rexctl logs and rexctl backup don't apply. Docker runs each server as fluxer-voice-<id>-livekit-1, and the edge as fluxer-voice-edge-edge-1.
docker logs fluxer-voice-fra-server-1-livekit-1docker logs fluxer-voice-edge-edge-1docker restart fluxer-voice-fra-server-1-livekit-1A voice machine has nothing to back up. When you lose one, remove the server in the admin panel and join a new machine with a new token.
Remove a server
Section titled “Remove a server”Run it on the machine that runs the server. For a server on a machine you no longer have, delete it in the admin panel.
rexctl voice remove fra-server-1rexctl marks the server as draining, waits up to 15 minutes for its calls to end, removes it from your instance, and deletes its container, files, route and the ports it opened. Pass --now to skip the wait.
On a voice machine, rexctl voice remove --all or rexctl uninstall removes every server and the edge. rexctl uninstall on the Fluxer machine removes its voice servers too.
Troubleshooting
Section titled “Troubleshooting”rexctl doctor checks voice servers with these checks.
The secret of a server is FLUXER_VOICE_API_SECRET in voice/<id>/.env in the rexctl folder. On a voice machine that's the voice folder.
| Check | What's wrong | What to do |
|---|---|---|
voice-servers | A container isn't running, or the endpoint doesn't answer through the edge. | Run rexctl voice status <id>. |
voice-endpoints | A server from rexctl voice add still uses an old domain or port of your instance. | Run rexctl doctor --fix. |
voice-ports | A port isn't published, another container publishes it, or the firewall on the machine closes it. | Open the port, and check your provider's firewall. |
voice-node-ip | The server gives clients another address than the machine's public address. | Set the right address. The steps follow this table. |
voice-tls | The certificate of an endpoint is missing or expires within 2 days. | Open ports 80 and 443. docker logs fluxer-voice-edge-edge-1 shows the error from Let's Encrypt. |
voice-registration | The instance deleted the server, has another secret for it, or rejects the time on its requests. | Run rexctl voice remove <id> and join with a new token, put the secret back in the admin panel, or fix the clock. |
voice-webhook | The instance doesn't accept a signed test webhook. A rejection means it has another secret for the server. | Put the server's secret back in the admin panel, or check that the webhook address answers. |
voice-keys | Two servers share an API key. | Give each server its own key. Remove the newer server and add it again. |
voice-health | The API can't connect to a server, or a server has been draining for more than an hour. | Run rexctl voice status on the machine that runs it. For a server stuck draining, clear Draining in the admin panel. |
When voice-node-ip warns, set the address the server gives clients. On the Fluxer machine, run rexctl config set voice.public_ip <address>. A server added with --ip keeps its address. Remove it and add it again with the right --ip. On a voice machine, run rexctl voice remove <id>, then join again with a new token and the right --ip.
When voice-registration says the clock on a voice machine is off, turn on time sync there with sudo timedatectl set-ntp true. Fluxer allows a minute of difference.
People on the same network as a voice machine at home may fail to connect while everyone else can. That happens when the router can't send traffic for its own public address back inside. The router needs NAT loopback, also called hairpin NAT, for those clients. A local DNS record isn't enough, because LiveKit gives clients the server's public address for audio and video. Turn on NAT loopback in the router, or run the server elsewhere.
When the instance is behind a firewall or bot protection, such as Cloudflare's, let these requests through from your voice machines:
/.well-known/fluxerand/on your domain.voice/join,voice/join/preview,voice/nodeandwebhooks/livekitunder your instance's public API address. With the API on your domain, these are/api/voice/and/api/webhooks/livekit. With the API on a host of its own, such asapi.example.com, they have no/apiin front.
rexctl and LiveKit send them without a browser, and bot protection can block them. A voice machine that can't send them can't join, can't drain its servers for an upgrade and can't remove them cleanly. Webhooks tell Fluxer when people leave calls.
Without rexctl
Section titled “Without rexctl”To run a voice server with Docker Compose by hand, see Run a voice server with Docker Compose. A join token also adds a voice server to an instance on Kubernetes, because joining talks only to the instance's public API.