# Automatic Database Creation - PostgreSQL ## Overview Jellyfin now automatically ensures the PostgreSQL database exists before applying migrations. This eliminates the need for manual database creation. ## How It Works ### Startup Sequence 1. **Database Existence Check** (New!) - Jellyfin connects to PostgreSQL's `postgres` maintenance database - Checks if the target database (e.g., `jellyfin`) exists - If not, creates the database automatically 2. **Database Schema Creation** - Connects to the target database - Applies EF Core migrations - Creates all 30 tables, indexes, and constraints 3. **Application Startup** - Continues normal Jellyfin initialization ### Code Flow ``` Startup ↓ JellyfinMigrationService.MigrateStepAsync() ↓ Check if PostgreSQL provider? ↓ YES PostgresDatabaseProvider.EnsureDatabaseExistsAsync() ↓ ├─ Connect to 'postgres' database ├─ Check if target database exists ├─ If not → CREATE DATABASE └─ GRANT privileges ↓ Continue with migrations ↓ Apply EF Core migrations (create tables) ``` ## What Gets Created ### Phase 1: Database Creation (If Needed) When the database doesn't exist, Jellyfin automatically runs: ```sql -- Check existence SELECT 1 FROM pg_database WHERE datname = 'jellyfin'; -- Create if missing CREATE DATABASE "jellyfin" OWNER "jellyfin"; -- Grant permissions GRANT ALL PRIVILEGES ON DATABASE "jellyfin" TO "jellyfin"; ``` ### Phase 2: Schema Creation (Always) EF Core migrations create all tables: - 30 tables with relationships - Primary keys, foreign keys, indexes - PostgreSQL-optimized data types ## Requirements ### PostgreSQL User Permissions The user specified in `database.xml` must have: 1. **Connection to `postgres` database**: ```sql -- Usually granted by default GRANT CONNECT ON DATABASE postgres TO jellyfin; ``` 2. **CREATE DATABASE privilege**: ```sql -- Option 1: Make user a CREATEDB role ALTER USER jellyfin CREATEDB; -- Option 2: Grant via superuser (less preferred) ALTER USER jellyfin WITH SUPERUSER; ``` 3. **Owner of the database** (automatically set during creation) ### Minimal Setup Required **Before (Manual):** ```bash # You had to do this manually psql -U postgres -c "CREATE DATABASE jellyfin;" psql -U postgres -c "CREATE USER jellyfin WITH PASSWORD 'pass';" psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE jellyfin TO jellyfin;" ``` **After (Automatic):** ```bash # Only create user with CREATEDB privilege psql -U postgres -c "CREATE USER jellyfin WITH PASSWORD 'pass' CREATEDB;" # Then configure Jellyfin and start - database auto-created! ``` ## Configuration No changes needed to your existing `database.xml`: ```xml Jellyfin-PostgreSQL NoLock host localhost port 5432 database jellyfin username jellyfin password your_password ``` ## Logs When the feature runs, you'll see logs like: ### Database Doesn't Exist ``` [INFO] Checking if PostgreSQL database exists... [INFO] PostgreSQL database 'jellyfin' does not exist. Creating... [INFO] PostgreSQL database 'jellyfin' created successfully [INFO] Granted all privileges on database 'jellyfin' to user 'jellyfin' ``` ### Database Already Exists ``` [INFO] Checking if PostgreSQL database exists... [INFO] PostgreSQL database 'jellyfin' already exists ``` ### Errors ``` [ERROR] Failed to ensure PostgreSQL database 'jellyfin' exists. Error: permission denied to create database ``` ## Troubleshooting ### Error: "permission denied to create database" **Cause:** User doesn't have CREATE DATABASE privilege **Solution:** ```sql -- As PostgreSQL superuser ALTER USER jellyfin CREATEDB; ``` ### Error: "database already exists" **Not an error!** Jellyfin detected existing database and skipped creation. ### Error: "could not connect to database postgres" **Cause:** User doesn't have connection permission to maintenance database **Solution:** ```sql -- As PostgreSQL superuser GRANT CONNECT ON DATABASE postgres TO jellyfin; ``` ### Error: "role 'jellyfin' does not exist" **Cause:** User hasn't been created yet **Solution:** ```sql -- As PostgreSQL superuser CREATE USER jellyfin WITH PASSWORD 'secure_password' CREATEDB; ``` ## Security Considerations ### CREATEDB Privilege Granting `CREATEDB` allows the user to create databases. This is safe for Jellyfin's dedicated user because: - User can only create databases, not modify system tables - User doesn't need SUPERUSER privilege - Follows principle of least privilege - Alternative: Pre-create database manually (defeats automation) ### Recommended Setup **Production:** ```sql -- Create user with limited privileges CREATE USER jellyfin WITH PASSWORD 'strong_password' CREATEDB LOGIN; -- Restrict to specific database after creation REVOKE CREATEDB FROM jellyfin; -- Optional: Remove after first run ``` **Development:** ```sql -- More permissive for convenience CREATE USER jellyfin WITH PASSWORD 'dev_password' CREATEDB LOGIN; ``` ## Comparison: Before vs After | Aspect | Manual (Before) | Automatic (After) | |--------|-----------------|-------------------| | **Steps** | 3-4 SQL commands | 1 SQL command (create user) | | **Errors** | Easy to forget steps | Impossible - automated | | **Docker** | Need init scripts | Just configure user | | **Fresh Install** | Extra documentation | Works out of box | | **Error Handling** | User troubleshoots | Jellyfin logs clear errors | ## Implementation Details ### PostgresDatabaseProvider.EnsureDatabaseExistsAsync() **Purpose:** Check and create database before migrations **Process:** 1. Parse configuration to extract connection details 2. Build connection string to `postgres` maintenance database 3. Connect and check if target database exists 4. Create database if missing with proper owner 5. Grant all privileges to the user 6. Log success or failure **Error Handling:** - Catches all exceptions - Logs detailed error messages - Re-throws to prevent silent failures - Shows user exactly what went wrong ### JellyfinMigrationService Integration **Hook Point:** `MigrateStepAsync()` at `CoreInitialisation` stage **Logic:** ```csharp if (stage == CoreInitialisation && provider is PostgresDatabaseProvider postgres) { await postgres.EnsureDatabaseExistsAsync(config); } ``` **Why CoreInitialisation?** - Earliest stage where database is needed - Before any migrations run - After configuration is loaded - Perfect timing for database creation ## Testing ### Manual Test ```bash # 1. Create user only psql -U postgres -c "CREATE USER jellyfin WITH PASSWORD 'test' CREATEDB;" # 2. DO NOT create database # 3. Configure Jellyfin with PostgreSQL # 4. Start Jellyfin # Expected: Database created automatically psql -U postgres -c "\l" | grep jellyfin # Should show: jellyfin | jellyfin | ... ``` ### Clean Test ```bash # 1. Drop existing database (if any) psql -U postgres -c "DROP DATABASE IF EXISTS jellyfin;" # 2. Start Jellyfin # 3. Verify database created psql -U jellyfin -d jellyfin -c "\dt" # Should show all 30 tables ``` ## Limitations ### What's Automated ✅ Database existence check ✅ Database creation ✅ Owner assignment ✅ Privilege granting ✅ Error logging ### What's Not Automated ❌ User creation (must exist) ❌ CREATEDB privilege (must be granted) ❌ PostgreSQL installation ❌ Network/firewall configuration ### Why These Aren't Automated - **User creation**: Requires PostgreSQL superuser credentials - **Privilege granting**: Security implications - **Installation**: OS-dependent - **Network**: Environment-specific ## Migration from Manual Setup If you previously created the database manually: **Impact:** None! The feature detects existing database and skips creation. **Steps:** 1. Update Jellyfin to version with this feature 2. Start Jellyfin normally 3. See log: "PostgreSQL database 'jellyfin' already exists" No migration or reconfiguration needed. ## Best Practices ### Docker/Containers **docker-compose.yml:** ```yaml services: postgres: image: postgres:15 environment: POSTGRES_USER: jellyfin POSTGRES_PASSWORD: secure_password # Don't set POSTGRES_DB - let Jellyfin create it volumes: - postgres_data:/var/lib/postgresql/data jellyfin: image: jellyfin/jellyfin depends_on: - postgres environment: - JELLYFIN_DB_TYPE=Jellyfin-PostgreSQL - JELLYFIN_DB_HOST=postgres - JELLYFIN_DB_USER=jellyfin - JELLYFIN_DB_PASS=secure_password ``` Note: By NOT setting `POSTGRES_DB`, we let Jellyfin create the database itself. ### Kubernetes ```yaml apiVersion: v1 kind: Secret metadata: name: jellyfin-db type: Opaque data: username: amVsbHlmaW4= # jellyfin password: --- apiVersion: v1 kind: ConfigMap metadata: name: jellyfin-db-config data: init-user.sql: | CREATE USER jellyfin WITH PASSWORD 'from-secret' CREATEDB; ``` ## Summary ✅ **Automated database creation** ✅ **Minimal user setup required** ✅ **Clear error messages** ✅ **Backward compatible** ✅ **Production-ready** ✅ **Fully logged** The automatic database creation feature makes PostgreSQL setup as simple as SQLite while maintaining security and reliability. --- **Feature Added:** February 22, 2026 **Status:** ✅ Production Ready **Tested:** ✅ Fresh install, existing database **Breaking Changes:** None