Skip to main content

Database Schema

This document describes the SQLite database schema used by duplistatus to store backup operation data.

Database Location

The database is stored in the application data directory:

  • Default Location: /app/data/backups.db
  • Docker Volume: duplistatus_data:/app/data
  • File Name: backups.db

Database Migration System

duplistatus uses an automated migration system to handle database schema changes between versions.

Migration Version History

The following are historical migration versions that brought the database to its current state:

  • Schema v1.0 (Application v0.6.x and earlier): Initial database schema with machines and backups tables
  • Schema v2.0 (Application v0.7.x): Added missing columns and configurations table
  • Schema v3.0 (Application v0.7.x): Renamed machines table to servers, added server_url column
  • Schema v3.1 (Application v0.8.x): Enhanced backup data fields, added server_password column
  • Schema v4.0 (Application v0.9.x / v1.0.x): Added User Access Control (users, sessions, audit_log tables)

Current application version (v1.1.0) uses Schema v4.0 as the latest database schema version.

Migration Process

  1. Automatic Backup: Creates backup before migration
  2. Schema Update: Updates database structure
  3. Data Migration: Preserves existing data
  4. Verification: Confirms successful migration

Tables

Servers Table

Stores information about Duplicati servers being monitored.

Fields

FieldTypeDescription
idTEXT PRIMARY KEYUnique server identifier
nameTEXT NOT NULLServer name from Duplicati
server_urlTEXTDuplicati server URL
aliasTEXTUser-defined friendly name
noteTEXTUser-defined notes/description
server_passwordTEXTServer password for authentication
created_atDATETIMEServer creation timestamp

Backups Table

Stores backup operation data received from Duplicati servers.

Key Fields

FieldTypeDescription
idTEXT PRIMARY KEYUnique backup identifier
server_idTEXT NOT NULLReference to servers table
backup_nameTEXT NOT NULLBackup job name
backup_idTEXT NOT NULLBackup ID from Duplicati
dateDATETIME NOT NULLBackup execution time
statusTEXT NOT NULLBackup status (Success, Warning, Error, Fatal)
duration_secondsINTEGER NOT NULLDuration in seconds
sizeINTEGERSize of source files
uploaded_sizeINTEGERSize of uploaded data
examined_filesINTEGERNumber of files examined
warningsINTEGERNumber of warnings
errorsINTEGERNumber of errors
created_atDATETIMERecord creation timestamp

Message Arrays (JSON Storage)

FieldTypeDescription
messages_arrayTEXTJSON array of log messages
warnings_arrayTEXTJSON array of warning messages
errors_arrayTEXTJSON array of error messages
available_backupsTEXTJSON array of available backup versions

File Operation Fields

FieldTypeDescription
examined_filesINTEGERFiles examined during backup
opened_filesINTEGERFiles opened for backup
added_filesINTEGERNew files added to backup
modified_filesINTEGERFiles modified in backup
deleted_filesINTEGERFiles deleted from backup
deleted_foldersINTEGERFolders deleted from backup
added_foldersINTEGERFolders added to backup
modified_foldersINTEGERFolders modified in backup
not_processed_filesINTEGERFiles not processed
too_large_filesINTEGERFiles too large to process
files_with_errorINTEGERFiles with errors
added_symlinksINTEGERSymbolic links added
modified_symlinksINTEGERSymbolic links modified
deleted_symlinksINTEGERSymbolic links deleted

File Size Fields

FieldTypeDescription
size_of_examined_filesINTEGERSize of files examined during backup
size_of_opened_filesINTEGERSize of files opened for backup
size_of_added_filesINTEGERSize of new files added to backup
size_of_modified_filesINTEGERSize of files modified in backup

Operation Status Fields

FieldTypeDescription
parsed_resultTEXT NOT NULLParsed operation result
main_operationTEXT NOT NULLMain operation type
interruptedBOOLEANWhether backup was interrupted
partial_backupBOOLEANWhether backup was partial
dryrunBOOLEANWhether backup was a dry run
versionTEXTDuplicati version used
begin_timeDATETIME NOT NULLBackup start time
end_timeDATETIME NOT NULLBackup end time
warnings_actual_lengthINTEGERActual warnings count
errors_actual_lengthINTEGERActual errors count
messages_actual_lengthINTEGERActual messages count

Backend Statistics Fields

FieldTypeDescription
bytes_downloadedINTEGERBytes downloaded from destination
known_file_sizeINTEGERKnown file size on destination
last_backup_dateDATETIMELast backup date on destination
backup_list_countINTEGERNumber of backup versions
reported_quota_errorBOOLEANQuota error reported
reported_quota_warningBOOLEANQuota warning reported
backend_main_operationTEXTBackend main operation
backend_parsed_resultTEXTBackend parsed result
backend_interruptedBOOLEANBackend operation interrupted
backend_versionTEXTBackend version
backend_begin_timeDATETIMEBackend operation start time
backend_durationTEXTBackend operation duration
backend_warnings_actual_lengthINTEGERBackend warnings count
backend_errors_actual_lengthINTEGERBackend errors count

Configurations Table

Stores application configuration settings.

Fields

FieldTypeDescription
keyTEXT PRIMARY KEY NOT NULLConfiguration key
valueTEXTConfiguration value (JSON)

Common Configuration Keys

  • email_config: Email notification settings
  • ntfy_config: NTFY notification settings
  • overdue_tolerance: Overdue backup tolerance settings
  • notification_templates: Notification message templates
  • audit_retention_days: Audit log retention period (default: 90 days)

Database Version Table

Tracks database schema version for migration purposes.

Fields

FieldTypeDescription
versionTEXT PRIMARY KEYDatabase version
applied_atDATETIMEWhen migration was applied

Users Table

Stores user account information for authentication and access control.

Fields

FieldTypeDescription
idTEXT PRIMARY KEYUnique user identifier
usernameTEXT UNIQUE NOT NULLUsername for login
password_hashTEXT NOT NULLBcrypt hashed password
is_adminBOOLEAN NOT NULLWhether user has admin privileges
must_change_passwordBOOLEANWhether password change is required
created_atDATETIMEAccount creation timestamp
updated_atDATETIMELast update timestamp
last_login_atDATETIMELast successful login timestamp
last_login_ipTEXTIP address of last login
failed_login_attemptsINTEGERCount of failed login attempts
locked_untilDATETIMEAccount lock expiration (if locked)

Sessions Table

Stores user session data for authentication and security.

Fields

FieldTypeDescription
idTEXT PRIMARY KEYSession identifier
user_idTEXTReference to users table (nullable for unauthenticated sessions)
created_atDATETIMESession creation timestamp
last_accessedDATETIMELast access timestamp
expires_atDATETIME NOT NULLSession expiration timestamp
ip_addressTEXTIP address of session origin
user_agentTEXTUser agent string
csrf_tokenTEXTCSRF token for the session
csrf_expires_atDATETIMECSRF token expiration

Audit Log Table

Stores audit trail of user actions and system events.

Fields

FieldTypeDescription
idINTEGER PRIMARY KEY AUTOINCREMENTUnique audit log entry identifier
timestampDATETIMEEvent timestamp
user_idTEXTReference to users table (nullable)
usernameTEXTUsername at time of action
actionTEXT NOT NULLAction performed
categoryTEXT NOT NULLCategory of action (e.g., 'authentication', 'settings', 'backup')
target_typeTEXTType of target (e.g., 'server', 'backup', 'user')
target_idTEXTIdentifier of target
detailsTEXTAdditional details (JSON)
ip_addressTEXTIP address of requester
user_agentTEXTUser agent string
statusTEXT NOT NULLStatus of action ('success', 'failure', 'error')
error_messageTEXTError message if action failed

Session Management

Database-Backed Session Storage

Sessions are stored in the database with in-memory fallback:

  • Primary Storage: Database-backed sessions table
  • Fallback: In-memory storage (legacy support or error cases)
  • Session ID: Cryptographically secure random string
  • Expiration: Configurable session timeout
  • CSRF Protection: Cross-site request forgery protection
  • Automatic Cleanup: Expired sessions are automatically removed

Session API Endpoints

  • POST /api/session: Create new session
  • GET /api/session: Validate existing session
  • DELETE /api/session: Destroy session
  • GET /api/csrf: Get CSRF token

Indexes

