Configuration¶
beetkeeper does not have its own config file. It reads its settings from an optional top-level
beetkeeper: section inside your existing beets config. A plain beets config without that section is
still a valid beets config — beetkeeper only requires the section when you run the server.
You tell beetkeeper which beets config to read via either:
- the
BEETSDIRenvironment variable — the directory holding your beetsconfig.yaml(beets' own convention), or - the
--config-pathCLI flag — the path to the config file itself (see the CLI).
Example¶
# ... your usual beets config (directory, library, plugins, ...) ...
beetkeeper:
log_level: INFO
server:
hostname: 0.0.0.0
port: 8337
database:
# beetkeeper's own SQLite db (separate from the beets library db); created automatically on first run.
sqlite_path: /beets/beetkeeper.db
# Optional: require a login for all access (off by default).
auth:
enable_login_protection: true
username: admin
password: change-me
Settings¶
| Key | Type | Default | Description |
|---|---|---|---|
log_level |
string | — | One of CRITICAL, DEBUG, ERROR, INFO, NOTSET, WARNING. |
server.hostname |
string | 127.0.0.1 |
Interface to bind (e.g. 0.0.0.0 to listen on all interfaces; the default binds loopback only). |
server.port |
int | 8337 |
Port the server listens on (must be > 0). |
server.forwarded_allow_ips |
string | — | Reverse-proxy addresses (comma-separated IPs and/or CIDR networks like 192.168.40.0/24, or "*" for all) whose X-Forwarded-* headers the server trusts. The UI renders root-relative URLs and works through any proxy without this; set it behind a reverse proxy so the request scheme and client address reflect the real client (accurate logs, correct absolute URLs anywhere one is ever emitted). Hostnames are not supported — uvicorn compares the raw peer IP. Note that setting this replaces the loopback default, so include 127.0.0.1 if a local proxy is also in play. Unset, uvicorn's default applies (the FORWARDED_ALLOW_IPS env var, else loopback only). |
database.sqlite_path |
path | — | Path to beetkeeper's own SQLite db (created automatically on first run). |
database.auto_upgrade |
bool | true |
Apply pending schema migrations automatically when the server starts (the db file is backed up first). When false, a stale schema fails startup until you run beetkeeper db upgrade. |
auth.enable_login_protection |
bool | false |
Opt-in login protection: when true, every page and API route requires a logged-in session. |
auth.username |
string | — | Login username. Required when enable_login_protection is true. |
auth.password |
string | — | Login password. Required when enable_login_protection is true. |
auth.session_ttl_hours |
int | 168 |
How long a login session stays valid before a new login is required. |
Login protection¶
beetkeeper is single-user: enabling auth.enable_login_protection gates the whole app behind the one
configured username/password pair (there are no per-route permissions — a logged-in client can do
everything).
- Browsers are redirected to a
/loginpage; a successful login stores the session in anHttpOnlycookie, and a Log out button appears in the navigation bar. - API clients exchange the credentials for a bearer token via
POST /api/auth/login, then send it as anAuthorization: Bearer <token>header (revoke it withPOST /api/auth/logout). - The login endpoints, API docs,
/api/health, and static assets stay reachable without a session.
Sessions are stored (hashed) in beetkeeper's database, so they survive restarts.
Authoritative source
The table above is a friendly summary. For the exact field definitions, validation, and defaults, see the
beetkeeper.settings.user_config
models in the source.
Separate database
database.sqlite_path is beetkeeper's own bookkeeping database — not your beets library database.
It is created (and kept schema-current) automatically by beetkeeper run, or manually via
beetkeeper db upgrade (see the CLI).
The beets plugin (beetkeeper_plugin)¶
The beetkeeper-plugin package provides the beets-side
plugin that pushes library events (imports, removals) to the server's /api/events endpoints — this is
what populates the Beets Events page. Enable it like any beets plugin:
Its own (optional) config section:
beetkeeper_plugin:
# Where to push events. Defaults to `http://127.0.0.1:8337` — matching the beetkeeper server's own
# defaults, right for the usual same-container/same-host setup — so most installs need no section at all.
server_url: http://127.0.0.1:8337
# Only needed when the server runs with `auth.enable_login_protection`; leave unset otherwise.
api_token: your-token-here
| Key | Type | Default | Description |
|---|---|---|---|
server_url |
url | http://127.0.0.1:8337 |
Base URL of the beetkeeper server to push events to. Must be a valid http(s):// URL. |
api_token |
string | — | Bearer token for the push requests, for servers running with login protection. When unset (or blank), pushes carry no auth at all. |
The section is validated when beets loads the plugin (see the
_bk_plugin_settings
pydantic models): a malformed server_url fails plugin load with a clear validation error instead of
silently pushing events nowhere, and blank values are treated as unset (their defaults apply).
Push failures are logged and swallowed — an unreachable beetkeeper server never breaks the beets operation that fired the event.