Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Backup Manager Bundle Laravel Package

lhpalacio/backup-manager-bundle

Backup Manager Bundle simplifies creating, scheduling, and restoring database and file backups in Symfony/PHP applications. Configure backup sources and destinations, run backups from services or commands, and keep recovery workflows flexible and maintainable.

View on GitHub
Deep Wiki
Context7

Getting Started

First, adjust expectations: you cannot composer require this into a Laravel app. lhpalacio/backup-manager-bundle is a Symfony2 bundle — it registers via AppKernel, wires services through the Symfony DI container, and exposes Symfony Console commands. There is no service provider, no config/backup.php stub, no Artisan namespace, and no support for Laravel's scheduler or queue. The repository is archived, targets EOL PHP (5.x/early 7.x), and has zero dependents. On a modern Laravel (9+) the code will not even install cleanly, let alone boot.

So the "getting started" is a reading path, not an install path:

  1. Read src/ in this order — the DI extension (config schema: sources, destinations, options), then the command classes (backup, restore, list), then the service wiring. That's the whole bundle; it's thin. Roughly an hour.
  2. Read the underlying library — backup-manager/backup-manager. This is the part with actual substance: dumpers, compressors, and storage adapters. The bundle adds nothing you can't call directly.
  3. Read the config tree — it is the most reusable artifact. It's a clean spec of what a backup feature needs: named sources (DB connection + type, filesystem paths), named destinations (adapter + credentials + path/prefix), and per-source options (compression, filename pattern, ignore tables).

Your first real use case, in Laravel, is one of two paths:

  • Standard case → adopt spatie/laravel-backup. It is the direct Laravel-native equivalent of everything this bundle does (scheduled dumps, multi-destination via Laravel filesystem disks, retention, notifications) and is maintained.
  • You specifically need an adapter this bundle has and Spatie doesn't → depend on backup-manager/backup-manager directly and write the ~150-line Laravel wrapper yourself: a config file, a singleton binding in a service provider, one Artisan command, and a scheduled entry. You own that surface either way — the difference is whether you also own a forklifted Symfony2 bundle.

Skip entirely if backups are a production or compliance obligation. An archived, 13-star, zero-dependent dependency holding your database credentials and object-storage keys is a non-starter in a security review, and backup tooling fails silently by nature.

Everything below assumes the "read it and re-implement the shape in Laravel" path.

Implementation Patterns

Pattern 1 — Config-first, mirroring the bundle's schema. The bundle's one genuinely good idea is that sources and destinations are named, declared once, and composed. Do the same in config/backup.php:

return [
    'sources' => [
        'primary' => ['driver' => 'mysql', 'connection' => 'mysql'],
        'uploads' => ['driver' => 'files', 'paths' => [storage_path('app/public')]],
    ],
    'destinations' => [
        's3' => ['disk' => 'backups-s3', 'prefix' => 'db/'],
        'nas' => ['disk' => 'backups-nas', 'prefix' => 'db/'],
    ],
];

Then config('backup.sources.primary') is the contract your service consumes. This is the piece worth copying.

Pattern 2 — One command, thin, no business logic. The bundle's commands are thin wrappers over the service; keep yours the same. php artisan backup:run primary --to=s3. The command resolves the source/destination pair, delegates, and returns an exit code. Nothing else. This makes the command trivially testable with a faked service.

Pattern 3 — Programmatic access via the container. The bundle's other useful idea: backups are callable from your own services, not just cron. In Laravel, bind a singleton in a service provider and inject it:

$this->app->singleton(BackupRunner::class, fn ($app) => new BackupRunner(
    config('backup.sources'), config('backup.destinations')
));

Then any controller, job, or listener can call $runner->run('primary', 's3') — useful for "backup before migration" or "export this tenant's data on request" flows.

Pattern 4 — Fan-out to multiple destinations. The bundle supports N destinations per source; so should you. Loop destinations inside the runner and fail loudly per-destination rather than aborting the whole run — a dead FTP target should not prevent the S3 copy from landing. Collect per-destination results and report them together.

Pattern 5 — Schedule with withoutOverlapping and onOneServer. This is where the bundle is weakest and Laravel is strongest. The bundle expects you to wire cron yourself; Laravel gives you:

Schedule::command('backup:run primary --to=s3')
    ->dailyAt('02:00')
    ->withoutOverlapping(120)
    ->onOneServer()
    ->runInBackground();

onOneServer() requires a shared cache driver (Redis, database) — not file. withoutOverlapping needs the same. Get this right, or you'll get concurrent dumps writing to the same temp file.

Pattern 6 — Notifications on failure, from day one. The bundle has no notification story, which is precisely the failure mode that matters. In Laravel, implement ShouldQueue on the command's failure path or use Notification::route('mail', ...) in the failed() handler. Better: a heartbeat model — a backup_runs table row per run plus an external dead-man's-switch check. Do not trust the tool's own success log; verify freshness independently.

