- Added Jellyfin.Database.Providers.Postgres project with full EF Core migration support for PostgreSQL. - Implemented automatic database creation and privilege assignment on startup. - Generated initial migration and model snapshot for PostgreSQL schema. - Updated build, test, and dependency files to include PostgreSQL provider and Npgsql packages. - Added PowerShell script for generating and testing PostgreSQL migrations. - No changes to application logic outside database provider/migration infrastructure.
9.8 KiB
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
-
Database Existence Check (New!)
- Jellyfin connects to PostgreSQL's
postgresmaintenance database - Checks if the target database (e.g.,
jellyfin) exists - If not, creates the database automatically
- Jellyfin connects to PostgreSQL's
-
Database Schema Creation
- Connects to the target database
- Applies EF Core migrations
- Creates all 30 tables, indexes, and constraints
-
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:
-- 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:
-
Connection to
postgresdatabase:-- Usually granted by default GRANT CONNECT ON DATABASE postgres TO jellyfin; -
CREATE DATABASE privilege:
-- Option 1: Make user a CREATEDB role ALTER USER jellyfin CREATEDB; -- Option 2: Grant via superuser (less preferred) ALTER USER jellyfin WITH SUPERUSER; -
Owner of the database (automatically set during creation)
Minimal Setup Required
Before (Manual):
# 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):
# 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:
<DatabaseType>Jellyfin-PostgreSQL</DatabaseType>
<LockingBehavior>NoLock</LockingBehavior>
<CustomProviderOptions>
<Options>
<CustomDatabaseOption>
<Key>host</Key>
<Value>localhost</Value>
</CustomDatabaseOption>
<CustomDatabaseOption>
<Key>port</Key>
<Value>5432</Value>
</CustomDatabaseOption>
<CustomDatabaseOption>
<Key>database</Key>
<Value>jellyfin</Value>
</CustomDatabaseOption>
<CustomDatabaseOption>
<Key>username</Key>
<Value>jellyfin</Value>
</CustomDatabaseOption>
<CustomDatabaseOption>
<Key>password</Key>
<Value>your_password</Value>
</CustomDatabaseOption>
</Options>
</CustomProviderOptions>
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:
-- 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:
-- As PostgreSQL superuser
GRANT CONNECT ON DATABASE postgres TO jellyfin;
Error: "role 'jellyfin' does not exist"
Cause: User hasn't been created yet
Solution:
-- 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:
-- 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:
-- 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:
- Parse configuration to extract connection details
- Build connection string to
postgresmaintenance database - Connect and check if target database exists
- Create database if missing with proper owner
- Grant all privileges to the user
- 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:
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
# 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
# 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:
- Update Jellyfin to version with this feature
- Start Jellyfin normally
- See log: "PostgreSQL database 'jellyfin' already exists"
No migration or reconfiguration needed.
Best Practices
Docker/Containers
docker-compose.yml:
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
apiVersion: v1
kind: Secret
metadata:
name: jellyfin-db
type: Opaque
data:
username: amVsbHlmaW4= # jellyfin
password: <base64-encoded>
---
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