tomatophp/tomato-backup
Tomato Backup brings backup management to TomatoPHP and Filament admin panels. Create, schedule, and restore database and file backups from a simple interface, with support for local and cloud storage via Laravel's filesystem. Integrates with queues for automated background backups.
Architecture fit. This is not a backup engine; it is a thin TomatoPHP/Filament presentation and settings layer over spatie/laravel-backup. That decomposition should drive the whole evaluation, because the two components carry very different risk. The risk-bearing half — archive creation, retention pruning, disk upload, notifications — is mature, widely deployed, and uninteresting in the best sense. The plugin half contributes settings screens, a listing/download surface, and a manual trigger. Architecturally it fits cleanly only if the target application is already on the TomatoPHP/Filament admin stack. In that case it slots in as a panel plugin with a published config, adds no new runtime services, and requires nothing beyond a destination disk, a queue worker, and a live scheduler. In any other stack it is a foreign dependency introduced to obtain a settings page, which inverts the value proposition.
Integration feasibility. Feasible and small — the package is readable end to end in about an hour, and installation follows the standard Filament plugin pattern plus the mandatory spatie/laravel-backup config publish. The real integration work is not the plugin; it is the surrounding prerequisites: a configured destination disk (with the Flysystem S3 adapter and correct root/permissions if remote), a dedicated queue for backup jobs, cron-driven schedule:run, and raised memory_limit/max_execution_time on the backup worker. Those are hours of platform work that the plugin neither supplies nor reminds the operator about. Treat the plugin as one to two days of saved UI work, not as the delivery of a backup capability.
Technical risk. Four items dominate:
spatie/laravel-backup may silently constrain or downgrade the engine version we actually want — a wrapper pinning an old engine is worse than no wrapper. This must be verified on a throwaway branch before adoption, not discovered during an upgrade.config/backup.php. Destination, retention, and encryption decisions should stay in config regardless.Secondary but real: with encryption: 'default', archives are encrypted with APP_KEY, so a key rotation renders every prior backup unreadable. That is a policy decision we own, not a plugin defect.
Key questions to answer before committing.
spatie/laravel-backup plus a minimal action — do not adopt the plugin.spatie/laravel-backup version, or does it constrain it?config/backup.php or from a settings table?mysqldump against a live primary acceptable, and where do we need a read replica instead?Stack fit. Adopt only on an existing TomatoPHP/Filament panel. The value is one to two days of saved admin-screen work in front of non-technical operators; it is not a platform commitment. On any other stack, the correct move is spatie/laravel-backup directly with a single Filament action or artisan command wrapping Artisan::call('backup:run', ['--only-db' => true]) — that is the entire value the wrapper adds, reproducible in one class.
Migration path. There is nothing to migrate from; this is a greenfield capability for most teams. The genuine migration concern is the fork: an MIT license, negligible community, and no release in over a year make forking into our own namespace on day one the cheapest insurance available. A repositories VCS entry pointing at our fork costs twenty minutes now and prevents an abandoned package from blocking a future framework upgrade.
Compatibility. Verify in a throwaway branch, in this order: (1) install and run composer why-not/composer update --dry-run to confirm the pinned spatie/laravel-backup constraint does not downgrade the engine; (2) confirm the exact plugin class name from vendor/tomatophp/tomato-backup/src/; (3) publish config/backup.php and read it before touching the panel; (4) determine whether settings live in config or a table. Only after those pass should the Filament plugin be registered in AdminPanelProvider.
Sequencing. Prove the engine before the UI — this is the sequencing decision that matters:
php artisan backup:run --only-db and php artisan backup:list from the CLI.--only-db nightly, --only-files weekly, backup:clean daily, backup:monitor daily), and age-based alerting wired to a channel a human reads.If the work is being scoped as a checkbox item against a disaster-recovery gap, the honest v1 is steps 1–4 with step 5 optional. Steps 1–4 are also the part that survives if the plugin is later dropped.
Maintenance. We become the maintainer the moment we install it. Budget one compatibility check per Laravel and Filament major, plus an owner for the fork. The wrapper is thin enough that a fix is usually hours rather than days — but only if someone has already read the source. Assign that reading as a precondition of adoption, not a follow-up task.
Support. No vendor, no SLA, no CVE response, no security questionnaire survival. This will not clear procurement in a regulated or enterprise buyer's process. Internally, support for "the backup panel is empty" and "the restore button doesn't work" lands on whoever owns the panel, and both symptoms point at infrastructure (dead scheduler, missing worker) or at the gap between what the description promises and what the engine does.
Scaling. The plugin neither causes nor cures scale problems, and it should not be presented as addressing them. Two engine behaviors set the ceiling: mysqldump against a live primary holds locks and creates a write-stall window — for anything non-trivial, point the dump at a read replica; and large file payloads will hit PHP memory_limit and max_execution_time, which should be raised on the backup queue worker specifically rather than globally. Long remote uploads occupy a worker for minutes; sharing the default queue means user-facing jobs stall behind a database dump.
Failure modes, roughly in order of severity:
backup:clean deletes the last good archive while backup:run has been failing quietly for weeks. The only mitigation is backup:monitor with alerting on backup age, set conservatively at roughly twice the backup cadence — not 30 days. Build this before enabling aggressive retention, not after.backup.password held in a secrets manager outside the app, or accept that key rotation requires re-encrypting archives first.Ramp-up. Low for a competent Laravel engineer: the source audits in about an hour and the engine is well-trodden. The ramp-up cost that actually matters is organizational, not technical — it is the restore drill. Write the procedure down (fetch archive, unzip, mysql < or pg_restore, rsync the files payload), rehearse it against a non-production database, and record who is accountable when a restore fails. The package answers "can the operator click a button?" It does not answer "who is on call at 3am?" Until that drill has been run, the panel must not become the artifact we point at when asked whether we are backed up.
How can I help you explore Laravel packages today?