Files
pgsql-jellyfin/docs/database/postgres-backup-configuration.md
wjones c76853a442 PostgreSQL: Production fixes—UPSERT, remote backup, auth logs
- Fix: Atomic UPSERT for BaseItemProviders (resolves duplicate key errors during concurrent metadata refresh; clears navigation property to prevent EF Core tracking conflicts)
- Add: Remote PostgreSQL backup support (removes localhost-only restriction; works with pg_dump/pg_restore for both local and remote DBs)
- Add: Configurable backup disable option (`disable-backups`)
- Fix: Query timeout and performance (documented `command-timeout` config, added performance index scripts)
- Fix: Authentication errors now log as warnings with clear messages (ExceptionMiddleware), reducing log noise
- Fix: SyncPlay authorization handler validates user before lookup, logs warnings for unauthenticated/unknown users (returns 403/404)
- Fix: Database deadlock detection logs warnings and allows EF Core auto-retry
- Add: Configurable LibraryMonitorDelay (min 30s, default 60s)
- Fix: SQLite migration filtering—skip SQLite-only migrations on PostgreSQL
- Chore: Suppress StyleCop warnings (SA1137, etc.) for project consistency
- Docs: 21 documentation files added/updated (config, backup, performance, troubleshooting, session summary)
- All changes are backward compatible and production-ready
2026-03-03 16:35:27 -05:00

7.7 KiB

PostgreSQL Backup Configuration

This document explains how to configure PostgreSQL backup options in Jellyfin using the database.xml configuration file.

IMPORTANT: Local Database Requirement

pg_dump and pg_restore backup features are ONLY available when the PostgreSQL database host is localhost or 127.0.0.1 (or ::1 for IPv6).

  • Local database (localhost/127.0.0.1): Automated backups via pg_dump/pg_restore are enabled
  • Remote database: Backup settings are ignored; you must manage backups externally

When using a remote PostgreSQL server, Jellyfin will log:

"PostgreSQL database is on remote server ({Host}). Backup operations via pg_dump/pg_restore are disabled"

And backup operations will show:

"Backup deletion is not implemented for PostgreSQL on a remote server. Manage backups externally."

Configuration File Location

The database.xml file should be placed in your Jellyfin configuration directory:

  • Windows: %AppData%\Jellyfin\Server\config\database.xml
  • Linux: ~/.config/jellyfin/config/database.xml or /etc/jellyfin/config/database.xml
  • Docker: Mount to /config/database.xml

Basic Configuration

Here's a minimal configuration for PostgreSQL with default backup settings:

<DatabaseConfigurationOptions>
  <DatabaseType>Jellyfin-PostgreSQL</DatabaseType>
  <LockingBehavior>NoLock</LockingBehavior>
  
  <CustomProviderOptions>
    <PluginName>Jellyfin-PostgreSQL</PluginName>
    <PluginAssembly>Jellyfin.Database.Providers.Postgres</PluginAssembly>
    <ConnectionString>Host=localhost;Port=5432;Database=jellyfin;Username=jellyfin;Password=your_password</ConnectionString>
  </CustomProviderOptions>
  
  <!-- Backup configuration (all settings are optional) -->
  <BackupOptions>
    <PgDumpPath>pg_dump</PgDumpPath>
    <BackupFormat>custom</BackupFormat>
  </BackupOptions>
</DatabaseConfigurationOptions>

Backup Options Reference

PgDumpPath

Type: string (optional)
Default: Searches in system PATH

The full path to the pg_dump executable. If not specified, Jellyfin will search common locations:

  • Windows: C:\Program Files\PostgreSQL\{version}\bin\pg_dump.exe
  • Linux: /usr/bin/pg_dump, /usr/local/bin/pg_dump

Examples:

<!-- Windows -->
<PgDumpPath>C:\Program Files\PostgreSQL\16\bin\pg_dump.exe</PgDumpPath>

<!-- Linux -->
<PgDumpPath>/usr/bin/pg_dump</PgDumpPath>

<!-- Search in PATH -->
<PgDumpPath>pg_dump</PgDumpPath>

PgRestorePath

Type: string (optional)
Default: Searches in system PATH

Path to the pg_restore executable. Same search behavior as PgDumpPath.

BackupFormat

Type: string
Default: custom
Valid values: custom, plain, directory, tar

The backup file format:

  • custom: Compressed custom format (recommended) - can be restored with pg_restore
  • plain: Plain SQL script - can be restored with psql
  • directory: Directory format with one file per table
  • tar: Tar archive format
<BackupFormat>custom</BackupFormat>

IncludeBlobs

Type: boolean
Default: true

Whether to include large objects (BLOBs) in the backup.

<IncludeBlobs>true</IncludeBlobs>

