Lavalink

Running Lavalink in Docker With Docker Compose

Run Lavalink v4 in Docker with Docker Compose — the official image, mounting application.yml and plugins, memory flags, networking with your bot and safe updates.

On this page
  1. Prerequisites
  2. Folder layout
  3. docker-compose.yml
  4. Start it
  5. Configuring with environment variables
  6. Running the bot alongside Lavalink
  7. Updating
  8. Logs and troubleshooting
  9. Docker or systemd?
  10. Summary

Docker is a tidy way to run Lavalink: the official image bundles the right Java version, updates are a single command, and your configuration lives in two files you can keep in git. This guide sets up Lavalink v4 with Docker Compose on a Linux server.

Prerequisites

Folder layout

/opt/lavalink/
├── docker-compose.yml
├── application.yml
└── plugins/

Create it:

sudo mkdir -p /opt/lavalink/plugins
cd /opt/lavalink

docker-compose.yml

services:
  lavalink:
    image: ghcr.io/lavalink-devs/lavalink:4
    container_name: lavalink
    restart: unless-stopped
    environment:
      - _JAVA_OPTIONS=-Xmx700M -XX:+UseG1GC -XX:MaxGCPauseMillis=50
    volumes:
      - ./application.yml:/opt/Lavalink/application.yml:ro
      - ./plugins:/opt/Lavalink/plugins
    ports:
      - "2333:2333"
    mem_limit: 1g

What each part does:

  • image: ghcr.io/lavalink-devs/lavalink:4 — the official image, tracking the latest v4 release. Pin a specific version tag (for example 4.x.y) if you prefer controlled upgrades.
  • restart: unless-stopped — Docker restarts Lavalink if it crashes and when the server reboots.
  • _JAVA_OPTIONS — JVM flags. Keep -Xmx at about 70–75% of the container’s memory limit. See JVM tuning for Lavalink.
  • Volumes — your application.yml is mounted read-only into the path Lavalink reads, and the plugins folder persists downloaded plugins between container restarts.
  • mem_limit — caps the container’s memory so Lavalink can’t starve other services on the same server.

Plugin folder permissions

The official image runs Lavalink as a non-root user. That user must be able to write to the mounted plugins folder, or plugin downloads fail at startup with permission errors. The Lavalink documentation lists the user ID the image uses; set ownership of ./plugins to match (with chown) before the first start.

Start it

docker compose up -d
docker compose logs -f lavalink

Wait for “Lavalink is ready to accept connections.” On first start, Lavalink downloads plugins from lavalink.plugins into the plugins folder.

Test from the server:

curl -H "Authorization: your-password" http://localhost:2333/version

Configuring with environment variables

Lavalink is a Spring Boot application, so settings in application.yml can also be supplied as environment variables using relaxed binding — nested keys become upper-case with underscores:

environment:
  - SERVER_PORT=2333
  - LAVALINK_SERVER_PASSWORD=${LAVALINK_PASSWORD}

This keeps the password out of application.yml, so the config file can live in git. Put the real value in a .env file next to docker-compose.yml (Compose reads it automatically) and keep that file out of version control.

If your bot runs on the same server in Docker, put both services in one Compose file. They share a network, and the bot reaches Lavalink by service name:

services:
  lavalink:
    image: ghcr.io/lavalink-devs/lavalink:4
    restart: unless-stopped
    environment:
      - _JAVA_OPTIONS=-Xmx700M
      - LAVALINK_SERVER_PASSWORD=${LAVALINK_PASSWORD}
    volumes:
      - ./application.yml:/opt/Lavalink/application.yml:ro
      - ./plugins:/opt/Lavalink/plugins
    # no "ports:" — only reachable from other containers on this network

  bot:
    build: ./bot
    restart: unless-stopped
    depends_on:
      - lavalink
    environment:
      - DISCORD_TOKEN=${DISCORD_TOKEN}
      - LAVALINK_HOST=lavalink
      - LAVALINK_PORT=2333
      - LAVALINK_PASSWORD=${LAVALINK_PASSWORD}

Leaving out the ports section for Lavalink means it isn’t exposed to the internet at all — the most secure option when the bot is on the same machine. depends_on starts Lavalink first, but it doesn’t wait for it to be ready, so make sure your bot’s client retries the connection (all major clients do).

Updating

With the :4 tag:

docker compose pull
docker compose up -d

Compose recreates the container with the new image; your config and plugins stay in the mounted folders. To update a plugin, change its version in application.yml and restart:

docker compose restart lavalink

Plugins update far more often than Lavalink itself — especially the YouTube plugin — so this is the command you’ll run most. See the YouTube source plugin.

If you run several nodes, update them one at a time so players can resume or move to a healthy node.

Logs and troubleshooting

docker compose logs --tail=200 lavalink     # recent logs
docker stats lavalink                        # live CPU and memory
docker compose exec lavalink java -version   # confirm the Java version inside

Common issues:

  • Plugins fail to download — permissions on the plugins folder, or no outbound internet from the container.
  • Config changes ignored — the volume path is wrong. The file must be mounted at /opt/Lavalink/application.yml.
  • Container keeps restarting — check logs for YAML errors, or an out-of-memory kill if -Xmx is too close to mem_limit.
  • Bot can’t connect — from another server, check ports and your firewall; from the same Compose file, use the service name (lavalink), not localhost.

More fixes in troubleshooting Lavalink.

Docker or systemd?

Both are fine. Docker makes Java version management and updates trivial and keeps everything in two files; systemd has fewer layers and uses slightly less memory. If you already run your bot or other services in Docker, keep Lavalink there too. For the systemd route, see setting up Lavalink v4 from scratch.

Summary

Run Lavalink in Docker with the official ghcr.io/lavalink-devs/lavalink:4 image, mount application.yml read-only and a writable plugins folder, set heap flags through _JAVA_OPTIONS with a matching memory limit, and use restart: unless-stopped. Keep secrets in environment variables, put the bot on the same Compose network without exposing Lavalink’s port when they share a server, and update with docker compose pull && docker compose up -d.