Skip to content

Configuration

bitwerk runs without a configuration file: the server sets up the admin password and the secret for the stream links itself during the initial setup; everything else has sensible defaults. You only need to change what depends on your environment – usually the music folder, and the installer sets that.

Everything you may want to change while bitwerk runs is set by an admin in the web UI (Settings) and stored in the database; a value saved there wins over its environment variable, which wins over the default. Environment variables are for what has to be known before the web UI runs (database, ports, folders) and for developers:

  • on the server in the optional .env next to the docker-compose.yml (/opt/bitwerk/.env after the installer; template with all variables: .env.example). After a change: cd /opt/bitwerk && sudo docker compose up -d.
  • on the Pis in /etc/hifi/player.env; hifi-setup writes it for you (Set up a Raspberry Pi).

Every variable is also available as a CLI flag (--help). The complete list is in the README.md of the repository (section “Configuration”). The most important ones are here, sorted by service.

VariableMeaningDefault
MUSIC_HOST_DIRMusic folder on the host (contains uploads/ and library/)/srv/bitwerk/music
MUSIC_READ_ONLYtrue mounts the music folder read-only (a library bitwerk only reads, e.g. on a NAS): no uploads, no sortingfalse
BITWERK_IMAGESRegistry prefix of the prebuilt images (set by the installer)ghcr.io/arteiii
BITWERK_VERSIONImage version, e.g. 1.2 or 1.2.0latest
BITWERK_SOURCEBuild context if an image is missing (Git URL or checkout)GitHub repository
COMPOSE_PROFILEShttps also starts the HTTPS proxy (set by install.sh --https)–
DRIVE_SETUPfalse = the installer leaves the host’s drive mounting alone (set by install.sh --no-drives, --drives sets it back to true); read by the installer only–
BACKUP_TIMERfalse = no scheduled backups (set by install.sh --no-backup-timer); read by the installer only–
BACKUP_KEEPDatabase backups kept by the scheduled backup and updates; read by the installer only5
BACKUP_STATE_DIRcontrol-server: folder shared with the host’s backup job, for Settings → Backups; set in the Compose files/var/lib/hifi/backup

The BITWERK_* variables only apply to the installer’s Compose file (docker-compose.release.yml); the docker-compose.yml in the repository always builds from source. The backup interval (every 72 hours by default) is set in the web UI under Settings → Backups (Operations).

PortServiceWho connects
8080/tcp, 80/tcpcontrol-server: web UI, REST API, WebSocket (80 only when it is free)Browsers, players, displays, Stream Deck
8443/tcp, 443/tcpcontrol-server: the same over HTTPS with certificates from its own certificate authority (Spotify redirect URI, passkeys; HTTPS_BIND=off turns it off)Browsers
53535/udpcontrol-server: discoveryPlayers (broadcast)
53536/udpcontrol-server: shared clock of the synchronous roomsPlayers
9000/udpcontrol-server: OSCFaders, TouchOSC
8082/tcpmedia-service: streams, cover art, playback-infoPlayers
8083/tcp, 1900/udpcontrol-server: UPnP renderer and SSDP (off by default)UPnP control point apps
8084/tcpai-service (only on 127.0.0.1)only the control-server
5432/tcpPostgres (only on 127.0.0.1)only the server services
3000/tcpKiosk on the Pilocal only (127.0.0.1)

The server’s firewall must allow 8080, 8082, 53535/udp, 53536/udp and, if needed, 80, 443, 8443, 9000/udp and (with the UPnP renderer) 8083 and 1900/udp from the LAN. Postgres needs no access from outside.

