Skip to content

Repository files navigation

Lobby

Documentation | Screenshots | Videos

A communication portal for media server communities. Lobby gives Plex and Jellyfin users one place for announcements, chat, requests, feedback, shared links, digest emails, and media lifecycle updates.

Lobby dashboard with homepage modules

Features

  • Multi-method authentication: Local, Plex, Jellyfin, and OIDC with cross-provider identity resolution
  • Homepage modules: Announcements, requests, service status, feedback tickets, real-time chat, shared links, recently added media, and leaving soon
  • Invitations: Shareable invite links that provision Plex/Jellyfin access (and a Lobby account) on redemption, with per-invite libraries, usage limits, and time-limited memberships
  • Requests and discovery: Link users to a request portal and let admins connect Overseerr or Jellyseerr for lifecycle-aware request protection
  • Media lifecycle management: Rule-based media aging, grace periods, watch-history checks, and automated cleanup via Radarr/Sonarr
  • Feedback and notifications: User feedback tickets, in-app notifications, and email (SMTP) for announcements, chat mentions, feedback activity, and digests
  • Internationalization: 16 languages (en, es, fr, de, it, pt, nl, sv, no, da, fi, pl, ru, ja, zh, ko)
  • Admin dashboard: Full configuration UI with environment variable override visibility
  • Help: Built-in answers and custom community documentation, with optional declarative management

Quickstart

Docker Run (SQLite)

docker run -d \
  --name lobby \
  -p 8080:8080 \
  -e BASE_URL=http://localhost:8080 \
  -v lobby_config:/config \
  ghcr.io/rinseaid/lobby:latest

Open http://localhost:8080 and complete the bootstrap wizard to create the initial admin account.

Docker Compose From Source

git clone https://github.com/rinseaid/lobby.git
cd lobby
docker compose up -d

The default compose file builds the local source tree and starts Lobby with SQLite. Open http://localhost:8080 and complete the bootstrap wizard. To use PostgreSQL instead, see docker-compose.postgres.yml.

Docker Compose (PostgreSQL)

git clone https://github.com/rinseaid/lobby.git
cd lobby
docker compose -f docker-compose.postgres.yml up -d

First Launch

On first launch with no users in the database, Lobby presents a bootstrap wizard. You can either:

  1. Web UI: Navigate to the app and complete the setup wizard
  2. Environment variables: Set BOOTSTRAP_ADMIN_USERNAME, BOOTSTRAP_ADMIN_PASSWORD, and optionally BOOTSTRAP_ADMIN_EMAIL to auto-create the admin user

After bootstrap, the admin setup wizard guides you through media server connection, authentication, and module configuration.

Configuration

Lobby uses a three-tier configuration system:

  1. Environment variables (highest priority)
  2. TOML file at /config/lobby.toml
  3. Hardcoded defaults (lowest priority)

The admin UI writes changes to the TOML file. Fields managed by environment variables appear greyed out with a "Managed by environment variable" badge.

TOML Configuration

Copy the example config to get started:

cp config/lobby.example.toml /config/lobby.toml

See docs/configuration.md for the full config reference and docs/screenshots.md for generated UI screenshots.

Environment Variables

Server

Variable Default Description
PORT 8080 HTTP listen port
HOST 0.0.0.0 HTTP listen address
BASE_URL http://localhost:8080 Public-facing URL (used for OAuth callbacks, email links)
TRUST_PROXY false Honor X-Forwarded-For/X-Real-IP for client IPs (rate limiting, audit log). Enable ONLY behind a trusted reverse proxy
HIDE_HELP_LINKS false Hide help/documentation links in the UI
SKIP_TLS_VERIFY false Insecure: disables TLS verification for ALL outbound requests (every integration), not per-service
AUTH_RATE_LIMIT_PER_MINUTE 10 Per-IP request cap for auth and invite-redeem endpoints. Raise only behind a shared egress IP or for test harnesses
CONFIG_DIR /config Directory for TOML config and SQLite database
TZ UTC Container timezone (e.g. America/New_York)

