Self-hosting
Bandmate runs as a single Docker container, or as a single program without Docker. Everything it stores (Songs, audio, Covers and Backups) lives in one data folder.
Installing
With Docker Compose, save the repo's compose.yaml in an empty folder:
services:
bandmate:
image: ${BANDMATE_IMAGE:-ghcr.io/xkirtle/bandmate:latest}
container_name: bandmate
restart: unless-stopped
# Run as the owner of ./data so the bind-mounted folder stays writable.
user: "${PUID:-1000}:${PGID:-1000}"
ports:
- "${BANDMATE_PORT:-8080}:8080"
volumes:
- ./data:/data
healthcheck:
test: ["CMD", "/bandmate", "healthcheck"]
interval: 30s
timeout: 5s
start_period: 10s
retries: 3Then create the data folder and start it:
mkdir data
docker compose up -dOr with plain Docker:
mkdir data
docker run -d --name bandmate --restart unless-stopped \
--user "$(id -u):$(id -g)" -p 8080:8080 -v "$PWD/data:/data" \
ghcr.io/xkirtle/bandmate:latestOpen http://localhost:8080. Bandmate has no login, so before opening it up beyond your own machine, read Security.
The Compose file runs the container as user 1000 unless you set PUID and PGID. That user must own the data folder, or Bandmate can't write to it.
Without Docker
Docker is the easiest way to run Bandmate, because the image includes everything it needs. If you'd rather not use it, Bandmate also runs as a single program on Linux and macOS, either downloaded ready-made or built yourself. Either way, it keeps its data in a data folder beside where you start it, and listens on port 8080. Configuration changes either. On Windows, run Bandmate with Docker, or run the Linux program in WSL.
Downloading it
Every release from v0.10.0 on has the program ready-made:
| Download | For |
|---|---|
bandmate-linux-amd64.tar.gz | Linux on Intel or AMD |
bandmate-linux-arm64.tar.gz | Linux on ARM |
bandmate-darwin-arm64.tar.gz | macOS on Apple silicon |
bandmate-darwin-amd64.tar.gz | macOS on Intel |
Download yours into a folder of its own, check it against the release's SHA256SUMS, and start it:
mkdir bandmate && cd bandmate
curl -fsSLO https://github.com/xKirtle/bandmate/releases/latest/download/bandmate-linux-amd64.tar.gz
curl -fsSLO https://github.com/xKirtle/bandmate/releases/latest/download/SHA256SUMS
sha256sum -c --ignore-missing SHA256SUMS # on macOS: shasum -a 256 -c --ignore-missing SHA256SUMS
tar -xzf bandmate-linux-amd64.tar.gz
./bandmateDownload it with curl, as here: macOS won't open a program it hasn't verified if it was downloaded in a browser.
Building it yourself
You need Git, Go 1.27 or later and Node.js 26 or later. Check out the latest release's tag, build the web app, which the program includes, then build the program and start it:
git clone https://github.com/xKirtle/bandmate.git && cd bandmate
git checkout vX.Y.Z # the latest release
(cd web && npm ci && npm run build)
CGO_ENABLED=0 go build -o bandmate \
-ldflags "-X github.com/xKirtle/bandmate/internal/build.version=vX.Y.Z" ./cmd/bandmate
./bandmateThe -ldflags tell Settings → About which version it's running.
Adding Beats from links
To add Beats from links, install the three programs the image includes: yt-dlp, ffmpeg and QuickJS. Bandmate finds them on your PATH. Without them, everything else works, and adding from a link says it can't find yt-dlp.
brew install yt-dlp ffmpeg quickjs # macOS, with Homebrew
sudo apt install yt-dlp ffmpeg quickjs # Debian or Ubuntu
sudo pacman -S yt-dlp ffmpeg quickjs-ng # ArchDistributions' packages of yt-dlp can fall behind the sites it downloads from. If one stops working, install yt-dlp's own build instead. Update, in Settings → About, can only update yt-dlp's own build: update one from a package manager with that package manager.
Health check
The Compose file checks Bandmate is healthy with bandmate healthcheck. For your own monitoring, GET /api/health returns 200 {"status":"ok"} while Bandmate can reach its database, and 503 while it can't, or while it refuses to start, e.g. on a database a newer Bandmate changed.
Configuration
Set these in a .env file next to compose.yaml. Without Docker, set them in Bandmate's environment instead, e.g. BANDMATE_UPDATE_CHECK=off ./bandmate. All are optional.
| Variable | Default | What it does |
|---|---|---|
BANDMATE_PORT | 8080 | The port Bandmate is on, on your machine |
BANDMATE_IMAGE | …:latest | Which version to run: see Image tags |
PUID / PGID | 1000 | The user the container runs as. It must own ./data |
BANDMATE_MAX_UPLOAD_MB | 500 | The largest audio file Bandmate takes, uploaded or downloaded, in megabytes |
BANDMATE_UPDATE_CHECK | on | off stops Bandmate checking GitHub for new versions of itself and of yt-dlp |
BANDMATE_ADD_FROM_LINK | on | off turns off downloading Beats from links |
BANDMATE_PORT, BANDMATE_IMAGE, PUID and PGID are the Compose file's. Running Bandmate without Docker, it also reads BANDMATE_ADDR, the address it listens on (:8080), and BANDMATE_DATA_DIR, where it keeps its data (./data; the image sets it to /data).
Security
Bandmate has no login: anyone who can reach it can read and change every Song. That's fine on your own machine. Anywhere else, put it behind a reverse proxy that adds HTTPS and authentication, and let only the proxy reach it. Downloading a Beat from a link is one long request, so give the proxy a read timeout of a few minutes.
Recording also needs HTTPS, or localhost: browsers don't allow the microphone otherwise.
To report a vulnerability, see SECURITY.md.
What Bandmate connects to
Bandmate only goes online when you do something that needs it:
| When you… | It connects to | Turn it off with |
|---|---|---|
| Open Settings → About | GitHub, for the latest Bandmate and yt-dlp, and release notes | BANDMATE_UPDATE_CHECK=off |
| Add a Beat from a link | The site the link is on | BANDMATE_ADD_FROM_LINK=off |
| Press Update for yt-dlp in About | GitHub | BANDMATE_ADD_FROM_LINK=off |
Updating
Image tags
:latest is the newest release, and what most installs want. To control when you update, pin a version instead, by setting BANDMATE_IMAGE, e.g. BANDMATE_IMAGE=ghcr.io/xkirtle/bandmate:1.
| Tag | What you get |
|---|---|
:latest | The newest release |
:X | New features and fixes, but never a breaking change, e.g. :1. From 1.0 on |
:X.Y | Fixes for that version, but no new features, e.g. :1.4 |
:X.Y.Z | Exactly one release, e.g. :1.4.0. It never changes |
:edge | The latest development build, ahead of any release. It can change at any time |
:sha-<short> | One development build, by its commit, e.g. :sha-1a2b3c4 |
A breaking change bumps the first number, new features the middle one, and fixes alone the last one, as CONTRIBUTING.md describes. A breaking change is one that stops your data folder upgrading in place, stops a Backup restoring into a newer Bandmate, changes what an image tag or a BANDMATE_* setting means, or adds logins to Bandmate.
Upgrading and rolling back
To upgrade, pull the new image and start it:
docker compose pull && docker compose up -dBandmate updates its database by itself when it starts. Before it does, it saves an Upgrade copy of the database in data/upgrade-copies, named after the version you're upgrading from, e.g. before-0.14.2.db. That copy is what lets you roll back, and Bandmate keeps the newest three. Its log says where it put the copy and what the upgrade changed.
To roll back, stop Bandmate, put the copy in place of the database, and run the version it's named after. Anything done since the upgrade is lost. For example, back to 0.14.2:
docker compose down
mkdir newer-database && mv data/bandmate.db* newer-database/
cp data/upgrade-copies/before-0.14.2.db data/bandmate.dbThen set BANDMATE_IMAGE=ghcr.io/xkirtle/bandmate:0.14.2 in .env and run docker compose up -d.
- A copy named by date, e.g.
before-2026-10-10.db, goes with the version you ran before that day. - No copy for an upgrade means it didn't change the database: just run the older version.
- The copy holds the database only, not audio or Covers. To keep everything, copy the data folder before upgrading.
If Bandmate won't start, every page and its log say why:
- The database is from a newer Bandmate, e.g. after running an older version without putting its copy back. Run the newer version again, or roll back as above. If one of your Upgrade copies will run on this version, the page names it.
- Bandmate couldn't save the copy. Your database is untouched. Check the disk isn't full and that
datais owned by the user Bandmate runs as (PUIDandPGID), then rundocker compose restart.
Without Docker, upgrade by stopping Bandmate, downloading the new release over the old program (or building the newer tag), and starting it again. Roll back the same way as above, running the older release's program instead.
Copying the data folder
Stop Bandmate, so nothing is mid-write, and copy data. It holds everything: the database, audio, Covers, Backups and Upgrade copies. To back up only some Songs or Beats, from the app, see Backups.
Mounting your own yt-dlp
For advanced setups only
Most installs never need this: Update, in Settings → About, keeps yt-dlp current, as the feature tour describes.
You need this only if Update says it can't run programs from the data folder. That happens when the data folder is mounted noexec, as on some NAS systems. Instead, give Bandmate a yt-dlp of your own:
Download yt-dlp's standalone Linux build:
yt-dlp_linux, oryt-dlp_linux_aarch64on ARM.Make it executable:
chmod +x yt-dlp_linux.Mount it over the bundled one, with a line under
volumes:incompose.yaml:yamlvolumes: - ./data:/data - ./yt-dlp_linux:/usr/local/bin/yt-dlp:roRecreate the container:
docker compose up -d.
To update it later, replace the file and recreate the container again.
Licenses
Bandmate is licensed under the GNU Affero General Public License v3.0. If you run a modified Bandmate as a service that others use over a network, you must offer them its source code.
To download Beats from links, the image also includes three programs under their own licenses: yt-dlp (The Unlicense), ffmpeg (LGPL v2.1 or later, built from its unmodified source without its GPL parts) and QuickJS (MIT). Settings → About lists them with their versions, and their license texts are in the image at /usr/local/share/licenses/. Bandmate without Docker includes none of them.