VariableMeaningDefault
ADMIN_PASSWORD_HASHAdmin password as an argon2 hash (control-server --hash-password), instead of setting it in the browser– (initial setup in the browser)
ADMIN_PASSWORDPlain-text alternative (≥ 8 characters), for testing only: it gets a new hash at every start, so its logins end with each restart. Use ADMIN_PASSWORD_HASH or your own user account (Settings → Access) instead–
API_TOKENSFixed API tokens (each ≥ 32 characters) for the control-server API; tokens for the media API (port 8082) and more come from the web UI under Access–
MEDIA_URL_SECRETSecret for signed stream links (≥ 32 characters); set identically on control-server and media-service– (generated by the server, stored in the database)
SETUP_CODE_TTL_SECSValidity of the setup code of the initial setup; afterwards the next attempt generates a new one86400 (24 h)
SESSION_TTL_HOURSHow long a login lasts without use; every visit renews it, and restarts and updates do not log you out720 (30 days)
PASSKEY_ORIGINThe HTTPS address you open bitwerk at, e.g. https://hifi.example; user accounts can then sign in with a passkey (fingerprint, face, device PIN or security key). Passkeys work only at exactly this address, never at an IP address– (off)
SESSION_COOKIE_SECUREtrue behind an HTTPS reverse proxyfalse
LOGIN_MAX_ATTEMPTS, LOGIN_LOCKOUT_SECSProtection against password guessing per IP5, 300
TRUSTED_PROXIESReverse proxies on another machine whose X-Forwarded-For names the browser for that protection, IP addresses, comma-separated (a proxy on the same machine is always trusted)–
OSC_ALLOWED_IPSAllowed OSC senders (IPs/CIDR, * = all)loopback only
OSC_FEEDBACKControllers that get the rooms’ state over OSC, IP:port, comma-separated (controllers can also subscribe with /hifi/feedback)–
AUTH_DISABLEDEverything without login (for tests only!)false
VariableMeaningDefault
MUSIC_DIRRoot with uploads/ and library/ (inside the container)/data/music
UPLOAD_DEBOUNCE_MSQuiet time before an upload is processed2500
UPLOAD_MAX_FILE_MBLargest file the web upload takes, in MiB; 0 turns it off4096
UPLOAD_STAGING_TTL_HOURSHours after which an abandoned web upload (closed tab, lost network) is removed; 0 keeps it until the next restart24
SKIP_LOUDNESS_ANALYSISNo R128 measurement when ReplayGain tags are missing; only a fallback, the web UI’s server setting analysis.loudness winsfalse
SKIP_QUALITY_ANALYSISNo file quality analysis (band edge, effective bits, clipping, defects); fallback for analysis.qualityfalse
SKIP_LIBRARY_SCANNo full scan on startfalse
SCAN_CONCURRENCYFiles the library scan reads and measures in parallel; 0 = number of CPU cores (lower on a slow NAS disk)0
FULL_LIBRARY_SCANRe-read every file on start, even unchanged ones (measurements are kept)false
RESCAN_INTERVAL_SECSSeconds between periodic rescans (missed uploads, changed and deleted files); 0 = only with Rescan600
DRIVE_MOUNT_DIRSFolders (comma-separated) below which mounted USB drives are offered as external libraries (External drives); empty = off/media
DRIVES_PROPAGATIONMount propagation of the host’s /media in the media-service: rslave shows drives mounted later; rprivate on hosts where /media is on a private mount (the installer sets it there)rslave
WRITE_GUARDLet the kernel refuse every write of the media-service outside the music folder and the cover cache (Landlock), so external libraries stay read-only even on a writable mount; false = offtrue
COVER_LOOKUPLook up missing covers online (MusicBrainz + Cover Art Archive, no API key); false = no cover requests. Usually switched in the web UI’s server settings, which take precedencetrue
METADATA_LOOKUPArtist photos and bios, album background, “About this song” next to the lyrics (MusicBrainz, Wikidata, Wikipedia, Commons; no API key, sources and licenses shown in the UI); false = off. Both false = no internet requests. Also in the web UI’s server settingstrue
METADATA_LANGUAGEWikipedia language of the bios (en, de, …); English when the article is missing. Also in the web UI’s server settingsen
LYRICS_LOOKUPSong lyrics from LRCLIB (no API key) for tracks without a sidecar .lrc file or lyrics tag, and for Spotify and radio; false = local lyrics onlytrue
LRCLIB_URLAddress of LRCLIB (or a self-hosted mirror)https://lrclib.net/
MEDIA_PUBLIC_URLBase URL for stream links if the Host header does not match (proxy)–
VariableMeaning
SPOTIFY_CLIENT_ID, SPOTIFY_REDIRECT_URISpotify link, if not set under Settings → Spotify (which takes precedence)
MQTT_URLMQTT broker for triggers, e.g. mqtt://user:pass@broker:1883 (do not set to empty), if not set in the web UI’s server settings (which take precedence)
HA_DISCOVERY, HA_DISCOVERY_PREFIX, HA_BASE_TOPICHome Assistant entities per room over the MQTT broker (on by default, prefix homeassistant, topics under bitwerk), if not set in the web UI
LISTENBRAINZ_API_URLListenBrainz API for scrobbling (a self-hosted ListenBrainz works too)
LASTFM_API_KEY, LASTFM_API_SECRETYour Last.fm API account (create one at last.fm/api/account/create), if not entered under Settings → Listening (which takes precedence); with both set you can connect Last.fm for scrobbling
ALLOWED_MACSClients that are approved on start (pairing is still required)
VariableMeaningDefault
CLIENT_IDName of the room in the player selection; a name assigned in the web UI (“Rename”) takes precedenceclient-<MAC>
HIFI_MACMAC with which the player registers (special cases only)MAC of the network card with the route to the server
ALSA_DEVICEOutput device, always hw: for bit-perfect playback; empty: the first USB or HAT card the player findsempty
HIFI_RATESRates the DAC can do natively44.1–192 kHz
HIFI_MAX_BITSHighest bit depth (16, 24, 32)24
HIFI_DSDnone, dop, nativenone
HIFI_VOLUMEsoftware, hardware (DAC mixer), fixedsoftware
HIFI_MIXERMixer element for hardwareautomatic
HIFI_BUFFER_MS, HIFI_PREFILL_MSJitter buffer and prefill2000, 500
HIFI_PAIRING_CODEOne-time pairing code (alternative to --mode pair)–
HIFI_SERVER, HIFI_PLAYER_IDSet the server explicitly instead of discovery–
KIOSK_SESSION, KIOSK_ROTATEDisplay: wayland/x11, rotation 0/90/180/270auto, 0

DAC-specific values for HiFiBerry, Allo, IQaudIO and USB DACs: Set up a Raspberry Pi.

  • Set the password in the browser (initial setup) or as a hash in ADMIN_PASSWORD_HASH, not in plain text in the .env.
  • HTTPS via a reverse proxy (Compose profile https with Caddy, see Operations) if the web UI is reachable over Wi-Fi with guests. Without TLS an attacker on the same network can read cookies. Then set SESSION_COOKIE_SECURE=true.
  • Approve OSC only for known devices.
  • Backups: the installer backs up the database every 72 hours; back up the music folder and, if needed, the .env yourself (Operations). The cover cache can be regenerated at any time.