Data Storage

Variable Default Description
DATABASE_URL (empty) PostgreSQL connection string. When unset, SQLite is used at CONFIG_DIR/lobby.db

Bootstrap

Variable Default Description
BOOTSTRAP_ADMIN_USERNAME admin Auto-create admin with this username on first launch
BOOTSTRAP_ADMIN_PASSWORD (empty) Admin password (required with username)
BOOTSTRAP_ADMIN_EMAIL (empty) Admin email (optional)

Auth: Local

Variable Default Description
DISABLE_LOCAL_AUTH false Disable local auth. Safety lock: requires at least one other auth method enabled

Auth: Plex

Variable Default Description
AUTH_PLEX_ENABLED false Enable Plex authentication
AUTH_PLEX_SERVER_URL (empty) Plex server URL (e.g. http://plex:32400)
AUTH_PLEX_MACHINE_IDENTIFIER (empty) Plex server machine identifier
AUTH_PLEX_CLIENT_IDENTIFIER lobby Plex client identifier
PLEX_TOKEN (empty) Plex authentication token

Plex requires interactive PIN-based OAuth setup via the admin UI. Environment variables are available for automated deployments.

Auth: Jellyfin

Variable Default Description
AUTH_JELLYFIN_ENABLED false Enable Jellyfin authentication
AUTH_JELLYFIN_SERVER_URL (empty) Jellyfin server URL (e.g. http://jellyfin:8096)
AUTH_JELLYFIN_API_KEY (empty) Jellyfin API key

Auth: OIDC

Variable Default Description
AUTH_OIDC_ENABLED false Enable OIDC authentication
AUTH_OIDC_DISPLAY_NAME (empty) Display name on the login page
AUTH_OIDC_ISSUER_URL (empty) OIDC issuer URL (must support .well-known/openid-configuration)
AUTH_OIDC_CLIENT_ID (empty) OIDC client ID
AUTH_OIDC_CLIENT_SECRET (empty) OIDC client secret
AUTH_OIDC_REDIRECT_URI (empty) OAuth callback URL (e.g. https://lobby.example.com/api/auth/oidc/callback)
AUTH_OIDC_SCOPES openid profile email OIDC scopes. groups is auto-appended when AUTH_OIDC_GROUP_CLAIM is set
AUTH_OIDC_GROUP_CLAIM groups Token claim containing group membership
AUTH_OIDC_ADMIN_GROUPS (empty) Comma-separated group names that grant admin access

Media

Variable Default Description
TMDB_API_KEY (empty) TMDB API key for direct CDN poster URLs (free at themoviedb.org)
MEDIA_SELECTED_LIBRARIES (empty) Comma-separated library IDs to display in Recently Added

SMTP

Variable Default Description
SMTP_ENABLED false Enable email sending
SMTP_HOST (empty) SMTP server hostname
SMTP_PORT 587 SMTP server port
SMTP_SECURE false Use TLS (port 465). When false, uses STARTTLS if available
SMTP_ALLOW_INSECURE_AUTH false Permit sending PLAIN/LOGIN credentials over an unencrypted connection (no TLS/STARTTLS). Leave off unless using a trusted local relay that requires auth without encryption
SMTP_USER (empty) SMTP username
SMTP_PASSWORD (empty) SMTP password
SMTP_FROM_ADDRESS noreply@lobby.local Sender email address
SMTP_FROM_NAME Lobby Sender display name

Chat

Variable Default Description
CHAT_RETENTION_DAYS 30 Delete messages older than this (0 = keep forever)
CHAT_MAX_MESSAGE_LENGTH 2000 Maximum characters per message
CHAT_MENTION_EMAIL_COOLDOWN_HOURS 1 Min hours between mention emails to the same user (rate limit); first is sent promptly, more within the window are batched. 0 = every mention
CHAT_PROFANITY_FILTER_ENABLED false Enable word-based profanity filter
CHAT_INACTIVITY_COLLAPSE_HOURS 24 Auto-collapse chat after inactivity (0 = never)
CHAT_GIFS_ENABLED false Enable GIPHY GIF search in chat
CHAT_GIPHY_API_KEY (empty) GIPHY API key (required when GIFs enabled)

Internationalization

Variable Default Description
DEFAULT_LANGUAGE en Default UI language

Feedback

Variable Default Description
FEEDBACK_ENABLED true Enable user feedback tickets and admin feedback management
FEEDBACK_RETENTION_DAYS 90 Days to retain resolved/closed feedback before cleanup
FEEDBACK_MAX_OPEN_ISSUES 5 Maximum open feedback issues each user can have at one time
FEEDBACK_PRE_CANNED_ISSUES built-in list Comma-separated key:Label issue types shown in the feedback form
FEEDBACK_PLAYBACK_SCAN_THRESHOLD_PCT 15 Percent of runtime that counts as substantial playback when suggesting feedback prompts
FEEDBACK_PLAYBACK_SCAN_MIN_PLAYS 2 Minimum playback count considered by feedback prompt scanning

Playback Diagnostics

Variable Default Description
PLAYBACK_DIAGNOSTICS_ENABLED true Enable log ingestion and scanner evidence correlation
PLAYBACK_DIAGNOSTICS_INTERVAL_MINUTES 15 Minutes between background log ingestion runs
PLAYBACK_DIAGNOSTICS_RETENTION_DAYS 30 Days to retain parsed playback log events
PLAYBACK_DIAGNOSTICS_CORRELATION_WINDOW_MINUTES 15 Minutes around a low-completion play to search for log evidence
PLAYBACK_DIAGNOSTICS_LOCAL_PLEX_LOG_PATHS (empty) Comma-separated Plex log files or directories mounted read-only into Lobby
PLAYBACK_DIAGNOSTICS_LOCAL_JELLYFIN_LOG_PATHS (empty) Comma-separated Jellyfin log files or directories mounted read-only into Lobby
PLAYBACK_DIAGNOSTICS_LOKI_URL (empty) Loki base URL, e.g. http://loki:3100
PLAYBACK_DIAGNOSTICS_LOKI_TENANT_ID (empty) Optional Loki tenant header
PLAYBACK_DIAGNOSTICS_LOKI_USERNAME (empty) Optional Loki basic auth username
PLAYBACK_DIAGNOSTICS_LOKI_PASSWORD (empty) Optional Loki basic auth password
PLAYBACK_DIAGNOSTICS_LOKI_BEARER_TOKEN (empty) Optional Loki bearer token
PLAYBACK_DIAGNOSTICS_LOKI_PLEX_SELECTOR {namespace="plex"} Loki selector for Plex logs
PLAYBACK_DIAGNOSTICS_LOKI_JELLYFIN_SELECTOR {namespace="jellyfin"} Loki selector for Jellyfin logs
PLAYBACK_DIAGNOSTICS_LOKI_LIMIT 1000 Maximum Loki log entries fetched per source per ingest

See Playback Diagnostics for Docker mount examples, Loki setup, sanitization, and testing.

Lifecycle

Variable Default Description
LIFECYCLE_ENABLED false Enable the lifecycle management engine
LIFECYCLE_SCHEDULE_HOUR 2 Hour (0-23) for daily lifecycle evaluation
LIFECYCLE_SCHEDULE_MINUTE 30 Minute (0-59) for daily lifecycle evaluation
LIFECYCLE_SCHEDULE_CRON (empty) Full cron expression (overrides hour/minute when set)
LIFECYCLE_DRY_RUN false Simulate evaluation, staging, and automatic actions without changing lifecycle item state or calling external actions
LIFECYCLE_DELETE_FILES true Delete physical files when removing media from Radarr/Sonarr
LIFECYCLE_ADD_IMPORT_EXCLUSION true Add import exclusion in Radarr/Sonarr when deleting media
LIFECYCLE_LEAVING_SOON_ENABLED true Show Leaving Soon module on homepage
LIFECYCLE_SAVE_TOKEN_SECRET (empty) HMAC secret for magic save tokens. Generate with openssl rand -hex 32
LIFECYCLE_SAVE_TOKEN_TTL_HOURS 72 Save token validity period
LIFECYCLE_PROTECTED_REQUESTERS (empty) Comma-separated Seerr usernames whose requests prevent deletion
LIFECYCLE_USER_SYNC_INTERVAL_HOURS 6 Hours between media server user/watch history syncs
LIFECYCLE_RULES_SYNC_MODE merge How config-managed rules sync to DB: merge (add/update, keep removed) or replace (delete config-managed rules not in files)

Lifecycle: External Services

Variable Default Description
SEERR_URL (empty) Seerr/Overseerr/Jellyseerr URL
SEERR_API_KEY (empty) Seerr API key
SEERR_MAX_AUTO_APPROVE_SEASONS 1 Maximum TV seasons Lobby will submit in one request. Higher-count requests are blocked in Lobby
SEERR_PUBLIC_RECENT_REQUESTS false Allow non-admin users to see shared recent request titles and statuses
SEERR_PUBLIC_RECENT_REQUEST_USERS false Show requester names in shared recent requests. Leave disabled for the private default
SEERR_PUBLIC_URL (empty) Public-facing Seerr URL for links in save emails
RADARR_URL (empty) Radarr URL
RADARR_API_KEY (empty) Radarr API key
SONARR_URL (empty) Sonarr URL
SONARR_API_KEY (empty) Sonarr API key
LIFECYCLE_ARR_SAVE_TAG lobby-saved Tag applied in Radarr/Sonarr when items are saved. Empty to disable
APPRISE_URL (empty) Apprise notification endpoint for lifecycle events
TAUTULLI_URL (empty) Tautulli instance URL (overrides Plex watch history sync)
TAUTULLI_API_KEY (empty) Tautulli API key
JELLYSTAT_URL (empty) Jellystat instance URL (overrides Jellyfin watch history sync)
JELLYSTAT_API_KEY (empty) Jellystat API key

Lifecycle: Media Server Sections

Variable Default Description
PLEX_MOVIES_SECTION (empty) Plex movies library section ID for lifecycle
PLEX_TV_SECTION (empty) Plex TV library section ID for lifecycle
PLEX_MOVIES_KEEP_COLLECTION (empty) Plex collection name to protect movies from deletion
PLEX_TV_KEEP_COLLECTION (empty) Plex collection name to protect TV shows from deletion
PLEX_PUBLIC_URL (empty) Public-facing Plex URL for email links
JELLYFIN_MOVIES_SECTION (empty) Jellyfin movies library ID for lifecycle
JELLYFIN_TV_SECTION (empty) Jellyfin TV library ID for lifecycle
JELLYFIN_MOVIES_KEEP_COLLECTION (empty) Jellyfin collection name to protect movies from deletion
JELLYFIN_TV_KEEP_COLLECTION (empty) Jellyfin collection name to protect TV shows from deletion
JELLYFIN_PUBLIC_URL (empty) Public-facing Jellyfin URL for browser and email links; defaults to AUTH_JELLYFIN_SERVER_URL
LEAVING_BASE_URL (empty) Base URL for "leaving soon" email links (defaults to BASE_URL)

Declarative Lifecycle Rules

Lifecycle rules can be defined as YAML files in CONFIG_DIR/rules/ (default: /config/rules/). This provides a version-controllable, GitOps-friendly alternative to creating rules through the admin UI.

On startup, Lobby loads all .yaml and .yml files from the rules directory and syncs them to the database. Config-managed rules are marked with a lock icon in the admin UI and cannot be edited through the web interface.

Creating a rule file:

name: remove-unwatched-movies
media_type: movie
grace_days: 30
enabled: true
priority: 100

criteria:
  never_watched:
    enabled: true
    check_plex_views: true
  no_active_request:
    enabled: true
  recently_added:
    enabled: true
    days: 14

protections:
  protected_users: [admin]
  protected_tags: [keep-forever]
  protected_collections: [Best Of 2024]

actions:
  - type: delete
  - type: delete_files
  - type: import_exclusion

Sync modes (LIFECYCLE_RULES_SYNC_MODE):

Mode Behavior
merge (default) Add new rules, update existing config-managed rules. Rules removed from files are kept in the DB
replace Same as merge, but config-managed rules not present in files are deleted. UI-managed rules are never deleted

See config/rules/example-remove-unwatched-movies.yaml and config/rules/example-cleanup-old-seasons.yaml for fully commented examples.

Homepage Modules

Admin-configurable modules displayed on the homepage. Each can be enabled, disabled, and reordered.

Module Description
Announcements Scheduled messages with optional email notifications
Requests Homepage search and request flow backed by Overseerr or Jellyseerr
Recent Requests Recent request titles and statuses, with requester privacy controlled by the Seerr sharing settings
Service Status Public health cards for Plex, Jellyfin, request portals, and other monitored services
Chatbox Real-time WebSocket chat with @mentions and GIF support
Custom Links Admin-defined links with icons, grouping, and access control
Feedback User issue reports with admin triage and playback diagnostics
Recently Added New media from Plex/Jellyfin, grouped by library
Leaving Soon Media staged for removal with grace countdown and Save button

Admin UI

The admin area is accessible to users with the admin flag. The sidebar is organized into:

Integrations (collapsible): Media Servers, Downloaders, Discovery, Service Status

Configuration (collapsible): Authentication, Users, Invitations, Notifications

Content (collapsible): Modules, Help, Announcements, Chat, Feedback, Digest, Lifecycle, Watch History, Links, Recently Added

Standalone: System

Health Endpoints

Endpoint Description
GET /healthz Returns 200 if the server is running
GET /readyz Returns 200 if the database is reachable

Architecture

Layer Technology
Backend Go (chi, sqlx, gorilla/websocket)
Frontend React, Vite, Tailwind CSS, React Query
Database SQLite (default) or PostgreSQL
Deployment Single binary, multi-stage Docker (~30MB)

The Go binary embeds the built React SPA and serves it at /. API routes are at /api/. Non-API requests fall through to index.html for client-side routing.

Documentation

Document Description
Configuration Three-tier config system, full reference
Authentication All auth methods, identity resolution, OIDC groups
Users User database, local creation, Plex/Jellyfin import, sync
Invitations Invite links, provisioning, expiration, onboarding
Media Servers Connecting Plex and Jellyfin, libraries, tokens
Arr Instances Multi-instance Radarr/Sonarr configuration
Watch History Tautulli/Jellystat sync, native sources, lifecycle feed
Discovery TMDB posters, rating sources, Overseerr/Jellyseerr
Modules Homepage modules, layout, navigation tabs
Help Built-in answers and custom community documentation
Recently Added Recently added homepage module and tab
Leaving Soon Homepage module, Save control, signed save tokens
Announcements Time-bounded broadcasts, scheduling, templates
Service Status Plex, Jellyfin, custom HTTP checks, incident announcements
Chat WebSocket chat, mentions, profanity filter, GIFs
Feedback Issue tickets, comments, attachments, scanner
Notifications In-app and email notifications, SMTP, digests
Digest Digest rules admin UI, templates, test email
Links Custom navigation links, groups, visibility
Lifecycle Lifecycle engine overview, staging, grace periods
Lifecycle Rules Criteria types, protections, rule priority
Declarative Rules YAML rule files, sync modes, GitOps workflow
Lifecycle Actions Action pipeline, Radarr/Sonarr integration
Playback Diagnostics Playback scan, issue detection, Docker mounts
Internationalization 16 languages, locale detection, adding a language
Screenshots Capturing and updating documentation screenshots
Development Local dev setup, project structure, testing

License

MIT

About

Communication portal for media server communities

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages