spatie/laravel-backup-server
Securely store and manage backups from multiple Laravel apps on a dedicated backup server. Built on spatie/laravel-backup, it automatically receives and organizes incoming backups, with setup and docs tailored for Laravel deployments.
## Getting Started
### Minimal Setup
1. **Install the Package**
```bash
composer require spatie/laravel-backup-server
php artisan vendor:publish --provider="Spatie\BackupServer\BackupServerServiceProvider"
This publishes the config file (config/backup-server.php) and migrations.
Configure Destinations
Define a filesystem disk in config/filesystems.php (e.g., backup_disk with local driver) and create a Destination model:
use Spatie\BackupServer\Models\Destination;
Destination::create([
'name' => 'primary_backup',
'disk_name' => 'backup_disk',
'capacity_in_mb' => 1000000, // Optional: Set capacity for monitoring
]);
Define a Source
Register a source (e.g., a remote server) via the backup-server:create-source command or manually:
use Spatie\BackupServer\Models\Source;
Source::create([
'name' => 'production-server',
'ssh_host' => 'user@remote-server.com',
'backup_hour' => 3, // Run backups at 3 AM
'includes' => ['/var/www/html', '/etc/nginx'],
'excludes' => ['/var/www/html/storage/logs'],
'pre_backup_commands' => ['mysqldump -u user -p db_name > /tmp/db_backup.sql'],
'post_backup_commands' => ['rm /tmp/db_backup.sql'],
]);
Run a Backup Manually trigger a backup:
php artisan backup-server:backup production-server
Or schedule it via Laravel’s scheduler (e.g., in app/Console/Kernel.php):
$schedule->command('backup-server:dispatch-backups')->hourly();
Verify Backups Check the status of sources/destinations:
php artisan backup-server:list
php artisan backup-server:list-destinations
Multi-Server Backup Coordination
Use the backup-server:dispatch-backups command to orchestrate backups for all registered sources. The package handles SSH connections, rsync transfers, and deduplication automatically.
// app/Console/Kernel.php
$schedule->command('backup-server:dispatch-backups')->hourlyAt('3');
Incremental Backups with Hard Links
Leverage rsync’s built-in deduplication. Only changed files are transferred, and identical files are hard-linked to previous backups, saving disk space.
# config/backup-server.php
'backup' => [
'use_hard_links' => true, // Default: true
]
Pre/Post-Backup Hooks
Use pre_backup_commands and post_backup_commands to manage database dumps or cleanup tasks:
Source::create([
'name' => 'api-server',
'pre_backup_commands' => [
'pg_dump -U user db_name > /tmp/db_backup.sql',
'gzip /tmp/db_backup.sql',
],
'post_backup_commands' => [
'rm /tmp/db_backup.sql.gz',
],
]);
Monitoring and Alerts Integrate with Laravel Notifications to send alerts for failed backups or unhealthy sources:
// config/backup-server.php
'notifications' => [
'notifications' => [
\Spatie\BackupServer\Notifications\Notifications\BackupFailedNotification::class => ['mail', 'slack'],
],
'mail' => ['to' => 'admin@example.com'],
'slack' => ['webhook_url' => env('SLACK_WEBHOOK')],
];
Searching Backups Use CLI tools to search backups without restoring:
# Find all JSON files in a source's backups
php artisan backup-server:find-files production-server *.json
# Search for content in backups
php artisan backup-server:find-content production-server "Taylor"
Cleanup Automation
Schedule cleanup for old backups via the backup-server:cleanup command:
$schedule->command('backup-server:cleanup')->weekly();
Configure retention rules in the Destination model:
Destination::create([
'name' => 'primary_backup',
'keep_backups_for_days' => 30,
'keep_backup_count' => 5,
]);
SSH Key Authentication Ensure SSH keys are properly configured for password-less logins to remote sources. Test connectivity manually:
ssh user@remote-server.com whoami
If this fails, the backup will abort. Use backup-server:test-source to debug:
php artisan backup-server:test-source production-server
Disk Space Monitoring
The package checks disk capacity but relies on the capacity_in_mb field in the Destination model. If this is omitted, capacity checks are skipped. Set it to avoid silent failures:
Destination::create(['capacity_in_mb' => 1000000]); // 1TB
Hard Link Limitations Hard links may fail if the destination filesystem doesn’t support them (e.g., network drives). Disable hard links if needed:
// config/backup-server.php
'backup' => ['use_hard_links' => false],
This increases storage usage but ensures compatibility.
Large Backup Size Calculation Calculating backup sizes for very large directories (e.g., >100GB) may time out. Increase the timeout:
// config/backup-server.php
'backup' => ['backup_size_calculation_timeout_in_seconds' => 300],
Excludes vs. Includes
excludes are applied after includes. Misconfiguration can lead to unintended exclusions:
// Bad: Excludes everything under /var/www
'includes' => ['/var/www'],
'excludes' => ['/var/www/node_modules'], // Won't work as expected
// Good: Exclude directly
'includes' => ['/var/www'],
'excludes' => ['/var/www/node_modules', '/var/www/storage/logs'],
Notification Delays
Notifications are sent after the backup completes (success/failure). For critical systems, consider adding a BackupCompletedEvent listener to trigger immediate alerts:
use Spatie\BackupServer\Tasks\Backup\Events\BackupCompletedEvent;
Event::listen(BackupCompletedEvent::class, function ($event) {
if (!$event->backup->is_healthy()) {
// Send custom alert (e.g., via PagerDuty)
}
});
Verbose Logging
Enable debug mode in config/backup-server.php:
'debug' => env('BACKUP_SERVER_DEBUG', false),
Logs appear in storage/logs/laravel.log.
Dry Runs
Test backups without writing to disk by setting dry_run in the Source model:
Source::create(['dry_run' => true]);
This simulates the backup process and logs actions without transferring files.
Artisan Commands Use these commands for troubleshooting:
# List all sources with health status
php artisan backup-server:list --sortBy=healthy
# Test SSH connectivity to a source
php artisan backup-server:test-source production-server
# Simulate a backup (dry run)
php artisan backup-server:backup production-server --dry-run
Event Listeners
Listen to BackupFailedEvent to log failures to a monitoring system:
use Spatie\BackupServer\Tasks\Backup\Events\BackupFailedEvent;
Event::listen(BackupFailedEvent::class, function ($event) {
\Log::error("Backup failed for {$event->backup->source->name}: {$event->backup->error_message}");
// Send alert to Datadog/PagerDuty
});
Custom Destinations
Extend the Destination model to add custom logic (e.g., cloud storage integration). Override the path() method to return a custom path:
namespace App\Models;
use Spatie\BackupServer\Models\Destination;
class CustomDestination extends Destination {
public function path(): string {
return storage_path('custom-backups/' . $this->name);
}
}
Custom Notifications Create a custom notification channel
How can I help you explore Laravel packages today?