Files
pgsql-jellyfin/docs/AUTOMATIC_DATABASE_CREATION.md
T
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

414 lines
9.8 KiB
Markdown

# 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