With an isolated DOCKER_CONFIG the CLI looks for buildx only in $DOCKER_CONFIG/cli-plugins, fell back to the legacy builder, and rejected --provenance. Symlink the user's plugin dir, honour Homebrew's, and fail early if buildx is still missing. Co-Authored-By: Claude Fable 5.1 <[email protected]>
$uicide Music Bot
A self-hosted Discord music bot with a clean interface — built from source and served from this Gitea, ready for Unraid.
Note
This is a fork of a fork of a fork. The bot is JMusicBot and the credit belongs to the people below. This repository exists so there is a copy of the working 0.7.1 line that cannot disappear, with a container image built here rather than pulled from someone else's registry. Nothing under
src/is changed.
Author Repository Role jagrosh jagrosh/MusicBot Original author of JMusicBot arif-banai arif-banai/MusicBot Revived and maintains it — dependency updates, Java 25, config migration purnell569 purnell569/MusicBot Fixed YouTube playback (0.7.1). Direct parent of this repository. mward4 this repository Reliable copy, image built and served from gitea.milesward.dev, Unraid template, Gitea CI
Contents
- What this copy adds
- Quick start (Docker)
- Unraid
- Versions and upgrades
- What's different in 0.7.1
- Requirements
- Features
- Supported sources and formats
- Running directly (without Docker)
- Docker notes
- Performance tuning
- Proxy configuration
- Development and publishing
- Questions, bug reports, credits
What this copy adds
- The image is built here.
gitea.milesward.dev/mward4/musicbotcomes out of this repository's Dockerfile,linux/amd64, with OCI labels recording the commit and version it was built from. No dependency onghcr.ioor on anyone else's build. - An Unraid template, deploy/unraid/musicbot.xml: repository,
appdata mapping, JVM flags, and a
--user 99:100default so a freshly created appdata folder works without a chown step. - CI on Gitea Actions. .gitea/workflows/build-and-test.yml
runs the full Maven build — 676 unit and 48 integration tests — and a Docker build on every
push to
main. Upstream's GitHub workflows are kept in.github/but do not run here. - Upstream tags preserved under
upstream/<tag>(upstream/v0.7.1). This repository's own release tags arevX.Y.Zand match the version inpom.xml. - A first run that actually works headless. Upstream's image prompts for a token on a
console that does not exist and exits without writing anything. This image's
entrypoint runs the bot's own
generate-configwhenconfig.txtis missing, tells you what to edit, and stops. It also checks the data directory is writable up front — the bot's own message for that case is a misleading "Another instance is already running". - A publishing runbook, docs/PUBLISHING.md, and a one-shot build-and-publish script for cutting the next release.
Quick start (Docker)
The container runs headless and writes a default config.txt on first run.
mkdir -p /path/to/musicbot
docker run --rm -it \
--name musicbot \
--user "$(id -u):$(id -g)" \
-v "/path/to/musicbot:/musicbot" \
gitea.milesward.dev/mward4/musicbot:latest
- The first run finds no
config.txt, writes the default one into the mounted directory, and exits. - Edit
/path/to/musicbot/config.txt: in thediscord { … }block settokento your Discord bot token andownerto your Discord user ID. - Run the container again.
If the log says the directory is not writable, fix the ownership of the mounted directory (or
change --user) and run again.
Already have a directory with config.txt, Playlists/ and serversettings.json from a JAR
install? Mount that directory instead; nothing else changes.
Docker Compose:
services:
musicbot:
image: gitea.milesward.dev/mward4/musicbot:latest # or :0.7.1 to pin a release
container_name: musicbot
user: "1000:1000" # whoever owns the mounted directory
volumes:
- /path/to/musicbot:/musicbot
restart: unless-stopped
See docker-compose.example.yml for the commented version.
Unraid
Import deploy/unraid/musicbot.xml — Docker tab → Add Container →
Template, or drop it into /boot/config/plugins/dockerMan/templates-user/ — and apply. The
template maps /musicbot to /mnt/user/appdata/musicbot, publishes no ports (the bot only
makes outbound connections), and runs as 99:100 so the appdata folder Unraid creates is
writable straight away. Two things to know:
- First start writes the config and stops. Open
/mnt/user/appdata/musicbot/config.txt, settokenandownerin thediscord { … }block, start the container again. If the log instead says/musicbot is not writable by uid 99, the folder was created with different ownership:chown -R 99:100 /mnt/user/appdata/musicbotfrom the Unraid terminal and start again. - The Discord side needs one switch. In the Developer Portal, under your application → Bot → Privileged Gateway Intents, turn on Message Content Intent. Without it the bot never sees a command.
To run as the image's own non-root user (UID 10001) instead, clear Extra Parameters and
chown -R 10001:10001 /mnt/user/appdata/musicbot once.
Versions and upgrades
| Tag | Meaning |
|---|---|
gitea.milesward.dev/mward4/musicbot:latest |
The newest release. |
gitea.milesward.dev/mward4/musicbot:0.7.1 |
That release, pinned. A version tag is never rebuilt with different code. |
Each release is listed on this repository's
Releases page with the pull command,
what changed, and the JAR for non-Docker installs. To upgrade an Unraid container, change the
tag and apply; config.txt, serversettings.json and Playlists/ live in the appdata mount
and carry over. The config format is versioned and migrates itself on start.
What's different in 0.7.1
These are purnell569's notes for the release this copy is built from.
YouTube changed how it serves streams, which broke playback for everyone on the upstream 0.7.0 release. This version fixes that:
- Signature cipher is no longer tied to OAuth. Deciphering and authentication are unrelated
concerns, but the cipher was only applied when OAuth was on — so the default install fell back
to the bundled extractor and died with
Must find sig function from script: /s/player/…. It is now always applied and configurable viaplayback.youtube.remoteCipher.url. - Added the ANDROID client and reordered the client list by reliability. ANDROID is currently the only client YouTube reliably streams to anonymously; WEB now sits last because YouTube answers it with SABR-only responses that contain no playable format.
- Sign-in failures are now reported usefully. The existing hint could never fire: it matched
on the aggregate exception message rather than the per-client reasons, and compared against an
ASCII apostrophe while YouTube sends
’(U+2019). - youtube-source pinned past 1.18.2, which predates the fixes for the TV client User-Agent
and for formats that omit
contentLength.
Measured on the tracks that were failing: 5/5 playing, stable across repeat runs (previously 1/5). No OAuth or Google account required.
Requirements
Inside the container all of this is already handled. It matters when running the JAR directly.
- Java 25 or newer.
- glibc 2.38 or newer for the JDave/udpqueue native libraries. Ubuntu 24.04 "Noble" and
Debian 13 "Trixie" qualify; check with
ldd --version. On Debian/Ubuntu you may also needlibopus0andlibsodium23. - Message Content Intent enabled in the Discord Developer Portal (see Unraid).
Features
- Easy to run (just make sure Java is installed, and run!)
- Fast loading of songs
- No external keys needed (besides a Discord Bot token)
- Smooth playback
- Server-specific setup for the "DJ" role that can moderate the music
- Clean and beautiful menus
- Supports many sites, including YouTube, SoundCloud, and more
- Supports many online radio/streams
- Supports local files
- Playlist support (both web/youtube, and local)
Supported sources and formats
Everything lavaplayer supports:
- Sources: YouTube, SoundCloud, Bandcamp, Vimeo, Twitch streams, local files, HTTP URLs
- Formats: MP3, FLAC, WAV, Matroska/WebM (AAC, Opus or Vorbis), MP4/M4A (AAC), OGG streams (Opus, Vorbis and FLAC), AAC streams, stream playlists (M3U and PLS)
Running directly (without Docker)
The JAR for each version is attached to its
release as JMusicBot-<version>.zip
(Gitea does not allow .jar attachments; unzip it) with a SHA256SUMS.txt beside it. Then,
with Java 25 on the path:
Linux / macOS / Windows (CMD):
java -Dfile.encoding=UTF-8 -Dnogui=true --enable-native-access=ALL-UNNAMED -jar JMusicBot-0.7.1.jar
Windows (PowerShell): PowerShell treats -D as its own parameter, so quote each option:
java "-Dfile.encoding=UTF-8" "-Dnogui=true" "--enable-native-access=ALL-UNNAMED" "-jar" ".\JMusicBot-0.7.1.jar"
-Dfile.encoding=UTF-8 keeps non-English characters (Cyrillic, Japanese, …) intact in Discord.
See the Setup Page and Config reference
for config.txt.
Docker notes
- Config persistence.
/musicbotis the container's working directory and must be mounted;config.txt,serversettings.jsonandPlaylists/are read and written there. - User. The image's own user is
jmusicbot(UID 10001). Either make the mounted directory writable by 10001 or pass--user(the Unraid template does the latter with99:100). - JAVA_OPTS. The entrypoint starts the JVM with
-XX:+UseZGC -XX:+AlwaysPreTouch. SetJAVA_OPTSto replace that, e.g.-Xms256m -Xmx512m -XX:+UseZGC -XX:+AlwaysPreTouchto cap the heap. - Image contents. A jlink-trimmed Java 25 runtime on
bitnami/minideb:trixie(glibc 2.41) and the shaded JAR built with thedockerMaven profile, which drops the Windows and macOS natives. Labels:org.opencontainers.image.revision(commit) and.version.
Published images: https://gitea.milesward.dev/mward4/-/packages/container/musicbot
Performance tuning
For optimal audio quality with minimal stuttering, the following JVM and configuration options are recommended.
JVM flags
The bot works best with ZGC (Z Garbage Collector), which provides sub-millisecond pause times:
java -Dfile.encoding=UTF-8 \
-XX:+UseZGC \
-XX:+AlwaysPreTouch \
-Dnogui=true \
--enable-native-access=ALL-UNNAMED \
-jar JMusicBot-0.7.1.jar
-XX:+UseZGC: sub-millisecond GC pauses (generational mode is default in JDK 24+)-XX:+AlwaysPreTouch: pre-allocates memory at startup to avoid page faults-Xms/-Xmx: optional; fix or cap the heap (e.g.-Xms256m -Xmx512m)
The Docker image uses ZGC and AlwaysPreTouch by default; JAVA_OPTS overrides.
Audio buffers
Configurable buffers in config.txt protect against GC pauses. Higher values add latency;
the defaults suit most setups.
performance {
# NAS buffer duration in ms (protects against JVM pauses up to this duration)
nasBufferMs = 800
# Lavaplayer frame buffer duration in ms (amount of decoded audio to buffer)
frameBufferMs = 2000
}
Proxy configuration
Specific components can be routed through an HTTP proxy while others connect directly —
useful for proxying audio traffic without touching Discord API traffic. Add to config.txt:
proxy {
host = "127.0.0.1"
port = 8080
lavaplayer = true # Audio source requests (YouTube, SoundCloud, etc.)
jda = false # Discord API traffic
github = false # Version check requests
}
Leave host empty or port at 0 to disable. SOCKS proxies are not supported.
Development and publishing
There is no Java toolchain requirement on the workstation; the build runs in a container:
docker run --rm -v "$PWD":/build -w /build -v musicbot-m2:/root/.m2 maven:3.9.12-eclipse-temurin-25-alpine mvn -B -ntp verify
That is what CI runs on every push to main. Cutting a release — tag, image build, registry
push, Gitea release with the JAR attached — is documented step by step in
docs/PUBLISHING.md; scripts/build-and-publish-unraid.sh does the image
part in one pass.
Keeping up with upstream:
git fetch upstream --tags
git merge upstream/main
upstream is purnell569/MusicBot on GitHub. Their
DEVELOPMENT_WORKFLOW.md and
ARCHITECTURE.md are kept as-is for reference; the GitHub Actions and
ghcr.io specifics in the former do not apply to this repository.
Questions, bug reports, credits
Problems with the bot itself — playback, commands, config — are almost always upstream's to fix and are best raised at arif-banai/MusicBot or purnell569/MusicBot. Anything specific to this copy (the image, the Unraid template, CI, publishing) belongs in this repository's issues.
The libraries that make the bot possible deserve the stars:
Editing
This bot (and the source code here) might not be easy to edit for inexperienced programmers. The main purpose of having the source public is to show the capabilities of the libraries, to allow others to understand how the bot works, and to allow those knowledgeable about Java, JDA, and Discord bot development to contribute. If you choose to make edits, please do so in accordance with the Apache 2.0 License.


