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

Filament Spatie Laravel Backup Laravel Package

shuvroroy/filament-spatie-laravel-backup

A Filament page for creating and managing application backups using spatie/laravel-backup. Register the plugin in your panel provider, publish assets, and get a ready-made Backups page with customizable icon and heading.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a thin Filament page on top of spatie/laravel-backup, so the day-one job is really "get Spatie working, then bolt on the UI".

1. Install and publish assets

composer require shuvroroy/filament-spatie-laravel-backup
php artisan filament:assets

The filament:assets step is easy to forget and is the usual cause of a page that renders but looks broken after an upgrade.

2. Make sure spatie/laravel-backup is actually configured

The plugin respects your config/backup.php, so publish and review it before touching the panel:

php artisan vendor:publish --provider="Spatie\Backup\BackupServiceProvider"

At minimum set backup.destination.disks (e.g. ['backups'] pointing at S3 or a local disk outside the webroot), and sanity-check backup.source.files.include/exclude and backup.source.databases. Run one from the CLI first to prove credentials and paths:

php artisan backup:run
php artisan backup:list

If backup:list is empty, the UI will be empty too — debug there, not in Filament.

3. Register the plugin in the panel

use ShuvroRoy\FilamentSpatieLaravelBackup\FilamentSpatieLaravelBackupPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->plugin(
            FilamentSpatieLaravelBackupPlugin::make()
                ->navigationGroup('Settings')
                ->navigationSort(99)
                ->authorize(fn (): bool => auth()->user()->hasRole('admin'))
        );
}

First real use case: give operators a "take a backup before I deploy / run this migration" button instead of a terminal. That is the moment this package pays for itself — the page is a controlled, permissioned trigger for backup:run plus a list of what already exists on disk.

Where to look first when something is off: config/backup.php (Spatie), then the panel provider (plugin options), then ShuvroRoy\FilamentSpatieLaravelBackup\Pages\Backups (the page class you extend for anything UI-level).

Implementation Patterns

Lock the page down before you ship it. The authorize() callback on the plugin is the coarse gate for who can even see the page:

FilamentSpatieLaravelBackupPlugin::make()
    ->authorize(fn (): bool => auth()->user()->can('view_backups'))

Then handle the fine-grained actions. The page checks three ability names — create-backup, download-backup, delete-backup — so with spatie/laravel-permission or Filament Shield you must create them yourself; they are not auto-discovered by Shield's policy generation:

foreach (['create-backup', 'download-backup', 'delete-backup'] as $name) {
    Permission::firstOrCreate(['name' => $name, 'guard_name' => 'web']);
}

This split is genuinely useful: an on-call role gets create-backup + download-backup but not delete-backup, so nobody can wipe the retention window from the UI.

Make backups asynchronous in production. Synchronous backups in a web request are how you get 504s and half-written zips:

FilamentSpatieLaravelBackupPlugin::make()
    ->usingQueue('backups')
    ->usingPolingInterval('10s')
    ->timeout(600)

Run a dedicated worker (php artisan queue:work --queue=backups --timeout=900). The trade-off is UX: the modal closes immediately and the archive appears in the list a minute later, so the polling interval is doing real work here — don't leave it at 4s against an S3 disk.

Extend the page class when you need more than navigation. Plugin setters cover icon/label/group/sort, but for anything structural (extra table columns, a custom header action, your own empty-state copy) subclass it and register via usingPage():

class Backups extends BaseBackups
{
    protected static string | \BackedEnum | null $navigationIcon = 'heroicon-o-circle-stack';

    public function getHeading(): string | Htmlable
    {
        return 'Application Backups';
    }

    public static function getNavigationGroup(): ?string
    {
        return 'Core';
    }
}

// AdminPanelProvider
FilamentSpatieLaravelBackupPlugin::make()->usingPage(Backups::class);

Keep the override in App\Filament\Pages so Composer updates never fight you.

Pair scheduled backups with the UI. Let Spatie's scheduler own the nightly cadence (backup:run then backup:clean then backup:monitor), and treat the Filament page as the on-demand + inspection surface. That keeps the backup list meaningful (retention stays predictable) while still giving humans a button when they need one.

