wnx/laravel-backup-restore
Restore database backups created by spatie/laravel-backup. Adds an interactive php artisan backup:restore command to pick a backup and optionally decrypt it, then run configurable post-restore health checks to validate the restored DB.
## Getting Started
### Minimal Steps to Begin
1. **Installation**:
```bash
composer require wnx/laravel-backup-restore
php artisan vendor:publish --tag="backup-restore-config"
spatie/laravel-backup is installed (this package depends on it).First Use Case: Restore the latest backup interactively:
php artisan backup:restore
Key Configurations:
config/backup.php (from spatie/laravel-backup) is correctly set up with:
s3, local).config/laravel-backup-restore.php for health checks (default: DatabaseHasTables).Interactive Restoration:
php artisan backup:restore
APP_NAME and config/backup.php destinations).latest).config/backup.php).--no-interaction).Non-Interactive (CI/CD/Pipelines):
php artisan backup:restore \
--disk=s3 \
--backup=latest \
--connection=mysql \
--password=my-secret \
--reset \
--no-interaction
--disk: Override default destination (e.g., s3, local).--backup: Specify backup file (e.g., backup-2024-01-01.sql.gz or latest).--connection: Target database connection (from config/backup.php sources).--password: Manual override for encrypted backups.--reset: Drop tables before restore (default: false).--keep: Preserve downloaded/decrypted files (default: false).--no-interaction: Bypass prompts (critical for automation).Health Checks Integration:
// app/HealthChecks/CustomCheck.php
namespace App\HealthChecks;
use Wnx\LaravelBackupRestore\HealthChecks\HealthCheck;
use Wnx\LaravelBackupRestore\PendingRestore;
class CustomCheck extends HealthCheck {
public function run(PendingRestore $pendingRestore) {
$result = Result::make($this);
if (!Model::count()) {
return $result->failed('Critical data missing!');
}
return $result->ok();
}
}
config/laravel-backup-restore.php:
'health-checks' => [
\Wnx\LaravelBackupRestore\HealthChecks\Checks\DatabaseHasTables::class,
\App\HealthChecks\CustomCheck::class,
],
Automated Validation (GitHub Actions):
- name: Restore Backup
run: php artisan backup:restore --backup=latest --no-interaction
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
BACKUP_ARCHIVE_PASSWORD: ${{ secrets.BACKUP_ARCHIVE_PASSWORD }}
Database-Specific Quirks:
--set-gtid-purged=OFF to config/backup.php under dump for DigitalOcean-managed DBs:
'dump' => [
'add_extra_option' => '--set-gtid-purged=OFF',
],
pg_dump/pg_restore paths are correct.Multi-Environment:
APP_NAME to isolate backups (e.g., APP_NAME=staging for staging backups).config/backup.php per environment (e.g., via .env or environment variables).Backup Rotation:
spatie/laravel-backup retention policies to manage storage costs.Backup Naming Convention:
APP_NAME. Mismatches (e.g., APP_NAME=App but backup folder is app) cause failures.APP_NAME in .env matches the backup folder name.Encryption Passwords:
php artisan backup:restore --password=$BACKUP_PASSWORD
BACKUP_ARCHIVE_PASSWORD in .env and exclude it from version control.Database Connection Conflicts:
--reset to drop tables first, or restore to a fresh database.File Permissions:
storage/backups/ permissions or S3 bucket policies for remote storage.Health Check Failures:
public function run(PendingRestore $pendingRestore) {
$result = Result::make($this);
try {
if (!Model::count()) {
return $result->failed('Data missing!');
}
} catch (\Exception $e) {
return $result->failed("Check failed: {$e->getMessage()}");
}
return $result->ok();
}
GTID Errors (MySQL):
config/backup.php (as shown above) before creating backups. Existing GTID-enabled backups cannot be restored with this package.Post-Restore Data Validation:
Verbose Output:
php artisan backup:restore --verbose
storage/logs/laravel.log) for errors.Manual Backup Paths:
php artisan backup:restore --backup="backups/app/backup-2024-01-01.sql.gz"
Dry Runs:
--keep to inspect downloaded/decrypted files:
php artisan backup:restore --backup=latest --keep
storage/backups/decrypted/ for the extracted SQL file.Connection Issues:
config/backup.php sources match the target environment.mysql -h localhost -u root -p
Health Check Isolation:
use App\HealthChecks\CustomCheck;
$check = new CustomCheck();
$result = $check->run(new PendingRestore(...));
dd($result->isOk()); // Debug output
Custom Health Checks:
Wnx\LaravelBackupRestore\HealthChecks\HealthCheck to validate business logic (e.g., user counts, critical records).class AdminUserCheck extends HealthCheck {
public function run(PendingRestore $pendingRestore) {
$result = Result::make($this);
if (User::where('role', 'admin')->doesntExist()) {
return $result->failed('No admin users found!');
}
return $result->ok();
}
}
Pre/Post-Restore Hooks:
registering/registered events for the backup:restore command (via service providers) to run custom logic before/after restore.Backup Metadata:
backup-2024-01-01.sql.gz) to extract timestamps or tags for dynamic selection:
$backup = 'backup-2024-01-01.sql.gz';
$date = substr($backup, 7, 10); // Extracts '2024-01-01'
How can I help you explore Laravel packages today?