All apps · 0 apps
DizzyBot
Docker app from John's Repository
Overview
Readme
View on GitHubDizzyBot
DizzyBot is a fully open-source, self-hosted Discord music bot written in Python. It uses Lavalink for voice playback and is designed for Docker hosts, including Unraid.
The project is deliberately easy to fork. Queueing, source resolution, playback, interactive controls, permissions, settings, presentation, commands, health checks, and runtime lifecycle all have public base classes and first-party default subclasses. A single composition module selects the implementations.
Features
- YouTube videos and playlists
- SoundCloud tracks and sets
- Spotify track, album, and playlist links mirrored to YouTube or SoundCloud
- Persistent per-server internet radio stations, with no stations imposed by default
- Slash commands only; no message-content intent
- Independent queues and playback in multiple Discord servers
- Pause, resume, seek, volume, queue editing, shuffle, and track/queue repeat
- An automatically updated Now Playing panel with Previous, Play/Pause, Skip, and Stop buttons
- SQLite-backed per-server settings with automatic migrations
- Configurable same-channel permissions plus administrator/DJ-role overrides
- Configurable idle and empty-channel disconnects, with DJ-controlled 24/7 mode
- Docker health endpoints and pinned Lavalink plugins
- One-container Unraid template with bundled private Lavalink
- Published
linux/amd64andlinux/arm64image workflow
Spotify's API does not provide downloadable full-track audio for a Discord bot. DizzyBot uses LavaSrc mirroring: Spotify supplies metadata and a playable match is found on YouTube or SoundCloud. Operators must follow the terms of every configured media service. DizzyBot does not bypass DRM, download media, or cache audio.
Unraid: one-container installation
Unraid users do not need to clone the repository, manage Compose, create a Lavalink container, or
publish any ports. The Community Applications template deploys
ghcr.io/john-9474/dizzybot-standalone:latest, which contains both DizzyBot and its private Lavalink
process.
Once the Community Applications listing is published:
- Open Apps in Unraid and search for DizzyBot.
- Select Install, enter the Discord bot token, and optionally enter both Spotify credentials.
- Select Apply. The template creates the appdata mapping and starts the single container.
The standalone image generates its internal Lavalink password automatically. It runs the bot and
Lavalink as Unraid's standard UID/GID 99:100, persists server settings under
/mnt/user/appdata/dizzybot, supervises both processes, and stops the whole container if either one
fails. The source for the listing is templates/dizzybot.xml.
The template and image publication workflow are included in this repository. The listing will become searchable after a tagged image has been published to the public GHCR package and the template has passed the Community Applications Validate and Scan submission.
Quick start with Docker Compose
Prerequisites:
- Docker Engine with Docker Compose v2
- A Discord application and bot token
- Optional Spotify Premium account and developer application for Spotify links
In the Discord Developer Portal, create an application and bot. You do not need to enable Message Content Intent.
Generate an invite under OAuth2 > URL Generator with the
botandapplications.commandsscopes. GrantView Channels,Send Messages,Embed Links,Connect,Speak, andUse Slash Commands.Clone this repository, copy the environment template, and edit
.env:cp .env.example .envSet
DISCORD_TOKENto the bot token andLAVALINK_PASSWORDto a long random value. The same Lavalink password is passed to both containers automatically. The repository ignores.env, but you should still restrict access to it and never commit or share it.Start both containers:
docker compose up --detach --build docker compose ps
The bot and Lavalink communicate over a private Compose network. Lavalink's port and the bot's health port are not published to the host by default.
For released images, set DIZZYBOT_VERSION in .env, then run docker compose pull followed by
docker compose up --detach. The Compose file points to
ghcr.io/john-9474/dizzybot and ghcr.io/john-9474/dizzybot-lavalink.
YouTube setup
YouTube playback uses the yt-dlp source built into LavaSrc. The image bundles pinned yt-dlp and Deno
executables for both linux/amd64 and linux/arm64, so no Google account, cookies, converter site, or
extra container is required. yt-dlp resolves the playable stream and Lavalink relays it directly to
Discord; DizzyBot does not save or cache media files.
YouTube changes its playback interface frequently. DizzyBot therefore pins yt-dlp so image builds are reproducible, while subsequent DizzyBot releases can update the extractor independently of Lavalink. Occasional videos may still be unavailable because of regional, age, account, or rights restrictions.
Spotify setup
Create an application in the Spotify Developer Dashboard
and add these values to .env:
SPOTIFY_CLIENT_ID=your-client-id
SPOTIFY_CLIENT_SECRET=your-client-secret
SPOTIFY_COUNTRY_CODE=GB
Restart the stack after changing these values. Spotify is enabled automatically when both the client ID and client secret are present. It is optional: without both credentials, YouTube and SoundCloud remain available and Spotify requests return a configuration error. Current Spotify development-mode accounts require the application owner to have Premium and are subject to Spotify's user and quota limits.
The provided Compose stack reads all credentials from .env. Both containers also understand
*_FILE variants for custom deployments that mount Docker secrets instead; supported names include
DISCORD_TOKEN_FILE, LAVALINK_PASSWORD_FILE, SPOTIFY_CLIENT_ID_FILE, and
SPOTIFY_CLIENT_SECRET_FILE.
Internet radio
DizzyBot can relay direct internet radio streams through Lavalink. It deliberately ships with an empty station list: each Discord server chooses and names its own stations. For example, a DJ or administrator can save a Radio.co listen endpoint with:
/radio add name:House Nation url:https://streaming.radio.co/s06bd9d805/listen
Then any member who meets the normal voice-channel rules can use /radio play name:House Nation.
Both modern HTTPS streams and older HTTP Icecast/SHOUTcast streams are supported. Use the direct
audio endpoint—usually returning a content type such as audio/mpeg—rather than a station's web
player page. Plain HTTP works for legacy stations but is not encrypted in transit.
Adding and removing stations requires an administrator or the configured DJ role. This is important
because the saved URL causes the self-hosted audio service to make an outbound connection. URLs with
embedded credentials and URLs resolving to local, private, link-local, or reserved addresses are
blocked by default. A trusted private deployment can opt in with
bot.allow_private_radio_streams: true. Radio streams remain subject to the configured idle/empty
channel timeout and 24/7 mode.
Commands
| Command | Behavior |
|---|---|
/play query [source] |
Search or queue a supported track/playlist URL; joins automatically |
/join, /leave |
Join, or stop/clear/disconnect |
/pause, /resume, /skip, /stop |
Control current playback |
/queue [page], /nowplaying |
Inspect playback and the upcoming queue |
/remove position, /move from_position to_position |
Edit upcoming tracks |
/clear, /shuffle |
Clear or shuffle upcoming tracks |
/repeat mode |
Select off, track, or queue |
/volume percent |
Set session volume from 0–100 |
/seek position |
Seek using seconds, MM:SS, or HH:MM:SS |
/radio add name url |
Save a direct stream URL (DJ role or administrator) |
/radio play name |
Play or queue a saved station; station names autocomplete |
/radio list [page] |
List this server's saved stations |
/radio remove name |
Remove a saved station (DJ role or administrator) |
/settings show |
Display persistent settings (Manage Server required) |
/settings volume |
Change the default volume |
/settings idle-timeout |
Change empty/idle voice disconnect time from 30–86400 seconds |
/settings 24-7 |
Enable or disable persistent voice connection (DJ role or administrator) |
/settings dj-role |
Set the role allowed to control an active session remotely |
/settings search-provider |
Select the default provider for text searches |
/settings reset |
Restore all deployment defaults |
Normal members must be in the bot's voice channel. Administrators and the configured DJ role may control an existing session remotely, but no command silently moves an active player to another voice channel.
When playback starts, DizzyBot posts one Now Playing panel in the active announcement channel and edits that message as the session changes. It shows the title, artist, artwork when available, playback time, requester, source, volume, repeat mode, current queue position, and the playlist name and playlist position when applicable. The buttons use the same voice-channel and DJ/administrator rules as slash commands. Previous returns to the last successfully played or manually skipped track; failed tracks are excluded from history. Panels are disabled when playback stops or the bot leaves.
By default, DizzyBot leaves five minutes after playback becomes idle or the voice channel has no
human listeners. Administrators can change that delay with /settings idle-timeout. Members with the
configured DJ role can use /settings 24-7 enabled:true to suppress automatic departure, or
enabled:false to restore it. Administrators can also toggle 24/7 mode as an emergency override.
24/7 mode keeps an active connection open; it does not restore a voice session after a bot restart.
Configuration
config.example.yml contains every ordinary deployment setting. The container
uses this file by default. To customize it, copy the file and mount it at
/etc/dizzybot/config.yml:ro.
Environment variables beginning with DIZZYBOT__ override nested YAML keys. For example:
DIZZYBOT__BOT__DEFAULT_VOLUME=60
DIZZYBOT__BOT__PLAYLIST_TRACK_LIMIT=200
DIZZYBOT__BOT__RADIO_STATION_LIMIT=25
DIZZYBOT__BOT__COMMAND_SYNC_GUILD_ID=123456789012345678
DIZZYBOT__HEALTH__PORT=8081
Precedence is environment override, direct credential/credential file, YAML, then built-in default. Playlist and total queue limits must be between 1 and 500. A development guild ID makes slash commands sync immediately to that guild; omit it in production to use global commands.
Persistent server settings and saved radio stations live in /data/dizzybot.sqlite3. Queues, voice
sessions, repeat state, and session volume intentionally do not survive restarts. Back up the /data
volume before upgrades; database migrations run automatically before Discord connects. The default
radio station limit is 50 per server and can be configured from 1 to 100.
Health endpoints are:
/health/live: the process and HTTP server are running/health/ready: storage is migrated and Discord and Lavalink are connected
Customizing a fork
Public contracts are in src/dizzybot/contracts, default behavior is in
src/dizzybot/defaults, and composition.py is the only intended
class-selection point.
For example, a fork can reverse every batch added to a queue:
from dizzybot.defaults.queue import DefaultQueue
from dizzybot.domain import Track
class ReverseQueue(DefaultQueue):
def enqueue(self, tracks: tuple[Track, ...]) -> None:
super().enqueue(tuple(reversed(tracks)))
Then change only the queue factory in build_services:
players = DefaultPlayerManager(
audio,
settings,
presenter,
player_factory=DefaultGuildPlayer,
queue_factory=ReverseQueue,
queue_limit=config.bot.queue_track_limit,
)
The same pattern applies to BaseAudioBackend, BaseTrackResolver, BaseGuildPlayer,
BasePermissionPolicy, BaseSettingsRepository, BasePresenter, command bases, the health service,
BasePlaybackControls, BaseRadioRepository, BaseRadioResolver, and runtime. There is
intentionally no dynamic plugin loader or import-path configuration.
Development
Python 3.13 and uv are recommended:
uv sync --frozen --extra dev
uv run ruff format --check src tests
uv run ruff check src tests
uv run pyright
uv run pytest
uv run pip-audit
Validate a configuration without connecting to Discord:
DISCORD_TOKEN=dummy LAVALINK_PASSWORD=dummy \
uv run dizzybot --config config.example.yml --check-config
Live provider tests are deliberately not part of required CI because upstream search results and availability are nondeterministic. Unit and integration tests use recorded payloads and fake service boundaries. Pull requests build the bot-only, Lavalink, and standalone containers; version tags publish all three as multi-architecture GHCR images with provenance and an SBOM.
License
DizzyBot is licensed under GNU GPL version 3. Lavalink and its plugins are separate projects distributed under their respective licenses.
Install DizzyBot on Unraid in a few clicks.
Find DizzyBot in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.
Requirements
Categories
Related apps
Explore more like this
Explore allDetails
ghcr.io/john-9474/dizzybot-standalone:latestRuntime arguments
- Network
bridge- Shell
sh- Privileged
- false
- Extra Params
--restart=unless-stopped
Template configuration
Persistent DizzyBot database and server settings.
- Target
- /data
- Default
- /mnt/user/appdata/dizzybot
Required token from the Discord Developer Portal.
- Target
- DISCORD_TOKEN
Optional. Spotify activates automatically when both Spotify credentials are provided.
- Target
- SPOTIFY_CLIENT_ID
Optional. Spotify activates automatically when both Spotify credentials are provided.
- Target
- SPOTIFY_CLIENT_SECRET
Two-letter country code used for Spotify metadata.
- Target
- SPOTIFY_COUNTRY_CODE
- Default
- GB
Python bot log level.
- Target
- DIZZYBOT__LOGGING__LEVEL
- Default
- INFO
Maximum number of saved radio stations per Discord server.
- Target
- DIZZYBOT__BOT__RADIO_STATION_LIMIT
- Default
- 50
Allow saved radio URLs to reach private or local network addresses. Enable only for a trusted server.
- Target
- DIZZYBOT__BOT__ALLOW_PRIVATE_RADIO_STREAMS
- Default
- false
Memory options for the bundled Lavalink JVM.
- Target
- _JAVA_OPTIONS
- Default
- -Xms128m -Xmx1g
UID used for the bot and Lavalink processes. The Unraid default is 99.
- Target
- PUID
- Default
- 99
GID used for the bot and Lavalink processes. The Unraid default is 100.
- Target
- PGID
- Default
- 100