zenstruck/backup-bundle
Symfony bundle that adds console commands to back up and restore your Doctrine database. Supports MySQL, PostgreSQL, and SQLite, stores archives via Flysystem, and lets you configure connections, retention, and storage so database snapshots fit cleanly into your deployment workflow.
Before writing a single line of code, verify the target: this is a Symfony bundle, not a Laravel package. There is no config/app.php provider entry, no Extra\Laravel\Providers auto-discovery, and no facade. A Laravel developer's "getting started" is therefore mostly a compatibility triage:
AppKernel, bundle registration in app/AppKernel.php, DI extension loading). Laravel's container is unrelated. You cannot composer require this and have it "just work" in a Laravel app.Command/ — the actual console commands. This is the reusable core; everything else is Symfony plumbing.DependencyInjection/Configuration.php — the config tree (DB credentials, dump binary path, output dir, retention, etc.).DependencyInjection/ZenstruckBackupExtension.php — how services are wired.Resources/config/ — service definitions and any default parameters.Symfony\Component\Process\Process (or Illuminate\Support\Facades\Process on Laravel 10+) to call mysqldump/pg_dump.bin/console command from Laravel.spatie/laravel-backup is the idiomatic equivalent and does not require a second framework.If you are on a mixed estate (some Symfony services, some Laravel apps), the shell-out bridge is the only pattern that lets you "use the package" without forking it.
Pattern A — Separate Symfony console app, invoked from Artisan. This is the cleanest way to actually consume the bundle from Laravel.
// app/Console/Commands/RunSymfonyBackup.php
use Illuminate\Support\Facades\Process;
public function handle(): int
{
$result = Process::path(base_path('../symfony-backup'))
->env([
'DATABASE_URL' => config('database.connections.mysql.dsn'),
'BACKUP_TARGET' => storage_path('backups'),
])
->timeout(600)
->run('php bin/console zenstruck:backup --no-interaction');
$this->line($result->output());
$result->throw();
return self::SUCCESS;
}
Pass credentials and paths through environment variables so the Symfony config.yml stays thin and you don't duplicate secrets. This mirrors the bundle's own console semantics (--no-interaction, meaningful exit codes, stdout/stderr separation).
Pattern B — Logic port into an Artisan command. Extract the dump invocation and retention logic from Command/ and re-express it with Laravel primitives. You get Laravel's scheduler, Storage::disk() for offsite uploads, and config/database.php as the single source of DB credentials. In practice this is what most Laravel developers end up doing; the bundle is a map, not a dependency.
Pattern C — Scheduled invocation. Whichever mechanism you pick, schedule it through Laravel so backups live in one place:
// routes/console.php
Schedule::command('backup:run')->dailyAt('02:00')->onOneServer();
Schedule::command('backup:clean')->weekly();
Integration tips that make the bridge survivable:
storage/app/backups) so subsequent Laravel-side retention or upload logic does not need to know about Symfony.backup:monitor style) that asserts the newest dump file is younger than N hours — the bundle does not do this for you.composer require zenstruck/backup-bundle and start using a facade, it is for Symfony 2, not Laravel.symfony/console, symfony/process). Pin versions in the bridge project and do not let its composer.lock leak into your Laravel app.Configuration.php), not config/backup.php. Field names and defaults will not match spatie/laravel-backup, so runbooks and env vars do not transfer one-to-one.mysqldump/pg_dump locations differ between your Symfony bridge container and the Laravel app. Make the binary path configurable and test it explicitly; credentials with special characters need proper escaping — rely on Process's argument array form rather than string concatenation.bin/console <command> -vvv), then wrap. If it fails in Symfony, it is a bundle/config issue; if it succeeds there and fails from Laravel, it is an env var, path, or permissions issue.spatie/laravel-backup), and vendor a copy of the relevant command if you depend on it long-term.spatie/laravel-backup plus a scheduler entry covers the same ground with native integration, offsite disks, notifications, and monitoring. Reach for this bundle only when you are explicitly maintaining Symfony services or want to mine its command logic.How can I help you explore Laravel packages today?