sameoldnick/laravel-backup-manager
Laravel package for managing application backups, offering configurable backup routines and storage destinations to help automate database and file backups within Laravel projects.
Baseline: Supersedes the v3.0.0 baseline for sameoldnick/laravel-backup-manager. Now written against v3.1.1, a single-issue patch on top of v3.0.0. The patch changes no schema, no published config keys, no responder signatures, and no service methods — the v3.0.0 breaking surface documented below is unchanged.
Architecture fit: The package follows conventional Laravel packaging — service provider, publishable config (backup-manager-config), publishable migrations (backup-manager-migrations), an artisan surface (backup:clean), and an optional Inertia/React admin UI exposed through responder abstractions. v3.0.0 deepens framework coupling in ways that generally help: backup and cleanup jobs now honour connection, queue, group, delay, middleware and after-commit, so the package slots into an existing queue topology rather than demanding a bespoke one. Destination resolution is centralized in BackupDestinationsService plus FilesystemConfigurationCollection helpers (getActive, getValid, getActiveAndValid, toDiskNames) — a cleaner seam than v2's BackupSchedulesService::getAvailableDestinations(). The new Runner::createBackupDestinations() base method makes backup and cleanup resolve destinations through one path, which is the right consolidation. v3.1.1 touches only the scheduler environment gate: comma-separated environment names are now split with explode(), trimmed, and empties discarded, rather than being joined with implode(). That is a correctness fix inside the scheduling path the package already owned — no new extension point, no new configuration concept.
Integration feasibility: Feasible, but not a drop-in bump from v2. There are four discrete workstreams: (1) publish and run the cleanup_schedule_filesystem_configuration pivot migration; (2) republish config to pick up jobs.backup / jobs.cleanup; (3) update the CleanupSchedulesUiResponder::renderCreateCleanupSchedule() signature to accept CreateCleanupScheduleViewData $data; (4) build frontend destination selection and submit destination_ids. Item 4 is the gating item — the release notes are explicit that until the UI is updated, "the new disk scoping cannot be reached from the frontend, and every cleanup keeps running against all disks." The headline feature is inert without app-side work. Also grep for and replace BackupSchedulesService::getAvailableDestinations() call sites. Upgrading 3.0.0 → 3.1.1 is a plain patch with no workstream attached; upgrading v2 → 3.1.1 is v2 → 3.0.0 plus a version bump.
Technical risk: Moderate-to-high for the v2 upgrade, low-to-moderate greenfield, and now low for the 3.0.0 → 3.1.1 hop.
CleanupJob instead of executing backup:clean. Any app on a non-sync connection without a worker silently accumulates jobs. This is the single largest operational risk in the release.implode() behaviour meant a comma-separated environments value was collapsed into one malformed string, so schedules could be gated off in environments where the operator expected them to run — another "silent absence" rather than an exception. If our published config sets multiple environments, 3.1.1 is not optional; it is a prerequisite for trusting the schedule at all.NoValidDisksException for an explicitly empty disks list changes behaviour for programmatic PerformBackupService::dispatchBackupJob() callers.jobs.*.name instead of jobs.*.queue, swallowed errors logged without stack traces, destination_ids absent from store/update validation, Inertia stubs pre-selecting every active destination) implies v2 behaviour was wrong in production for some apps. Historical backup/cleanup coverage should be audited, not assumed. The 3.1.1 environment gate belongs in the same audit: any period where schedules were gated to the wrong environment is a coverage gap that predates the upgrade.destination_ids capped at 255 bounds form-post fan-out; the broadcasting-authenticator fix restores /broadcasting/auth under php artisan optimize; PHPStan config plus memory limits in analyse/lint raise CI quality; BackupFile::file_exists caching removes a duplicate disk lookup per serialization; 3.1.1 is a narrow, well-scoped patch with no collateral surface.Key questions:
after-commit job semantics and route caching?backup:clean and CleanupJob both supported, and are they idempotent if both run concurrently during rollout?CleanupJob versus the legacy command?destination_ids interact with the app-level default destinations when the array is empty or omitted?FilesystemConfiguration resolution that would break multi-tenant use?Stack fit: Assumes Laravel with the standard scheduler, a queue backend (Horizon, Redis, SQS, or database), and disks defined in config/filesystems.php with FilesystemConfiguration records mapping to them. Natural fit where we already use S3/Spaces/SFTP via Flysystem and already operate a worker fleet. The responder abstraction means the package expects to own the schedule screens unless we implement responders ourselves — which is precisely the breaking surface in v3.
Migration path (sequenced):
^3.1 in composer.json before running composer update, or use composer require sameoldnick/laravel-backup-manager:^3.1. Existing 3.0.0 installs can take 3.1.1 as a standalone patch first, ahead of any other work, to close the environment-gating gap.environments value out of the published config. If it contains a comma-separated list, note it as a previously-broken setting and confirm post-upgrade that each named environment registers the schedule.--force, review the generated pivot before running php artisan migrate. The schema change is additive and independently safe.--force and reconcile the new jobs block against local overrides — --force will clobber customizations, so diff against version control first.CleanupSchedulesUiResponder::renderCreateCleanupSchedule() to accept the new view data; sweep for other responder implementations.BackupSchedulesService::getAvailableDestinations() with BackupDestinationsService::getAvailableDestinations().$data->selectedDestinationIds — never by filtering $data->configurations on is_active, since that collection includes unattached active disks and retains deactivated attached ones.Compatibility: The responder signature change and the removed service method are hard breaks with no soft-deprecation window — partial upgrades from v2 will not run. The database change is additive and reversible in practice. Behaviour diverges only at the edges: deactivated destinations, explicit empty disks, empty destination_ids, omitted-on-update destination_ids, and (pre-3.1.1 only) comma-separated scheduler environments. Note the dragonmantank/cron-expression ^3.6 pin introduced in 3.0; check for conflicts with other scheduler-aware packages during resolution.
Sequencing: Treat this as two distinct delivery states — backend-only (safe, disk scoping inert, cleanup behaves as before) and full (scoping live). Ship the first, validate, then enable the second. Decide the form contract up front and document it: an empty destination_ids array attaches nothing and reverts to app-configured destinations, while omitting the key on update preserves the current selection. The edit screen must not send the key when the user has not touched the picker, or destinations detach silently on unrelated saves. Take 3.1.1 before the backend deployment, not after — validating schedule execution against a known-broken environment gate would invalidate the whole validation step.
Maintenance: Modest but real. Config surface grows with the jobs block; the new pivot must stay consistent if the app mutates filesystem_configurations outside the package; and the responder layer is app-owned code that must track package signatures — the v3 break on renderCreateCleanupSchedule() demonstrates that abstraction is not stable. Shipping PHPStan config and bounded-memory lint/analyse scripts is a positive maintainability signal. Budget roughly half a day per major release for a single-instance app, plus frontend time when view data changes; patch releases like 3.1.1 should cost minutes.
Support: The release documentation quality is above average — ordered upgrade steps, an explicit upgrade prompt, and a documented UI contract for selectedDestinationIds and destination_ids semantics. That materially lowers support cost. The 3.1.1 note is terse but complete; the failure it describes is diagnosable from the diff. Residual support risk is concentration in a single-maintainer namespace, and the fact that the fixed-defects list documents prior silent failures means v2 behaviour cannot be assumed correct; treat historical coverage as unverified until audited, including any window governed by the broken environment gate.
Scaling: Improved. Cleanups no longer run inline in the scheduler and can be distributed across workers; per-job queue, group, connection, middleware and after-commit options give real control over isolation and concurrency; disk scoping cuts work per run from all disks to targeted disks; the 255-id cap bounds per-request fan-out. The caution: backups and cleanups now share worker capacity — put them on dedicated queues with concurrency caps, or a large backup fan-out will starve cleanups and vice versa. The file_exists caching also reduces disk round-trips for large listing pages, which matters at scale.
Failure modes:
environments on <3.1.1 → schedule gated to a malformed environment string and never registered in any real environment. Fixed, but verify the configured value round-trips and that each environment actually schedules; this class of failure produces no log line at all.NoValidDisksException on explicit empty disks → job failure; ensure $tries/backoff are configured now that job semantics are user-controlled and therefore misconfigurable.after-commit misconfiguration → jobs dispatched outside the expected transaction boundary, producing duplicate or dropped runs.destination_ids when untouched → silent detachment; never sending it → users cannot detach at all./broadcasting/auth; fixed, but php artisan optimize belongs in CI smoke tests.Ramp-up: Low for developers — standard Laravel idioms throughout. Higher for on-call, because the critical distinction is new: a failed cleanup surfaces as an exception or failed job, while a skipped cleanup surfaces only as a log line, and a mis-gated schedule on pre-3.1.1 surfaces as nothing at all. Provide a per-schedule last-success panel, an alert on skipped-run log entries, queue-depth alerts on the backup and cleanup queues, and a documented rollback path (constraint back to ^2.0, leaving the additive pivot in place; note that 3.1.1 itself has no rollback consideration — it is a strict improvement on 3.0.0 and should not be reverted). Exercise a deactivated-destination scenario and a multi-environment schedule registration end to end in staging before production — those are exactly where v2, 3.0.0 and 3.1.1 diverge.
How can I help you explore Laravel packages today?