Pattern 7 — Queue long-running backups. Never run a multi-GB dump synchronously in a web request or in a schedule:run that shares a worker with other jobs. Dispatch a job with $timeout set above your worst-case dump duration, and set retryUntil. Note the scheduler's runInBackground still occupies a PHP process; the queue is cleaner.

Pattern 8 — Post-backup verification. The bundle lists and restores, but does not verify. Add a separate weekly command that (a) downloads the newest artifact, (b) restores it into a throwaway database/schema, (c) runs CHECKSUM TABLE or compares row counts against the source, and (d) alerts on mismatch. This is the single highest-value thing you'll write, and it's the thing the bundle never had.

Pattern 9 — Retention as an explicit prune command. Filesystem-driven retention (just keep writing until the disk fills) is what you get by default. Implement backup:prune --keep-days=14 --keep-count=30 as its own scheduled command against the destination disk, driven by a policy in config — not by whatever the adapter happens to do.

Pattern 10 — Test against faked disks. Laravel's Storage::fake('backups-s3') plus a small fixture database makes the whole runner testable in milliseconds. If you fork anything from this bundle, you lose that; another reason not to.

Gotchas and Tips

  • Container semantics differ fundamentally. Symfony's parameter bag and compiler passes have no equivalent in Laravel's container. You cannot port the DependencyInjection/Configuration.php class as-is — rewrite the tree as a plain PHP config array with validation in a service provider's register()/boot().
  • The install will fail before it runs. The bundle's composer.json pins PHP 5.x/early 7.x and Symfony 2.x. On PHP 8.1+ you'll hit removed functions and incompatible transitive deps. Even composer require --ignore-platform-reqs gets you a class that won't autoload under Laravel.
  • Dead storage adapters. Dropbox v1 was shut down in 2017 — that adapter is functionally dead. Rackspace Cloud Files is legacy. S3 will be pinned to an ancient AWS SDK with known CVEs. Of five advertised backends, one or two are usable and none are current. Do not port the adapter list; use Laravel filesystem disks (league/flysystem-aws-s3-v3 etc.), which are maintained.
  • mysqldump invocation is the sharp edge. Expect to deal with: binary path resolution (which mysqldump differs by OS/base image), escapeshellarg on every interpolated value, and credentials. Prefer --defaults-extra-file or MYSQL_PWD over --password= on the command line, which leaks into ps and logs.
  • Consistency flags matter. Use --single-transaction for InnoDB (consistent dump without locking). It does nothing for MyISAM — those tables still lock. On MySQL 8 you'll also need --no-tablespaces unless the user has PROCESS, otherwise the dump fails.
  • Local temp disk is the hidden scaling limit. The dump-to-local-then-upload pattern requires free space equal to the full dump on the app node. On a container with a small overlay filesystem this is a No space left on device waiting to happen. Set a configurable temp path on a volume with headroom, and always clean up in a finally block — an orphaned 40 GB dump is a real incident.
  • Exit codes lie. exec() returns the shell exit; proc_open returns 0 even when the child failed unless you check proc_close's return. The command can exit 0 having written nothing. Always assert the artifact exists and has non-zero size before declaring success.
  • env() outside config files returns null under config:cache. Standard Laravel trap, but it bites hard here because backup credentials are exactly the sort of thing people reach for env('AWS_KEY') for in a service class. Put them in config/filesystems.php, never in the runner.
  • Silent non-execution is the dominant failure mode. Cron disabled by a migration, scheduler container not restarted, withoutOverlapping mutex stuck from a killed process — all produce "no backups" with zero signal. Monitor freshness of artifacts, not command exit status. A heartbeat/dead-man's-switch is worth more than any logging you add.
  • withoutOverlapping locks leak. If a PHP process is SIGKILLed (OOM during a large dump), the lock persists for the full withoutOverlapping window. Set it explicitly and log when a run is skipped due to an existing lock — otherwise you'll silently skip days.
  • Credential rotation breaks upload, after the dump. Old SDKs handle token refresh poorly. Expired keys fail after the local dump is written, leaving orphaned temp files and a false sense of "the backup ran." Log upload failure distinctly from dump failure.
  • Extension point worth using: Laravel's filesystem disks, not the library's adapters. You get Storage::disk('backups-s3')->put(...), fakes for testing, and a maintained SDK — the thing the bundle was trying to abstract is already abstracted upstream.
  • Do not fork this repo as an interim step. It adds permanent maintenance cost without reducing migration risk. If a legacy Symfony2 service depends on it, document it as a migration blocker with a target replacement and a parallel-run plan; don't extend its life.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings