cmus-web
A self-hosted PWA remote control for cmus. Control your player from any device on your network.
Table of contents
- Features
- A note on PWA and HTTPS
- Prerequisites
- Installation
- Usage
- Docker
- Keyboard shortcuts
- Tech stack
- License
Features
- Real-time player state via SSE (track, position, volume, shuffle/repeat)
- Embedded album art extraction (MP3, FLAC, M4A, etc.)
- Installable PWA with offline caching
- Keyboard shortcuts for all playback controls
- Customizable accent color (persisted in localStorage)
- Zero-build frontend (vanilla HTML/CSS/JS + Alpine.js)
A note on PWA and HTTPS
The install/Add to Home Screen prompt only works over HTTPS. Browsers require a secure context for service workers and PWA installation. If you're only accessing cmus-web over LAN on http://192.168.x.x:8000, the app still works fine as a regular web page, you just won't get the install prompt.
If you want full PWA support, you need a domain name with a TLS certificate. This is straightforward with a reverse proxy like Traefik, which handles automatic HTTPS via Let's Encrypt. Put cmus-web behind Traefik and you're done.
Prerequisites
- Python 3.13+
- cmus running with a socket (default:
$XDG_RUNTIME_DIR/cmus-socketor/tmp/cmus-socket) cmus-remoteon yourPATH
Installation
From PyPI
pip install cmus-web
Or with pipx (isolated, recommended for CLI tools):
pipx install cmus-web
From source
git clone https://github.com/FawkesOficial/cmus-web.git
cd cmus-web
uv sync
Usage
Make sure cmus is running, then:
cmus-web
Open http://127.0.0.1:8000 in your browser.
CLI options
cmus-web [OPTIONS]
Options:
--host TEXT Bind address (default: 127.0.0.1)
--port INTEGER Port (default: 8000)
--socket TEXT cmus socket path (overrides CMUS_SOCKET env)
--music-dir TEXT Path to music directory for album art in Docker
--prefix TEXT Host path prefix from cmus-remote to strip (for Docker)
--help Show this message and exit.
Examples
# Bind to all interfaces (accessible from other devices)
cmus-web --host 0.0.0.0
# Use a custom socket path
cmus-web --socket /run/user/1000/cmus-socket
# Run on a different port
cmus-web --port 9090
Docker
Important: cmus socket
cmus-web communicates with cmus through a Unix domain socket. If the socket path you mount does not exist on the host, Docker will silently create a directory in its place, which will break the connection and can be confusing to debug.
Use this helper script to verify the socket exists before starting the container:
#!/usr/bin/env bash
# start-cmus-web.sh - verify cmus socket exists, then start the container
SOCKET="${CMUS_SOCKET:-$XDG_RUNTIME_DIR/cmus-socket}"
if [ ! -S "$SOCKET" ]; then
echo "Error: cmus socket not found at $SOCKET"
echo "Make sure cmus is running and the socket path is correct."
exit 1
fi
docker run -d \
-v "$SOCKET":/tmp/cmus-socket \
-v ~/Music:/music \
-e CMUS_WEB_PREFIX="$HOME/Music" \
-p 8000:8000 \
cmus-web
docker run
docker build -t cmus-web .
docker run -d \
-v /run/user/1000/cmus-socket:/tmp/cmus-socket \
-v ~/Music:/music \
-e CMUS_WEB_PREFIX=/home/user/Music \
-p 8000:8000 \
cmus-web
The music volume mount enables album art extraction. Use CMUS_WEB_PREFIX to tell cmus-web what host path to strip from cmus-remote file paths so they resolve correctly inside the container.
docker compose
services:
cmus-web:
build: .
container_name: cmus-web
ports:
- "8000:8000"
volumes:
- /run/user/1000/cmus-socket:/tmp/cmus-socket
- ~/Music:/music
environment:
- CMUS_WEB_PREFIX=/home/user/Music
restart: unless-stopped
docker compose up -d
Keyboard shortcuts
| Key | Action |
|---|---|
Space / C |
Play / Pause |
B |
Next track |
Z |
Previous track |
S |
Toggle shuffle |
R |
Toggle repeat |
L |
Search lyrics online |
+ / - |
Volume up / down |
Arrow Left / Right |
Seek -5s / +5s |
Arrow Up / Down |
Volume up / down |
Tech stack
- Backend: Python, FastAPI, uvicorn, Mutagen, SSE-Starlette
- Frontend: Vanilla HTML/CSS/JS, Alpine.js
- Packaging: uv, Hatchling
- Container: Debian slim, uv
License
cmus-web is distributed under the terms of the GPL-3.0 license.
Metadata
Release files for cmus-web 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cmus_web-0.1.3.tar.gz | 132.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cmus_web-0.1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 236.5 kB
Release files / cmus_web-0.1.3.tar.gz
| Download URL | cmus_web-0.1.3.tar.gz |
|---|---|
| Size | 132.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8e1bca839b3dfb33dce22fea3c28026703aa45f26b886a2278346005a5a17531
|
|
BLAKE2b-256 checksum How to use checksums |
4d67eb01af905ef6452549081771057830240a172403bf0b67c7fbf0717886d3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 24, 2026.
Transparency logRelease files / cmus_web-0.1.3-py3-none-any.whl
| Download URL | cmus_web-0.1.3-py3-none-any.whl |
|---|---|
| Size | 103.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3c22ffc9e2ede53faf5d0c89b05e146ac7c1ddd0d5422716e32f79410d9d9fd7
|
|
BLAKE2b-256 checksum How to use checksums |
7a53d85c40f7b9a523b94fd2e3e05eea22198e17848571dd2b1c4b503f5ea23d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 24, 2026.
Transparency log