The database includes several indexes for optimal query performance:

  • Primary Keys: All tables have primary key indexes
  • Foreign Keys: Server references in backups table, user references in sessions and audit_log
  • Query Optimisation: Indexes on frequently queried fields
  • Date Indexes: Indexes on date fields for time-based queries
  • User Indexes: Username index for fast user lookups
  • Session Indexes: Expiration and user_id indexes for session management
  • Audit Indexes: Timestamp, user_id, action, category, and status indexes for audit queries

Relationships

  • Servers → Backups: One-to-many relationship
  • Users → Sessions: One-to-many relationship (sessions can exist without users)
  • Users → Audit Log: One-to-many relationship (audit entries can exist without users)
  • Backups → Messages: Embedded JSON arrays
  • Configurations: Key-value storage

Data Types

  • TEXT: String data, JSON arrays
  • INTEGER: Numeric data, file counts, sizes
  • REAL: Floating-point numbers, durations
  • DATETIME: Timestamp data
  • BOOLEAN: True/false values

Backup Status Values

  • Success: Backup completed successfully
  • Warning: Backup completed with warnings
  • Error: Backup completed with errors
  • Fatal: Backup failed fatally

Common Queries

Get Latest Backup for a Server

SELECT * FROM backups 
WHERE server_id = ?
ORDER BY date DESC
LIMIT 1;

Get All Backups for a Server

SELECT * FROM backups 
WHERE server_id = ?
ORDER BY date DESC;

Get Server Summary

SELECT 
s.name,
s.alias,
COUNT(b.id) as backup_count,
MAX(b.date) as last_backup,
b.status as last_status
FROM servers s
LEFT JOIN backups b ON s.id = b.server_id
GROUP BY s.id;

Get Overall Summary

SELECT 
COUNT(DISTINCT s.id) as total_servers,
COUNT(b.id) as total_backups_runs,
COUNT(DISTINCT s.id || ':' || b.backup_name) as total_backups,
COALESCE(SUM(b.uploaded_size), 0) as total_uploaded_size,
(
SELECT COALESCE(SUM(b2.known_file_size), 0)
FROM backups b2
INNER JOIN (
SELECT server_id, MAX(date) as max_date
FROM backups
GROUP BY server_id
) latest ON b2.server_id = latest.server_id AND b2.date = latest.max_date
) as total_storage_used,
(
SELECT COALESCE(SUM(b2.size_of_examined_files), 0)
FROM backups b2
INNER JOIN (
SELECT server_id, MAX(date) as max_date
FROM backups
GROUP BY server_id
) latest ON b2.server_id = latest.server_id AND b2.date = latest.max_date
) as total_backuped_size
FROM servers s
LEFT JOIN backups b ON b.server_id = s.id;

Database Cleanup

-- Delete old backups (older than 30 days)
DELETE FROM backups
WHERE date < datetime('now', '-30 days');

-- Delete servers with no backups
DELETE FROM servers
WHERE id NOT IN (SELECT DISTINCT server_id FROM backups);

JSON to Database Mapping

API Request Body to Database Columns Mapping

When Duplicati sends backup data via HTTP POST, the JSON structure is mapped to database columns:

{
"Data": {
"ExaminedFiles": 15399, // → examined_files
"OpenedFiles": 1861, // → opened_files
"AddedFiles": 1861, // → added_files
"SizeOfExaminedFiles": 11086692615, // → size_of_examined_files
"SizeOfOpenedFiles": 13450481, // → size_of_opened_files
"SizeOfAddedFiles": 13450481, // → size_of_added_files
"SizeOfModifiedFiles": 0, // → size_of_modified_files
"ParsedResult": "Success", // → status
"BeginTime": "2025-04-21T23:45:46.9712217Z", // → begin_time and date
"Duration": "00:00:51.3856057", // → duration_seconds (calculated)
"WarningsActualLength": 0, // → warnings_actual_length
"ErrorsActualLength": 0 // → errors_actual_length
},
"Extra": {
"machine-id": "66f5ffc7ff474a73a3c9cba4ac7bfb65", // → server_id
"machine-name": "WSJ-SER5", // → server name
"backup-name": "WSJ-SER5 Local files", // → backup_name
"backup-id": "DB-2" // → backup_id
}
}

Note: The size field in the backups table stores SizeOfExaminedFiles and uploaded_size stores the actual uploaded/transferred size from the backup operation.