Skip to content

Repository files navigation

Beets Music Management: Docker & Portainer Guide

Welcome!

I struggled with Beets setup and wanted to share my configs, tips, and troubleshooting for anyone else working on music management with Docker and Portainer. These examples reflect how I tag music alongside Lidarr while keeping Lidarr responsible for the final library paths and filenames.

Tested On

  • Debian 12
  • Docker
  • Docker Compose (optional, for Portainer stacks)
  • Portainer Business Edition (optional; note that stuck containers and stacks can occasionally require manual cleanup)

Host paths, user IDs, group IDs, and network names in docker-compose.yml are specific to my installation. Change them before deploying the stack.

Included Profiles

Main standalone profile

The root config.yaml is the complete standalone setup. It catalogs and tags music in place and includes:

  • MusicBrainz and Deezer metadata candidates
  • AcoustID fingerprinting
  • MusicBrainz and Last.fm genres, preserving up to seven useful genres
  • Cover-art fetching and embedding
  • Synced lyrics where available
  • ReplayGain analysis with FFmpeg
  • Tag cleanup, album-type tags, smart playlists, and the Beets web interface

The main profile does not move, copy, or rename music. Its path templates show the equivalent Lidarr naming layout and become active only if import.move or import.copy is deliberately enabled.

update-untagged

This maintenance profile improves metadata already inside a Lidarr-managed library. It writes tags in place while leaving all paths and filenames under Lidarr's control. Read its own README before running it against a library.

update-replaygain-only

This maintenance profile catalogs an existing library without retagging it, then recalculates only track and album ReplayGain metadata when explicitly requested. It defaults to a -14 LUFS target for players without preamp controls and documents how to switch back to the standard -18 LUFS target.

convert-m4a-flac

This is an example Beets conversion profile for producing FLAC files from M4A sources in a separate destination. Converting lossy AAC to FLAC does not restore lost quality. The included sidecar script separately handles the uncommon case where an M4A container already holds a FLAC stream.

arr-scripts

This directory preserves the Beets configs and helper files used by my Lidarr arr-scripts installation. They are kept as a reference and backup rather than as the main standalone Beets configuration.

Docker Setup

  1. Review and adjust every host path, PUID, PGID, timezone, and network in docker-compose.yml.

  2. Place config.yaml and docker-compose-post-commands.sh in the host folder mounted at /config.

  3. Make the startup script executable:

    chmod +x docker-compose-post-commands.sh
  4. Start the stack:

    docker compose up -d

The startup script installs the optional Python dependencies required by the enabled plugins, then launches the configured Beets web service on port 8337. It no longer downloads the abandoned beets-popularity plugin.

Validate Before Importing

Check that Beets can load the configuration and every enabled plugin:

docker exec -it -u abc beets beet -c /config/config.yaml version
docker exec -it -u abc beets beet -c /config/config.yaml config

Back up the library, then test with a small album before importing a full music collection:

docker exec -it -u abc beets beet -c /config/config.yaml import /music/path-to-test-album

The importer writes metadata, artwork, lyrics, and ReplayGain tags. It can still make incorrect metadata choices, so review uncertain matches instead of treating any automatic tagger as risk-free.

How I Use Beets

  • Most configs and example files are well-commented; please read through them first.
  • I use Lidarr arr-scripts to manage my music library.
  • Lidarr handles final naming and organization while Beets focuses on metadata.
  • I test new settings against a small folder before using them on the library.

Earlier Problems And Current Status

  • Spotify as the only indexer: After running for a while, only Spotify appeared as an indexer even with DNS access to MusicBrainz. The current main profile uses MusicBrainz as its primary source and does not require Spotify. Spotify can be enabled separately after adding valid application credentials.
  • Multi-indexer genre combining: WLG was broken and its old integration was unreliable. The current profiles seed genres from MusicBrainz, preserve those values, and add the strongest Last.fm track genres up to a total of seven.
  • Exporting lyrics (.lrc): The current lyrics profile prefers synced data from LRCLIB and lrcmux and stores it in supported audio tags. Sidecar .lrc output remains player- and workflow-dependent.
  • Popularity playlists: The old beets-popularity repository is no longer available, so that plugin and its popularity queries were removed. Modern Beets can retrieve Spotify popularity with spotifysync when the Spotify plugin and valid API credentials are configured.

Older snapshots may still contain (redacted) where private API credentials were removed. Supply your own credentials when enabling those optional sources.

Tips

  • Read through all config files and their comments before using them.
  • Use Docker Compose for easier stack management, but be aware of Portainer quirks.
  • Always back up your music library before testing new configs.
  • Run beet version after changing plugins so missing dependencies are caught before an import.
  • Keep Beets path management disabled when Lidarr owns the library structure.

Help & Community

Happy tagging and organizing!

Releases

Packages

Contributors

Languages