Files
pgsql-jellyfin/docs/AUTOMATIC_DATABASE_CREATION.md
wjones a3eb4b1b57 Add automatic PostgreSQL database creation and migration
- 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.
2026-02-22 18:51:24 -05:00

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

  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:

-- 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:

    -- Usually granted by default
    GRANT CONNECT ON DATABASE postgres TO jellyfin;
    
  2. 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;
    
  3. 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)

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:

  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:

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:

  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:

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