Four ways to run SoundCork, from simplest to most customizable.
docker run -d --name soundcork \
-p 8000:8000 \
-v /path/to/your/data:/soundcork/data \
-e base_url=http://your-server:8000 \
-e data_dir=/soundcork/data \
ghcr.io/timvw/soundcork:mainCreate a docker-compose.yml:
services:
soundcork:
image: ghcr.io/timvw/soundcork:main
ports:
- "8000:8000"
environment:
- base_url=http://your-server:8000
- data_dir=/soundcork/data
- SOUNDCORK_MODE=local
- SOUNDCORK_LOG_DIR=/soundcork/logs/traffic
# Optional: OIDC/SSO authentication (see Authentication section below)
# - OIDC_ISSUER_URL=https://your-provider/application/o/soundcork/
# - OIDC_CLIENT_ID=soundcork
# - OIDC_CLIENT_SECRET=your-secret
volumes:
- ./data:/soundcork/data
- ./logs:/soundcork/logs
restart: unless-stoppedThen run:
docker compose up -dThis is our production setup. Customize the hostname, volume paths, and ingress controller for your environment.
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: soundcork# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: soundcork
namespace: soundcork
spec:
replicas: 1
selector:
matchLabels:
app: soundcork
template:
metadata:
labels:
app: soundcork
spec:
containers:
- name: soundcork
image: ghcr.io/timvw/soundcork:main
ports:
- containerPort: 8000
env:
- name: base_url
value: "https://soundcork.example.com"
- name: data_dir
value: "/soundcork/data"
- name: SOUNDCORK_MODE
value: "local"
- name: SOUNDCORK_LOG_DIR
value: "/soundcork/logs/traffic"
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
volumeMounts:
- name: data
mountPath: /soundcork/data
- name: logs
mountPath: /soundcork/logs
volumes:
- name: data
hostPath:
path: /srv/soundcork/data
type: DirectoryOrCreate
- name: logs
hostPath:
path: /srv/soundcork/logs
type: DirectoryOrCreateNote: This example uses
hostPathvolumes. Adapt the volume type for your cluster (e.g.,persistentVolumeClaim, NFS, or a CSI driver).
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: soundcork
namespace: soundcork
spec:
type: ClusterIP
selector:
app: soundcork
ports:
- port: 8000
targetPort: 8000# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: soundcork
namespace: soundcork
annotations:
cert-manager.io/cluster-issuer: letsencrypt
spec:
tls:
- hosts:
- soundcork.example.com
secretName: soundcork-tls
rules:
- host: soundcork.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: soundcork
port:
number: 8000Note: We use Traefik IngressRoute in our setup. Adapt the ingress for your controller (nginx, Traefik, etc.).
kubectl apply -f namespace.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml- Python 3.12
python -m venv venv && source venv/bin/activate
pip install -r requirements.txtDevelopment:
fastapi dev soundcork/main.pyProduction:
gunicorn -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000 soundcork.main:appA sample unit file is included at soundcork.service.example. Copy it to /etc/systemd/system/soundcork.service, adjust paths and user, then:
sudo systemctl daemon-reload
sudo systemctl enable --now soundcorkNote: Make sure
PYTHONPATHincludes the project root and that the working directory is set correctly in your service file or shell environment.
| Variable | Default | Description |
|---|---|---|
base_url |
"" |
Public URL of your SoundCork instance (e.g., https://soundcork.example.com) |
data_dir |
"" |
Path to the speaker data directory |
SOUNDCORK_MODE |
local |
local (recommended) or proxy — see Architecture |
SOUNDCORK_LOG_DIR |
./logs/traffic |
Directory for traffic logs (proxy mode only) |
| Variable | Default | Description |
|---|---|---|
MGMT_USERNAME |
admin |
Username for password-based WebUI and management API login |
MGMT_PASSWORD |
change_me! |
Password for password-based WebUI and management API login |
OIDC_ISSUER_URL |
"" |
OIDC provider issuer URL (e.g., https://authentik.example.com/application/o/soundcork/) |
OIDC_CLIENT_ID |
"" |
OIDC client ID |
OIDC_CLIENT_SECRET |
"" |
OIDC client secret |
When all three OIDC_* variables are set, the WebUI login page shows a "Sign in with SSO" button and authenticates users via your OIDC provider. When any is empty, the WebUI falls back to password-based login using MGMT_USERNAME/MGMT_PASSWORD.
| Variable | Default | Description |
|---|---|---|
SPOTIFY_CLIENT_ID |
"" |
Spotify OAuth client ID — see Spotify Guide |
SPOTIFY_CLIENT_SECRET |
"" |
Spotify OAuth client secret |
SoundCork has three layers of authentication:
| Layer | Protects | Mechanism |
|---|---|---|
| Speaker IP allowlist | /marge/*, /bmx/*, /oauth/* (Bose protocol endpoints) |
Only registered speaker IPs can reach these endpoints |
| WebUI session auth | /webui/* |
Cookie-based sessions — login via password or OIDC |
| Management API auth | /mgmt/* |
HTTP Basic Auth (MGMT_USERNAME/MGMT_PASSWORD) |
SoundCork supports authentication via any standard OpenID Connect provider. It uses the authorization code flow with PKCE and auto-discovers endpoints from the provider's .well-known/openid-configuration.
Tested providers: Authentik, Keycloak. Any OIDC-compliant provider should work.
Steps:
-
Create an OAuth2/OIDC application in your provider with:
- Client type: Confidential
- Grant type: Authorization Code
- Redirect URI:
https://your-soundcork-url/auth/callback - Scopes:
openid,email,profile
-
Set the three environment variables:
OIDC_ISSUER_URL=https://your-provider/application/o/soundcork/ OIDC_CLIENT_ID=soundcork OIDC_CLIENT_SECRET=your-client-secret
-
Restart SoundCork. The login page will now show a "Sign in with SSO" button.
Notes:
- Password login remains available as a fallback even when OIDC is enabled (the password form is hidden from the UI but the API endpoint still works).
- Sessions are in-memory — on server restart, users simply re-authenticate (seamless if already logged in to the SSO provider).
- Logout clears the SoundCork session only; it does not log the user out of the SSO provider.
- For local development, add
http://localhost:8000/auth/callbackas an additional redirect URI in your provider.
- Image:
ghcr.io/timvw/soundcork:main - Multi-architecture:
linux/amd64+linux/arm64(works on Raspberry Pi) - Built automatically via GitHub Actions on every push to main
- Source: see
.github/workflows/docker-publish.yml
curl http://your-server:8000/
# Expected: {"Bose":"Can't Brick Us"}After redirecting your speaker (see Speaker Setup), you should see incoming requests in the server logs.