Describe the bug
A valid login succeeds according to the API, but the UI never leaves the login screen. After submitting valid credentials, the login form shows "Success / Login successful! Redirecting..." and then stays there.
If I manually open /web/dashboard/, the page is blank/white.
The backend accepts the login request (POST /api/v1/auth/cookie/login returns 204) and immediately after that /api/v1/users/me can return 200, but frontend routes like /web/ and /web/dashboard/ are served with HTTP 404. Later dashboard API calls begin returning 401, and the UI appears stuck or blank.
To Reproduce
Steps to reproduce the behavior:
- Deploy MediaManager
v1.12.3 with Docker Compose.
- Start with a Postgres-backed MediaManager setup and a default/admin user.
- Go to
http://server-ip:8000/web/login.
- Log in with valid credentials.
- See "Success / Login successful! Redirecting..." remain on the login screen.
- Manually open
http://server-ip:8000/web/dashboard/.
- See a blank/white page.
Expected behavior
After a successful login, the browser should navigate to the dashboard and render the dashboard UI.
Directly opening /web/dashboard/ should also load the SPA route successfully.
Screenshots
Login page: (No redirect happens!)

After manually navigating to the dashboard page:

Server Logs
Relevant excerpts:
mediamanager | POST /api/v1/auth/cookie/login HTTP/1.1" 204
mediamanager | GET /api/v1/users/me HTTP/1.1" 200
mediamanager | GET /web/dashboard/ HTTP/1.1" 404
mediamanager | GET /web/_app/version.json HTTP/1.1" 304
mediamanager | GET /api/v1/users/me HTTP/1.1" 401
mediamanager | POST /api/v1/auth/cookie/logout HTTP/1.1" 401
mediamanager | GET /api/v1/tv/shows HTTP/1.1" 401
mediamanager | GET /api/v1/movies HTTP/1.1" 401
On startup, Postgres also logs repeated scheduler duplicate key errors:
mediamanager-postgres | ERROR: duplicate key value violates unique constraint "apscheduler_jobs_pkey"
mediamanager-postgres | DETAIL: Key (id)=(import_all_movie_torrents) already exists.
mediamanager-postgres | STATEMENT: INSERT INTO apscheduler_jobs (id, next_run_time, job_state) VALUES ($1::VARCHAR, $2, $3)
mediamanager-postgres | ERROR: duplicate key value violates unique constraint "apscheduler_jobs_pkey"
mediamanager-postgres | DETAIL: Key (id)=(import_all_show_torrents) already exists.
mediamanager-postgres | STATEMENT: INSERT INTO apscheduler_jobs (id, next_run_time, job_state) VALUES ($1::VARCHAR, $2, $3)
mediamanager-postgres | ERROR: duplicate key value violates unique constraint "apscheduler_jobs_pkey"
mediamanager-postgres | DETAIL: Key (id)=(auto_download_all_approved_season_requests) already exists.
mediamanager-postgres | STATEMENT: INSERT INTO apscheduler_jobs (id, next_run_time, job_state) VALUES ($1::VARCHAR, $2, $3)
mediamanager-postgres | ERROR: duplicate key value violates unique constraint "apscheduler_jobs_pkey"
mediamanager-postgres | DETAIL: Key (id)=(auto_download_all_approved_movie_requests) already exists.
mediamanager-postgres | STATEMENT: INSERT INTO apscheduler_jobs (id, next_run_time, job_state) VALUES ($1::VARCHAR, $2, $3)
mediamanager-postgres | ERROR: duplicate key value violates unique constraint "apscheduler_jobs_pkey"
mediamanager-postgres | DETAIL: Key (id)=(update_all_movies_metadata) already exists.
mediamanager-postgres | STATEMENT: INSERT INTO apscheduler_jobs (id, next_run_time, job_state) VALUES ($1::VARCHAR, $2, $3)
mediamanager-postgres | ERROR: duplicate key value violates unique constraint "apscheduler_jobs_pkey"
mediamanager-postgres | DETAIL: Key (id)=(update_all_non_ended_shows_metadata) already exists.
mediamanager-postgres | STATEMENT: INSERT INTO apscheduler_jobs (id, next_run_time, job_state) VALUES ($1::VARCHAR, $2, $3)
Filesystem check warning:
mediamanager | ERROR - media_manager.main - run_filesystem_checks(): Hardlink creation failed, falling back to copying files
mediamanager | Traceback (most recent call last):
mediamanager | File "/app/media_manager/filesystem_checks.py", line 35, in run_filesystem_checks
mediamanager | test_hardlink.hardlink_to(test_torrent_file)
mediamanager | File "/usr/local/lib/python3.13/pathlib/_local.py", line 798, in hardlink_to
mediamanager | os.link(target, self)
mediamanager | OSError: [Errno 18] Invalid cross-device link: '/downloads/.media_manager_test_dir/.media_manager.test.torrent' -> '/downloads/tvshows/.media_manager_test_dir/.media_manager.test.hardlink'
Earlier, before increasing Postgres/SQLAlchemy pool limits locally, the backend also produced:
sqlalchemy.exc.TimeoutError: QueuePool limit of size 5 overflow 10 reached, connection timed out, timeout 30.00
Browser Logs
Not captured yet.
Network-level observations:
POST /api/v1/auth/cookie/login returns 204
/api/v1/users/me returns 200 immediately after login
/web/dashboard/ returns 404 while serving the SPA fallback HTML
- Later dashboard API calls return
401
Version
v1.12.3
Additional context
The same behavior occurs when accessing MediaManager directly through the published container port, without a Caddy reverse proxy in front of it. This does not appear to be reverse-proxy-specific.
The app appears to serve SvelteKit fallback HTML for /web/ and /web/dashboard/, but with HTTP status 404. That may be related to the blank dashboard and failed client-side routing after successful login.
docker-compose.yaml
services:
mediamanager:
image: quay.io/maxdorninger/mediamanager:latest
container_name: mediamanager
environment:
- CONFIG_DIR=/app/config
- TZ=Europe/Amsterdam
ports:
- "8000:8000"
volumes:
- /mnt/services/arr-stack/mediamanager-config:/app/config
- /mnt/services/arr-stack/mediamanager-images:/app/images
- /mnt/data:/downloads
depends_on:
mediamanager-db:
condition: service_healthy
restart: unless-stopped
mediamanager-db:
image: postgres:17
container_name: mediamanager-postgres
environment:
POSTGRES_USER: MediaManager
POSTGRES_DB: MediaManager
POSTGRES_PASSWORD: REDACTED
volumes:
- /mnt/services/arr-stack/mediamanager-postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
prowlarr:
image: linuxserver/prowlarr:latest
container_name: prowlarr
environment:
- PUID=1000
- PGID=100
- TZ=Europe/Amsterdam
volumes:
- /mnt/services/arr-stack/prowlarr-config:/config
ports:
- "9696:9696"
restart: unless-stopped
flaresolverr:
image: ghcr.io/flaresolverr/flaresolverr:latest
container_name: flaresolverr
environment:
- LOG_LEVEL=info
- LOG_HTML=false
- CAPTCHA_SOLVER=none
- TZ=Europe/Amsterdam
ports:
- "8191:8191"
restart: unless-stopped
config.toml
[misc]
frontend_url = "http://server-ip:8000"
cors_urls = ["http://server-ip:8000"]
image_directory = "/app/images"
tv_directory = "/downloads/tvshows"
movie_directory = "/downloads/movies"
torrent_directory = "/downloads"
development = false
[[misc.tv_libraries]]
name = "TV Shows"
path = "/downloads/tvshows"
[[misc.movie_libraries]]
name = "Movies"
path = "/downloads/movies"
[database]
host = "mediamanager-db"
port = 5432
user = "MediaManager"
password = "REDACTED"
dbname = "MediaManager"
[auth]
email_password_resets = false
token_secret = "REDACTED"
session_lifetime = 86400
admin_emails = ["admin@example.com"]
[auth.openid_connect]
enabled = false
client_id = ""
client_secret = ""
configuration_endpoint = "https://openid.example.com/.well-known/openid-configuration"
name = "OpenID"
[notifications]
[notifications.smtp_config]
smtp_host = "smtp.example.com"
smtp_port = 587
smtp_user = "admin"
smtp_password = "admin"
from_email = "mediamanager@example.com"
use_tls = true
[notifications.email_notifications]
enabled = false
emails = []
[notifications.gotify]
enabled = false
api_key = ""
url = "https://gotify.example.com"
[notifications.ntfy]
enabled = false
url = "https://ntfy.sh/your-topic"
[notifications.pushover]
enabled = false
api_key = ""
user = ""
[torrents]
[torrents.qbittorrent]
enabled = false
host = "http://gluetun"
port = 8085
username = ""
password = ""
[torrents.transmission]
enabled = true
username = "REDACTED"
password = "REDACTED"
https_enabled = false
host = "gluetun"
port = 9091
path = "/transmission/rpc"
[torrents.sabnzbd]
enabled = true
host = "http://sabnzbd"
port = 8080
api_key = "REDACTED"
base_path = "/api"
[indexers]
[indexers.prowlarr]
enabled = true
url = "http://prowlarr:9696"
api_key = "REDACTED"
timeout_seconds = 60
[indexers.jackett]
enabled = false
url = "http://jackett:9117"
api_key = ""
indexers = []
timeout_seconds = 60
[[indexers.title_scoring_rules]]
name = "prefer_h265"
keywords = ["h265", "hevc", "x265", "h.265", "x.265"]
score_modifier = 100
negate = false
[[indexers.title_scoring_rules]]
name = "avoid_cam"
keywords = ["cam", "camrip", "bdscr", "ddc", "dvdscreener", "dvdscr", "hdcam", "hdtc", "hdts", "scr", "screener", "telesync", "ts", "webscreener", "tc", "telecine", "tvrip"]
score_modifier = -10000
negate = false
[[indexers.indexer_flag_scoring_rules]]
name = "prefer_freeleech"
flags = ["freeleech", "freeleech75"]
score_modifier = 100
negate = false
[[indexers.indexer_flag_scoring_rules]]
name = "reject_nuked"
flags = ["nuked"]
score_modifier = -10000
negate = false
[[indexers.scoring_rule_sets]]
name = "default"
libraries = ["ALL_TV", "ALL_MOVIES"]
rule_names = ["prefer_h265", "avoid_cam", "reject_nuked", "prefer_freeleech"]
[metadata]
[metadata.tmdb]
tmdb_relay_url = "https://metadata-relay.dorninger.co/tmdb"
primary_languages = [""]
default_language = "en"
[metadata.tvdb]
tvdb_relay_url = "https://metadata-relay.dorninger.co/tvdb"
Describe the bug
A valid login succeeds according to the API, but the UI never leaves the login screen. After submitting valid credentials, the login form shows "Success / Login successful! Redirecting..." and then stays there.
If I manually open
/web/dashboard/, the page is blank/white.The backend accepts the login request (
POST /api/v1/auth/cookie/loginreturns204) and immediately after that/api/v1/users/mecan return200, but frontend routes like/web/and/web/dashboard/are served with HTTP404. Later dashboard API calls begin returning401, and the UI appears stuck or blank.To Reproduce
Steps to reproduce the behavior:
v1.12.3with Docker Compose.http://server-ip:8000/web/login.http://server-ip:8000/web/dashboard/.Expected behavior
After a successful login, the browser should navigate to the dashboard and render the dashboard UI.
Directly opening
/web/dashboard/should also load the SPA route successfully.Screenshots


Login page: (No redirect happens!)
After manually navigating to the dashboard page:
Server Logs
Relevant excerpts:
On startup, Postgres also logs repeated scheduler duplicate key errors:
Filesystem check warning:
Earlier, before increasing Postgres/SQLAlchemy pool limits locally, the backend also produced:
Browser Logs
Not captured yet.
Network-level observations:
POST /api/v1/auth/cookie/loginreturns204/api/v1/users/mereturns200immediately after login/web/dashboard/returns404while serving the SPA fallback HTML401Version
v1.12.3
Additional context
The same behavior occurs when accessing MediaManager directly through the published container port, without a Caddy reverse proxy in front of it. This does not appear to be reverse-proxy-specific.
The app appears to serve SvelteKit fallback HTML for
/web/and/web/dashboard/, but with HTTP status404. That may be related to the blank dashboard and failed client-side routing after successful login.docker-compose.yaml
config.toml