Environment-scoped registration. In local you generally don't want the page at all:

if (! app()->isLocal()) {
    $panel->plugin(FilamentSpatieLaravelBackupPlugin::make());
}

Or keep it and gate with authorize(fn () => auth()->user()->isAdmin()).

Localisation. Publish translations and override the keys per panel language:

php artisan vendor:publish --tag="filament-spatie-backup-translations"

Multi-panel / multi-tenant note. Each panel gets its own plugin instance, so an ops panel and a customer panel can use different pages, groups and authorization — but they all read and write the same disks defined in config/backup.php. If you need per-tenant backup isolation, that's a Spatie config concern (per-tenant disks / backup.backup.name), not something the plugin does for you.

Gotchas and Tips

  • usingPolingInterval() is spelled with one L. That's the actual method name in the package; typing usingPollingInterval gives an undefined-method error. Rely on IDE completion rather than memory.
  • Undefined permissions silently hide actions. With spatie/laravel-permission installed, can('create-backup') returns false for a permission that doesn't exist, so the buttons just vanish with no error. Seed the three permissions before users complain.
  • No queue worker, no backup. If you call usingQueue(), the job sits in the jobs table forever and the page shows nothing new. Failed jobs also die quietly — watch failed_jobs and Horizon/logs, not the Filament page.
  • timeout() is not the queue worker's timeout. It maps to set_time_limit, which is a no-op under CLI and often disabled on shared hosting. Set --timeout on the worker and retry_after in config/queue.php higher than your timeout(), otherwise the job is retried mid-run and you get duplicate archives. noTimeout() is the honest option for large databases.
  • The list is disk-derived, not database-derived. There's no backups table. An empty or stale list almost always means the disk in config/backup.php doesn't match what the UI is configured against, or credentials lack ListBucket. There is no error surface for this.
  • Polling + S3 = API calls. A 4s wire:poll on a page that lists an S3 prefix generates a LIST request every 4 seconds per open tab. Bump the interval to 10s/30s for remote disks; it also reduces latency complaints.
  • Downloads stream through the admin panel. For multi-GB archives this can hit PHP memory_limit/max_execution_time or proxy body-size limits. For big restores, prefer grabbing the signed URL / copying from the bucket directly, and keep the download button for small DB-only archives.
  • There is no restore feature. This package creates, lists, downloads and deletes — nothing else. Write an explicit runbook (gunzip → mysql < db-dump.sql → sync storage) and rehearse it, because a backup you've never restored is a hypothesis.
  • Trim the source, not the retention. Backup size pain is usually backup.source.files.include/exclude including node_modules, vendor, logs or storage/app caches. Fixing that in Spatie's config is far cheaper than raising disk quotas.
  • Add backup:monitor to the schedule (daily), with a notification channel configured. The UI tells you what exists; backup:monitor tells you when nothing new has appeared, which is the failure mode that actually hurts.
  • backup.backup.name must be unique per app if disks are shared. Two apps writing to the same S3 prefix will show each other's archives in the same page.
  • Upgrading 1.x → 2.x is a breaking change from config-file page registration to panel plugins — read UPGRADE.md, and re-run php artisan filament:assets afterwards.
  • Customisation extension points, in order of preference: plugin setters (navigation, polling, queue, timeout, authorize) → subclassing Pages\Backups for UI → config/backup.php for what actually gets backed up. Reaching for a full custom page is almost never necessary.
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.
dakiquang/laravel-oauth-refresh-lock
woda/laravel-worktrees
dalefranks/ramasite
faghy/laracashbook
roundly-consulting/google-places-for-laravel
roundly-consulting/kubernetes-api-for-laravel
roundly-consulting/plausible-for-laravel
gvp/gvp-laravel-template
metasyncsite/laravel-metasync-client
roundly-consulting/onboarding-for-laravel
andydefer/laravel-notification
adachsoft/cli-process-tester
jhaskell13/laraflow
sebastienheyd/boilerplate-media-manager
ccast/tagixo-primix
pretendtrue/laravel-builder
sawmainek/laravel-scout-tntsearch-driver
tomshaw/laravel-calendar-table
dibakar/laravel-ownership
adachsoft/changelog-linter