Packages the upstream beetbox/beets
music manager on a Alpine base, optional build extras, and an entrypoint that
handles UID/GID mapping at runtime.
- GHCR:
ghcr.io/treyturner/beets - Docker Hub:
docker.io/treyturner/beets
vX.Y.Z- manual builds that pin the upstream beets release tag (e.g.v2.5.1)- You want one of these. They're mutable, but always contain the specified version of beets.
- Need a version not currently posted? Open an issue and I'd be happy to build and publish it.
latest- the latest version of beets I manually promoted after deeming it stable (YMMV)- Discouraged, but provided for convenience if you don't require a specific version of beets
⚠️ If you use this tag, pull, and recreate your container, you WILL eventually upgrade beets to a version that breaks one or more plugins and/or your config 😭
vX.Y.Z-dev- latest dev image for beets X.Y.Z- Where builds of upstream refs are tested before being manually promoted to
vX.Y.Z - Automatically built for the version of beets pinned in
build-and-publish-dev.yamlon merges to this repo'smainbranch
- Where builds of upstream refs are tested before being manually promoted to
vX.Y.Z-dev-<run_id>.<attempt_id>– build-specific dev images for traceability- For debugging only, ie. helping me understand an issue you've reported
- The ubiquitous
beets - Samik081's
beatport4 - gtronset's
filetote- Only for beets
v2.3.xat the moment
- Only for beets
- edgars-supe's
importreplace
requests(forlyrics,fetchart, ...)requests_oauthlib(forbeatport4)beautifulsoup4(forlyrics)pyacoustid(forchroma)pylast(forlastgenre)python3-discogs-client(fordiscogs)langdetect(forlyrics)flask(forweb)Pillow(forfetchart,embedart, ...)
ffmpegchromaprint(providesfpcalctochroma)imagemagickjqyq
| Variable | Description | Default |
|---|---|---|
PUID |
Numeric user ID that the entrypoint creates | 99 |
PGID |
Numeric group ID that the entrypoint creates | 100 |
UMASK |
Mask applied before spawning beets | 0002 |
BEETSDIR |
Path to the beets config | /config |
RUNTIME_APK_PACKAGES |
Space-separated list of Alpine packages to install at runtime (note!) |
(none) |
RUNTIME_PIP_PACKAGES |
Space-separated list of Python packages to install at runtime (note!) |
(none) |
| Container Path | Description |
|---|---|
/config |
Beets configuration (config.yaml), state, and plugin artifacts |
| (custom) | Library mount(s) of your choice, e.g. -v /mnt/user/music:/library |
The container is suited for one-off commands; simply put it after the image name:
$ docker run --rm \
-v "$(pwd)/config:/config" \
-v "$(pwd)/library:/library" \
ghcr.io/treyturner/beets:v2.5.1 \
beet --version
beets version 2.5.1The image ships with bash if you prefer to work with an interactive shell inside the container:
$ docker run --rm -it \
-v "$(pwd)/config:/config" \
-v "$(pwd)/library:/library" \
ghcr.io/treyturner/beets:v2.5.1 \
bash
b8afc450904c:/config$ beet --version
beets version 2.5.1The default command launches the web plugin's HTTP server. If no config exists, the entrypoint creates one with web.host: 0.0.0.0 (required to reach the web UI from outside the container); otherwise it will MODIFY your config before starting the server by enabling the web plugin and ensuring the host binding is present.
$ docker run -d --rm --name beets \
-p 8337:8337 \
-v "$(pwd)/config:/config" \
-v "$(pwd)/library:/library" \
ghcr.io/treyturner/beets:v2.5.1
$ docker logs beets
* Serving Flask app 'beetsplug.web'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:8337
Press CTRL+C to quitOnce started, you can access the UI at http://ip.of.docker.host:8337, or start a shell via:
$ docker exec -it beets bash
2edbccf027b4:/config# beet --version
beets version 2.5.1
APK_RUNTIME_EXTRAS and USER_PIP_PACKAGES build arguments instead.
You can install additional Alpine (apk) or Python (pip) packages at container startup using the RUNTIME_APK_PACKAGES and RUNTIME_PIP_PACKAGES environment variables. This is useful for adding dependencies needed by specific plugins or custom workflows without rebuilding the image.
# Install some additional packages before starting
$ docker run -d --name beets \
-e RUNTIME_APK_PACKAGES="curl" \
-e RUNTIME_PIP_PACKAGES="beets-alternatives" \
-p 8337:8337 \
-v "$(pwd)/config:/config" \
-v "$(pwd)/library:/library" \
ghcr.io/treyturner/beets:v2.5.1You can add sqlite to the container to debug or resolve problems with the beets database.
# If container isn't running...
docker run -it --rm -w /config -e RUNTIME_APK_PACKAGES="sqlite" ghcr.io/treyturner/beets:v2.5.1 sqlite3 library.db
# With container running...
docker exec -it -w /config beets bash -c 'apk add --no-cache sqlite && sqlite3 library.db'Beets is extremely configurable; you'll want to read the usage and configuration docs on how to create a config.yaml to put in /config and run the CLI to manage your library.
Make sure to set the directory setting in your config (e.g. /library) to match whatever mount you provide.
docker build \
--build-arg BEETS_REF=master \
-t treyturner/beets:local-dev \
.| Build Arg | Description |
|---|---|
PYTHON_VERSION |
Python version to use, e.g. 3.12 |
PYTHON_BASE_SUFFIX |
Python base image suffix, e.g. alpine |
BEETS_REF |
The git ref of beetbox/beets to build (tag, branch, or SHA) |
APK_BUILD_DEPS |
Space-separated list of Alpine packages to install at build time |
APK_RUNTIME_EXTRAS |
Space-separated list of Alpine packages to include in the final image |
PIP_SOURCE_OVERRIDES |
Space-separated overrides in the form package=spec, replacing entries in DEFAULT_PIP_SOURCES |
USER_PIP_PACKAGES |
Space-separated list of user Python packages (wheels). Must already exist on PyPI or a compatible index so they can be wheeled offline. |
Set PIP_SOURCE_OVERRIDES to point specific packages at alternative sources (e.g., Git forks) without editing the Dockerfile:
docker build \
--build-arg PIP_SOURCE_OVERRIDES="beets-beatport4=git+https://github.com/you/beets-beatport4.git@testing" \
-t treyturner/beets:test-fork .For sources already specified as full VCS URLs (like importreplace, which defaults to git+https://github.com/edgars-supe/beets-importreplace.git), you need to match the exact token:
docker build \
--build-arg PIP_SOURCE_OVERRIDES="git+https://github.com/edgars-supe/beets-importreplace.git=git+https://github.com/you/beets-importreplace.git@integration-tests" \
-t treyturner/beets:test-importreplace .Multiple overrides can be separated by spaces, as long as each value has no whitespace.
Where each build argument and environment variable applies during the image build and container startup sequence.
%%{init: {'theme':'base','themeVariables':{
'primaryColor':'#0f172a',
'primaryBorderColor':'#94a3b8',
'primaryTextColor':'#f8fafc',
'secondaryColor':'#1e293b',
'tertiaryColor':'#e2e8f0',
'lineColor':'#2563eb',
'fontFamily':'"Inter","Segoe UI",sans-serif'
}}}%%
flowchart TD
A1["<strong>Build Args</strong><br/>PYTHON_VERSION<br/>PYTHON_BASE_SUFFIX"] --> B0
A2["<strong>Build Arg</strong><br/>APK_BUILD_DEPS"] --> B1
A3["<strong>Build Args</strong><br/>BEETS_REF<br/>DEFAULT_PIP_SOURCES<br/>PIP_SOURCE_OVERRIDES<br/>DEFAULT_PIP_PACKAGES<br/>USER_PIP_PACKAGES"] --> B2
A4["<strong>Build Arg</strong><br/>APK_RUNTIME_EXTRAS"] --> R1
A5["<strong>Build Args</strong><br/>DEFAULT_PIP_PACKAGES<br/>USER_PIP_PACKAGES"] --> R2
A6["<strong>Env. Vars</strong><br/>PUID<br/>PGID<br/>UMASK"] --> E1
A7["<strong>Env. Vars</strong><br/>RUNTIME_APK_PACKAGES<br/>RUNTIME_PIP_PACKAGES"] --> E2
subgraph Docker Build
direction LR
subgraph Builder Stage
B0["Select Python Base Image"] --> B1
B1["apk add build deps<br/>+ APK_BUILD_DEPS"] --> B2
B2["pip wheel into /wheels:<br/>beets, default srcs + pkgs<br/>+ USER_PIP_PACKAGES"]
end
subgraph Runtime Stage
B2 --> R1["apk add runtime deps:<br/>ffmpeg, chromaprint, imagemagick, ...<br/>+ APK_RUNTIME_EXTRAS"]
R1 --> R2["pip install from wheels:<br/>beets, default pkgs<br/>+ USER_PIP_PACKAGES"]
R2 --> R3["Copy entry scripts & licenses"]
end
end
subgraph Container Start
direction TB
R3 --> E1["docker-entrypoint.sh<br/>Sets up user/group and file mask"]
E1 --> E2["Runtime Installs<br/><strong>Discouraged</strong><br/>apk add / pip install <strong>on EVERY START</strong> of:<br/>RUNTIME_APK_PACKAGES<br/>RUNTIME_PIP_PACKAGES"]
E2 --> E3["Issued command<br/>or start-web.sh"]
end
classDef env fill:#f1f5f9,stroke:#475569,color:#0f172a;
classDef builder fill:#dbeafe,stroke:#2563eb,color:#0f172a;
classDef runtime fill:#dcfce7,stroke:#16a34a,color:#052e16;
classDef start fill:#fef3c7,stroke:#d97706,color:#78350f;
class A1,A2,A3,A4,A5,A6,A7 env;
class B0,B1,B2 builder;
class R1,R2,R3 runtime;
class E1,E2,E3 start;
I made this because I needed it. Issue reports, PRs, suggestions and questions are welcome and appreciated, or feel free to fork and make it more your own.
Build scripts/logic licensed via The Unlicense.
Bundled software includes additional licenses:
beets(MIT) — copy available in the image at/usr/share/licenses/beets/LICENSE.- Alpine packages such as
ffmpeg,chromaprint, and others ship their respective licenses in/usr/share/licenses/.
Review those notices to ensure your downstream use complies, especially with GPL/LGPL components like ffmpeg.