4 months ago
Hi Railway team,
I'm running a Discord bot on Railway that uses @discordjs/voice for voice channel transcription. The bot has been deployed on Railway since May 2025 and was working correctly through at least October 2025. Sometime after that, voice connections stopped working entirely with no code changes on my end.
What's happening:
The bot connects to Discord's main gateway fine (commands work, bot appears online). But when it tries to join a voice channel, the connection cycles through these states in under 200ms and never establishes:
signalling → connecting → connecting → signalling → (15s timeout) → destroyed
The error thrown is VOICE_CONNECTION_FAILED: The operation was aborted.
What I've ruled out:
- Confirmed it's not a code issue — zero code changes between the last working session (October 2025) and now
- Tested with both @discordjs/voice 0.18.0 and 0.19.0 — identical failure pattern
- Discord gateway connection works perfectly, so it's not a general networking issue
- The connecting → connecting double transition indicates Discord is sending two VOICE_SERVER_UPDATE events, but the connection to the voice server WebSocket/UDP is being immediately refused or dropped
Technical context:
Discord voice uses UDP (port 50000–65535 range) for audio data and WebSocket connections to *.discord.media for voice server signalling. These are on different infrastructure from the main gateway.discord.gg. The 200ms failure time is consistent with an
immediate network-level rejection rather than a timeout.
My question:
Does Railway restrict outbound UDP or outbound WebSocket connections to IP ranges outside of discord.gg? Specifically to Discord's voice server infrastructure (*.discord.media, Cloudflare IP ranges used for Discord voice)?
Has anything changed in Railway's network configuration since October 2025 that could affect this?
Thank you!
1 Replies
Status changed to Awaiting Railway Response Railway • 4 months ago
a month ago
I would separate the normal Discord gateway from the Discord voice path here.
Your bot being online only proves that outbound HTTPS/WebSocket to the main Discord gateway works. Voice uses a separate voice WebSocket plus outbound UDP to the selected Discord voice server, so the failure can still be voice-path specific.
I would not try to fix this with Railway public networking, custom domains, or TCP proxy settings. Those are for inbound traffic to your service. Discord voice is outbound traffic from your container to Discord.
The fastest useful test is to log the exact voice endpoint and state transition from inside the Railway deployment:
connection.on("debug", console.log);
connection.on("stateChange", (oldState, newState) => {
console.log("voice state", oldState.status, "->", newState.status);
console.log("networking", newState.networking?.state);
});
Then check whether the voice WebSocket reaches a real endpoint under discord.media and whether UDP setup starts after that. If the voice WebSocket never gets stable, this is not primarily a UDP audio issue yet. If the voice WebSocket is stable but UDP never completes, focus on outbound UDP to the remote voice IP and port Discord selected for that session.
I would also add one small UDP sanity check in the same Railway service, using the actual voice IP and port from the debug output:
import dgram from "node:dgram";
const socket = dgram.createSocket("udp4");
socket.on("error", (error) => {
console.error("udp error", error);
socket.close();
});
socket.send(Buffer.from([0]), DISCORD_VOICE_PORT, "DISCORD_VOICE_IP", (error) => {
console.log(error ? error : "udp send queued");
socket.close();
});
That does not prove Discord will accept the packet as a valid voice packet, but it does tell you whether the container can create a UDP socket and attempt egress to that destination. If this errors immediately inside Railway but works from another host, then the evidence points to a Railway egress path issue. If it queues successfully, the next suspect is the Discord voice handshake, IP family, encryption dependency, or runtime behavior rather than a blanket UDP block.
Also verify the runtime dependencies in the deployed image, not only locally:
npm ls @discordjs/voice @discordjs/opus sodium-native libsodium-wrappers tweetnacl prism-media
node -v
For @discordjs/voice, a missing or incompatible encryption/audio dependency can make voice setup fail even when the gateway connection itself is fine.
So I would narrow it down in this order:
- Log the exact Discord voice endpoint selected during the join.
- Confirm the voice WebSocket reaches that endpoint from Railway.
- Test UDP socket creation and send from the Railway container to the selected voice IP and port.
- Confirm the deployed image has a valid voice encryption and Opus stack.
- If only step 3 fails on Railway, then it is strong evidence for a Railway outbound UDP or routing regression.