CompressionLevel

Type: integer (0-9)
Default: 6

Compression level for custom and directory formats. Higher values mean better compression but slower speed.

  • 0 = No compression
  • 9 = Maximum compression
  • 6 = Good balance (recommended)
<CompressionLevel>6</CompressionLevel>

TimeoutSeconds

Type: integer
Default: 1800 (30 minutes)

Maximum time in seconds to wait for backup/restore operations to complete.

<TimeoutSeconds>1800</TimeoutSeconds>

VerboseOutput

Type: boolean
Default: true

Enable verbose logging output from pg_dump/pg_restore operations.

<VerboseOutput>true</VerboseOutput>

ParallelJobs

Type: integer (optional)
Default: null

Number of parallel jobs for backup operations. Only works with directory format.

<BackupFormat>directory</BackupFormat>
<ParallelJobs>4</ParallelJobs>

AdditionalArguments

Type: string (optional)
Default: null

Additional command-line arguments to pass to pg_dump.

Examples:

<!-- Exclude specific table data -->
<AdditionalArguments>--exclude-table-data=public.temp_logs</AdditionalArguments>

<!-- Exclude schema -->
<AdditionalArguments>--exclude-schema=archive</AdditionalArguments>

<!-- Multiple arguments -->
<AdditionalArguments>--exclude-table-data=public.logs --exclude-schema=test</AdditionalArguments>

Complete Example Configuration

<DatabaseConfigurationOptions>
  <DatabaseType>Jellyfin-PostgreSQL</DatabaseType>
  <LockingBehavior>NoLock</LockingBehavior>
  
  <CustomProviderOptions>
    <PluginName>Jellyfin-PostgreSQL</PluginName>
    <PluginAssembly>Jellyfin.Database.Providers.Postgres</PluginAssembly>
    <ConnectionString>Host=localhost;Port=5432;Database=jellyfin;Username=jellyfin;Password=your_password</ConnectionString>
  </CustomProviderOptions>
  
  <BackupOptions>
    <!-- Paths to executables -->
    <PgDumpPath>C:\Program Files\PostgreSQL\16\bin\pg_dump.exe</PgDumpPath>
    <PgRestorePath>C:\Program Files\PostgreSQL\16\bin\pg_restore.exe</PgRestorePath>
    
    <!-- Backup format and compression -->
    <BackupFormat>custom</BackupFormat>
    <CompressionLevel>6</CompressionLevel>
    <IncludeBlobs>true</IncludeBlobs>
    
    <!-- Performance and timeout -->
    <TimeoutSeconds>3600</TimeoutSeconds>
    <VerboseOutput>true</VerboseOutput>
    
    <!-- Optional: Parallel processing for directory format -->
    <!-- <ParallelJobs>4</ParallelJobs> -->
    
    <!-- Optional: Additional pg_dump arguments -->
    <!-- <AdditionalArguments>--exclude-table-data=public.temp_data</AdditionalArguments> -->
  </BackupOptions>
</DatabaseConfigurationOptions>

Usage in Code

The backup service will automatically read these settings from database.xml:

// Inject the service
public class MyClass
{
    private readonly PostgresBackupService _backupService;
    
    public MyClass(PostgresBackupService backupService)
    {
        _backupService = backupService;
    }
    
    // Create a backup
    public async Task CreateBackup()
    {
        var backupPath = await _backupService.CreateBackupAsync("/path/to/backups");
        Console.WriteLine($"Backup created: {backupPath}");
    }
    
    // Restore a backup
    public async Task RestoreBackup(string backupFile)
    {
        await _backupService.RestoreBackupAsync(backupFile);
        Console.WriteLine("Backup restored successfully");
    }
}

Troubleshooting

"pg_dump executable not found"

  1. Verify PostgreSQL is installed
  2. Check that pg_dump is in your system PATH, or
  3. Specify the full path in <PgDumpPath> configuration

Permission Issues

  • Ensure the user running Jellyfin has execute permissions for pg_dump
  • Verify database credentials have backup privileges

Backup Takes Too Long

  • Increase <TimeoutSeconds> value
  • Consider using <ParallelJobs> with directory format for large databases
  • Reduce <CompressionLevel> for faster backups

Restore Fails

  • Verify the backup file format matches what pg_restore expects
  • Check that the target database exists and is accessible
  • Review logs for specific error messages

Security Notes

  1. Password Storage: The connection string in database.xml contains the database password. Ensure this file has appropriate permissions (readable only by the Jellyfin user).

  2. PGPASSWORD: The service uses the PGPASSWORD environment variable to pass credentials to pg_dump, which is more secure than command-line arguments.

  3. Backup Files: Backup files contain your entire database. Store them securely and restrict access appropriately.