Lavalink Discord Self-Hosting

Self-Hosting Lavalink for Discord Music Bots: A Complete 2026 Guide

By Witchly Team · · 9 min read

If your Discord music bot keeps dropping the queue, stuttering on long tracks, or randomly disconnecting from voice channels, the bot itself usually isn’t the problem. The problem is Lavalink — the audio backend that almost every modern Discord music bot relies on — and where it’s running.

This guide explains what Lavalink actually does, why running your own node is a quality-of-life upgrade over public free nodes, and how to deploy one on Witchly.host with sensible production defaults.

A Discord bot written in Node.js (with discord.js, eris, or oceanic) or in Java (with JDA) can technically play audio on its own. But streaming audio inside the bot process is expensive — every track decode, transcode, and packet send happens in your bot’s event loop. One stuck stream can freeze the whole bot.

Lavalink is a separate audio worker. Your bot tells it “play this YouTube URL in voice channel X,” and Lavalink:

  1. Resolves the URL through one of its source plugins (YouTube, SoundCloud, Bandcamp, HTTP streams, local files).
  2. Downloads and transcodes the audio in its own JVM.
  3. Streams Opus packets directly to Discord’s voice gateway over a separate WebSocket.
  4. Tells your bot “track ended” or “queue empty” via events.

Your bot just sends commands and listens for events. The CPU-heavy work is offloaded.

Public Lavalink nodes (the lists circulating on GitHub gists) are convenient but unstable:

  • Rate-limited or capped by their operators. You and dozens of other bots share the same node.
  • No password rotation guarantees — when the operator changes the password, your bot dies.
  • Geographic distance — most public nodes are in random countries, adding 100-300ms of WebSocket latency between your bot and the Lavalink node.
  • YouTube source plugin breakages propagate slowly. When YouTube changes its tokens (which happens every few weeks), public nodes can be down for days.

A node you control is the same as anything else self-hosted: it works exactly as well as you maintain it, no more no less. For a bot serving more than a handful of Discord servers, this is usually the right call.

What you need to know before starting

Lavalink is a JVM application, distributed as a single Lavalink.jar file (~50 MB). It needs:

  • A JVM (Java 17 or newer). Witchly’s Lavalink egg ships with the right Java version baked in.
  • One TCP port for its REST + WebSocket API. The bot connects here.
  • Outbound network access so it can reach YouTube, SoundCloud, etc.
  • CPU more than RAM. Audio decoding burns CPU during active streams; idle Lavalink uses very little memory.

A typical node serving one bot in a few dozen Discord servers needs roughly 1 vCPU and 512 MB-1 GB RAM. The Witchly Application plans give you those baselines (The Helper plan = 1 GB / 1 vCPU, suitable for most use cases; The Daemon plan = 2 GB / 2 vCPU for heavier loads).

In the dashboard’s deploy wizard, pick the Applications category and select Lavalink from the egg list. The deploy wizard’s Configure step will surface the required environment variables — most importantly the password your bot will use to authenticate.

After deployment, the Console tab streams Lavalink’s startup logs. You’ll see something like:

.   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` |  \ \ \ \
\\/  ___)| |_)| | | | | || (_| |   ) ) ) )
'  |____| .__|_| |_|_| |_\__, |  / / / /
=========|_|==============|___/=/_/_/_/
                                                    
   Lavalink                Version: 4.x.x

When you see Lavalink is ready to accept connections, the node is live.

The Network tab in the dashboard shows the public host and port — that’s the address your bot uses. Witchly’s free Application port range is 41050–41349; Elite is 41650–41949. Both port ranges have UFW open and Pterodactyl panel allocations registered, so the port your server picks is reachable from the internet.

Configuring application.yml

The default application.yml shipped with the egg is sane, but worth understanding. The most relevant sections:

Server settings:

server:
  port: 2333  # Replaced by Witchly's allocation port at runtime
  address: 0.0.0.0

The port is overridden by the egg startup command — you don’t manually edit it.

Lavalink core:

lavalink:
  server:
    password: "youshallnotpass"  # Set via the Configure step
    sources:
      youtube: true
      soundcloud: true
      bandcamp: true
      http: true
      local: false
    bufferDurationMs: 400
    youtubePlaylistLoadLimit: 6
    playerUpdateInterval: 5
    youtubeSearchEnabled: true
    soundcloudSearchEnabled: true

bufferDurationMs is the only knob casual operators ever need to touch. The default 400ms is fine for stable connections. Bump it to 1000-2000ms if your players experience occasional audio glitches when network jitter spikes.

youtubePlaylistLoadLimit: 6 caps how many pages of a YouTube playlist Lavalink will load at once. Each page is 100 tracks. If you let users queue 10,000-track playlists, you’ll regret it during peak hours. Six is a sensible default.

After editing application.yml via the File Manager, restart the server in the Console tab. Lavalink picks up config changes only on restart.

Connecting your bot

The exact code depends on which Lavalink client library your bot uses, but the connection details are universal:

  • Host: Witchly server’s public hostname or IP
  • Port: The allocation port shown in the Network tab
  • Password: Whatever you set in the Configure step
  • Secure (TLS): Lavalink doesn’t terminate TLS itself. Witchly’s Lavalink egg serves plain TCP — your bot connects with secure: false. The bot ↔ Lavalink WebSocket is on Witchly’s private port; only your bot needs to reach it.

For a Node.js bot using lavalink-client:

import { LavalinkManager } from "lavalink-client"

const manager = new LavalinkManager({
  nodes: [{
    host: "your-server.witchly.host",
    port: 41123,           // From dashboard Network tab
    authorization: "your-password-here",
    secure: false,
  }],
  sendToShard: (id, payload) => client.guilds.cache.get(id)?.shard.send(payload),
  client: { id: "YOUR_BOT_USER_ID", username: "YourBot" },
})

await manager.init({ id: client.user.id, username: client.user.username })

For a Java bot using JDA + lavaplayer-fork: same fields, different syntax. The Lavalink documentation covers every major client library.

Troubleshooting common issues

Bot connects but every track fails to load. YouTube source plugin is out of date. Lavalink 4 introduced the Youtube source plugin (separate from the bundled lavaplayer source); the egg ships a recent build. If track loading breaks, update the plugin JAR via the File Manager — Witchly’s Discord support channel posts the latest known-good version when YouTube changes tokens.

Audio cuts out mid-track. Either CPU saturation (check the dashboard’s resource graphs) or network jitter. Bump bufferDurationMs to 2000.

Bot connects, joins voice, but no audio plays and no errors. The Discord voice WebSocket isn’t reaching Lavalink. This usually means your bot’s sendToShard callback is wrong — it has to forward voice_state_update and voice_server_update events from Discord to Lavalink unchanged.

Random disconnects. Lavalink’s lavalink.server.password doesn’t match what your bot sends. Re-verify in the Configure step.

Production checklist

Before pointing a real bot at your Lavalink node:

  • Confirm the password is something other than the default youshallnotpass.
  • Set up scheduled auto-restarts (Schedules tab → daily at 4 AM UTC). The JVM accumulates GC pressure over weeks of uptime.
  • Enable a daily backup so application.yml isn’t lost if you reinstall.
  • If your bot serves more than ~50 voice connections concurrently, upgrade from The Helper to The Daemon. CPU is the bottleneck, not RAM.
  • Keep an eye on the Console for OutOfMemoryError — if the JVM OOMs, your bot loses every active stream until restart.

Why this matters

A self-hosted Lavalink node is the difference between a bot that “works most of the time” and a bot that’s actually production-grade. It’s also the only Discord-bot infrastructure piece you can’t buy your way out of — there is no managed Lavalink SaaS. You either run it yourself or share with strangers.

If you’ve already deployed your bot on Witchly’s Languages tier, adding a Lavalink node beside it on the Applications tier takes about ten minutes. Bot and Lavalink ride the same internal network, audio quality is consistent, and you control the upgrade schedule.

Deploy a Lavalink instance or read the Lavalink setup doc for the exact dashboard walkthrough.