# 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
<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:**
```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: <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
