Muhammad Yasir Amin

Loading websiteโ€ฆ

More from Muhammad Yasir Amin

Build Along With Me

Practical walkthroughs on Laravel, React, APIs and deployment workflows.

Subscribe on YouTube

Automatic Daily Database Backups in Laravel 13 with Spatie Laravel Backup

๐Ÿ› ๏ธ Laravel Tutorials5 min read

Automatic Daily Database Backups in Laravel 13 with Spatie Laravel Backup

Muhammad Yasir Amin

Muhammad Yasir Amin

Senior Full Stack Engineerโ€ขNov 12, 2026
Automatic Daily Database Backups in Laravel 13 with Spatie Laravel Backup

To set up automatic daily database backups in Laravel 13, install spatie/laravel-backup, publish its config, point the backup destination at a disk (ideally an offsite one such as S3), and schedule backup:clean and backup:run --only-db in routes/console.php. The Laravel scheduler, triggered by one cron entry, then creates a zipped database dump every day.

Backups are one of those things nobody thinks about until the day they are needed. The setup below takes about fifteen minutes. The part people skip, and should not, is the restore test at the end.

Prerequisites

  • Laravel 13.x. Note that spatie/laravel-backup v10 requires PHP 8.4 or newer with the zip extension, which is stricter than Laravel 13's own PHP 8.3 minimum.
  • The dump tool for your database on the server: mysqldump for MySQL/MariaDB or pg_dump for PostgreSQL.
  • A Linux server for the scheduled job. The package documents that it does not support Windows servers.
  • Enough free disk space for at least one full backup.

Step 1: Install the package

Code
composer require spatie/laravel-backup
php artisan vendor:publish --provider="Spatie\Backup\BackupServiceProvider" --tag=backup-config

This creates config/backup.php. The service provider registers itself.

Step 2: Create a dedicated backup disk

A backup stored on the same server as the database does not protect you from losing that server. Add a disk to config/filesystems.php. Here I use S3 with a separate bucket (install league/flysystem-aws-s3-v3 first):

JavaScript
'disks' => [
    // ...

    'backups' => [
        'driver' => 's3',
        'key' => env('BACKUP_AWS_ACCESS_KEY_ID'),
        'secret' => env('BACKUP_AWS_SECRET_ACCESS_KEY'),
        'region' => env('BACKUP_AWS_DEFAULT_REGION'),
        'bucket' => env('BACKUP_AWS_BUCKET'),
        'throw' => true,
    ],
],

Give these credentials write access to the backup bucket only, so a leaked key cannot touch your other data.

Step 3: Configure what gets backed up

Open config/backup.php and adjust the key options:

JavaScript
'backup' => [
    'name' => env('APP_NAME', 'laravel-backup'),

    'source' => [
        'files' => [
            'include' => [base_path()],
            // exclude vendor, node_modules, etc. (already in the default list)
        ],
        'databases' => [env('DB_CONNECTION', 'mysql')],
    ],

    'destination' => [
        'disks' => ['backups'],
    ],
],

If the dump binary is not on the default path, set dump.dump_binary_path on the connection in config/database.php. Only put the directory path there, not the binary name. Large databases may also need a longer dump.timeout than the default 60 seconds.

Step 4: Schedule the backup

In Laravel 13, scheduled tasks live in routes/console.php:

CSS
<?php

use Illuminate\Support\Facades\Schedule;

Schedule::command('backup:clean')->daily()->at('01:00');

Schedule::command('backup:run --only-db')
    ->daily()
    ->at('01:30')
    ->onOneServer()
    ->withoutOverlapping();

Schedule::command('backup:monitor')->daily()->at('09:00');

--only-db skips application files, which usually live in Git anyway. Remove the flag to include files listed in source.files. onOneServer() stops duplicate backups when several servers run the scheduler; it requires a shared cache driver. The package recommends avoiding 02:00 to 03:00 if your server timezone uses daylight saving time.

Finally, make sure the scheduler runs every minute through cron on the server:

Shell
* * * * * cd /var/www/your-app && php artisan schedule:run >> /dev/null 2>&1

How do I get notified if a backup fails?

The package sends notifications for failed backups, failed cleanups and unhealthy backups. Set the recipient in config/backup.php:

JavaScript
'notifications' => [
    // ...
    'mail' => [
        'to' => 'ops@example.com',
    ],
],

backup:monitor checks the monitor_backups rules, by default that the newest backup is not older than one day and the total size stays under a limit. It alerts you when the scheduled job silently stopped running, which a failure email alone cannot catch.

The default monitor entry watches the local disk. Change its disks value to ['backups'] so the health checks look at the disk you actually write to.

How do old backups get removed?

backup:clean applies the retention strategy in the cleanup section: keep all backups for a few days, then daily, weekly, monthly and yearly backups for set periods, and delete the oldest backups when the storage limit is reached. Tune keep_daily_backups_for_days and the related keys to your recovery needs.

Common mistakes

  • Backing up to the local disk only. A disk failure or a compromised server takes the backups with it.
  • Cron not configured. Without the schedule:run entry nothing runs. Check with php artisan schedule:list.
  • mysqldump: command not found. Install the client tools or set dump_binary_path. In Docker images, the client is often missing.
  • Wrong PHP version. v10 needs PHP 8.4. Composer will refuse to install it on PHP 8.3.
  • Never testing a restore. An untested backup is a hope, not a backup.

How to verify it works

  1. Run a backup by hand: php artisan backup:run --only-db. The output reports the dump, the zip and the copy to each disk.
  2. List backups and their health: php artisan backup:list.
  3. Confirm the schedule: php artisan schedule:list should show both commands with their next run time.
  4. Do a restore drill: download the zip, unzip it, and import the SQL file into a fresh local database, for example with mysql new_db < path/to/dump.sql using the .sql file from the db-dumps folder. Then boot the app against it.

FAQ

Can I encrypt the backup archive?

Yes. Set BACKUP_ARCHIVE_PASSWORD in .env; the config reads it as the archive password. Store that password outside the server, or the backup is useless when the server is gone.

Can I back up to two places at once?

Yes. List several disks in destination.disks, for example ['local', 'backups'].

Does this replace managed database snapshots?

Not necessarily. Provider snapshots are great for full recovery; an application-level dump in another account or provider is a useful independent second copy.

References

Article Index & Topic Tags
LaravelDatabaseSchedulerBackup

YouTube Channel

Build Along With Me

Practical walkthroughs on Laravel, React, APIs and deployment workflows.

Muhammad Yasir Amin
Available for Contracts
Author & Solutions Architect

Senior Full Stack Engineer

Independent Senior Full Stack Engineer working on Laravel, React and Next.js applications, APIs and delivery workflows.

Laravel & PHP ApplicationsReact & Next.js InterfacesREST APIs & IntegrationsDatabase Queries & DebuggingDocker Development WorkflowsClear Scope & Handover
Discuss Your Next Step

What Does Your Application Need Next?

Share your current system, the problem you want to solve and what a useful result looks like. We can identify the right starting point and agree on a practical scope.

Technical Audit

Find the Next Useful Fix

Application Development

Scoped, Reviewable Milestones

Ongoing Support

Agreed Capacity and Priorities

Share your goals, current stack and requirements before the call.