Open-source project
spatie/laravel-db-snapshots avatar
spatie/laravel-db-snapshots

laravel-db-snapshots: database dumps you can restore from Artisan

Quickly dump and load databases

1,221 stars100 forksPHPMIT

At a glance

What is it?
laravel-db-snapshots gives a Laravel application Artisan commands to create, load, list, delete and clean up database dumps, with support for MySQL, PostgreSQL and SQLite and per-table inclusion or exclusion. It is MIT licensed, the current release is 2.10.1 from 2026-09-28, and the last push was on the same day.
Who is it for?
The package fits a Laravel developer who needs a database snapshot before a risky migration, a seed run or a manual edit, and who wants to restore it without leaving the terminal. It does not fit a production backup system, since the snapshots live on a filesystem disk and the loading path defaults to dropping every table.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Six commands that cover the whole snapshot lifecycle

The package provides Artisan commands to quickly dump and load databases in a Laravel application, and the whole surface fits in one block.

bash
# Create a dump
php artisan snapshot:create my-first-dump

# Make some changes to your db
# ...

# Create another dump
php artisan snapshot:create my-second-dump

# Load up the first dump
php artisan snapshot:load my-first-dump

# Load up the latest dump
php artisan snapshot:load --latest

# List all snapshots
php artisan snapshot:list

# Remove old snapshots. Keeping only the most recent
php artisan snapshot:cleanup --keep=2

The workflow the commands imply is the reason the package exists. You snapshot, you break the database, you snapshot again, and you go back to the first one. That is a development loop, and it is a loop that otherwise means either a hand-run `mysqldump` with a path you have to remember, or a database seed you have to reconstruct.

The `--latest` flag on load is the small convenience that makes the loop tight, since after a few iterations nobody remembers the timestamped name of the snapshot they want.

The package supports MySQL, PostgreSQL and SQLite, which matters more than it sounds. A dump format is not portable between engines, so a package that supports all three can sit in a codebase where the development database and the test database are not the same engine, and where a team member on a different platform is not running the same database.

The project is from Spatie, the PHP shop, and the package is published on Packagist as `spatie/laravel-db-snapshots`. The homepage is a blog post about building the package, and the release history is a steady 2.x line with 2.10.1 on 2026-09-28, 2.10.0 on 2026-04-01 and 2.9.0 on 2026-03-16.

The disk is yours to configure, and the temp directory is separate

Installation is a Composer command, and then one filesystem decision that the documentation is specific about.

bash
composer require spatie/laravel-db-snapshots

You should add a disk named `snapshots` to `config/filesystems.php` on which the snapshots will be saved:

php
// ...
'disks' => [
    // ...
    'snapshots' => [
        'driver' => 'local',
        'root' => database_path('snapshots'),
    ],
// ...

The `driver` is `local` in the example, but the disk is a normal Laravel filesystem disk, which means it can be `s3` or anything else you configure. That is a deliberate design choice, because it means the snapshots can live in the same place as the rest of your application's persistent files, on whatever storage the deployment uses, without the package knowing anything about storage backends.

Optionally you can publish the configuration file:

bash
php artisan vendor:publish --provider="Spatie\DbSnapshots\DbSnapshotsServiceProvider"

And the published file is short enough to read in one pass. It has the `disk` on which the snapshots are stored, the `default_connection` to be used to create snapshots, which you set to null to use the default configured in `config/databases.php`. The `temporary_directory_path` is where temporary files will be stored, defaulting to `storage_path('app/laravel-db-snapshots/temp')`, and there is a `compress` flag for gzipped dump files.

The two filtering options are `tables`, which lists the only tables to be included and defaults to null for all of them, and `exclude`, which lists the tables to be left out and also defaults to null. The distinction between an include list and an exclude list is what makes the package usable in a project with a migrations table or a queue jobs table you do not want to churn through on every restore.

Naming, filtering and the rule that --table wins over --exclude

The command line options are where the package earns its keep in a multi-connection application.

Naming is optional. If you don't pass a name the current date time will be used, so `php artisan snapshot:create` produces a snapshot named something like `2017-03-17 14:31`. And if you pass a connection but do not declare a name, the connection will be prepended, so `php artisan snapshot:create --connection=logging` produces something like `logging_2017-03-17 14:31`.

That naming convention is doing more work than it appears to. The connection is in the name, so a snapshot list is grouped by connection without needing a separate directory, and a restore that names the wrong connection is visible from the filename alone.

Table filtering works with both a comma separated list and repeated flags, and the two forms are equivalent:

bash
# Both commands create a snapshot containing only the posts and users tables:
php artisan snapshot:create --table=posts,users
php artisan snapshot:create --table=posts --table=users

And exclusion works the same way:

bash
# create snapshot from all tables excluding the users and posts
php artisan snapshot:create --exclude=posts,users
php artisan snapshot:create --exclude=posts --exclude=users

The one rule to know is documented as a note, and it is a sharp edge: if you pass `--table` and `--exclude` at the same time it will use `--table` to create the snapshot and ignore the `--exclude`.

That is a defensible choice, since an include list and an exclude list together are contradictory, and picking the include list is the safer of the two. But it is silent, and a command that quietly ignores one of your flags is a command you will misremember.

Compression is the last creation option, and it can be set on the command line or in the configuration:

bash
# Creates a snapshot named my-compressed-dump.sql.gz
php artisan snapshot:create my-compressed-dump --compress

Restoring drops every table unless you tell it not to

The load side has two defaults that matter, and both are about the same thing, which is that a restore is destructive.

The first is that by default `snapshot:load` will drop all existing tables in the database. If you don't want this behaviour, you can pass the `--drop-tables=0` option:

bash
php artisan snapshot:load my-first-dump --drop-tables=0

The second is that by default `snapshot:load` will load the entire snapshot into memory, which may cause problems when using large files. To avoid this, you can pass the `--stream` option to stream the snapshot to the database one statement at a time:

bash
php artisan snapshot:load my-first-dump --stream

Together those two defaults are a trap for a large database. The default load path holds the whole dump in memory, and the default is to drop every table, so a snapshot of a few hundred megabytes on a machine with a modest memory limit is a command that fails partway through, after the tables have already been dropped. `--stream` and `--drop-tables=0` are the two flags to know.

The connection override works for loads too, so a snapshot can be loaded into a different database than the one it came from:

bash
php artisan snapshot:load my-first-dump --connection=connectionName

Which is what makes a snapshot useful for seeding: dump a known-good dataset, then load it into a fresh environment, which is the same loop a database seeder does but with the data captured rather than written by hand.

The remaining commands are the obvious ones. `snapshot:list` shows the dumps, `snapshot:delete my-first-dump` removes one, and `snapshot:cleanup --keep=2` removes all backups except the most recent two. The cleanup command is the one to put in a scheduled task, and it is also the one to be careful with, because it deletes by count and not by age.

Where laravel-db-snapshots is the wrong tool

The first case is a production backup system. These are database dumps written to a filesystem disk, created by a developer running an Artisan command. There is no scheduling, no retention policy beyond a keep count, no off-site replication, no point-in-time recovery and no integrity verification. A team that needs to restore from a production incident needs a real backup tool, and this package is not one.

The second case is an application whose database is large enough that a dump-and-restore round trip is not the right primitive. If the database is hundreds of gigabytes, a full dump on every change is slow, and the streaming option exists precisely because the in-memory default does not scale. Migrating between schema versions on a large database is a job for a migration tool with incremental steps, not for restoring a snapshot.

The third is a deployment pipeline that needs deterministic data. A snapshot captures the database at a moment in time including whatever manual edits were in it, which is exactly what you want when debugging and exactly what you do not want when you need every environment to start from the same known state. Laravel's database seeders exist for that, and they are reproducible, reviewable and diffable, where a snapshot is a binary artefact whose content nobody reviews.

The fourth is a team that wants the dumps somewhere the framework cannot reach. The snapshots are written through a Laravel disk, so a deployment with an ephemeral or read-only filesystem has nowhere to put them without configuring an object store, and the `temporary_directory_path` has to be writable too.

One more point about what the package is not. The documentation mentions that if you need to pass extra options to the underlying db-dumper, you add a `dump` key to the data, and the naming of that tool in the documentation makes the architecture clear: the package is a well-integrated front end for a dump tool that already existed, with the Artisan commands, the disk integration, the naming convention and the filters built on top. That is a sensible design, and it also means the behaviour of the dump itself is that of the underlying tool, so any option you need from that tool has to be reachable through the configuration.

Editorial conclusion

The package fits a Laravel developer who needs a database snapshot before a risky migration, a seed run or a manual edit, and who wants to restore it without leaving the terminal. It does not fit a production backup system, since the snapshots live on a filesystem disk and the loading path defaults to dropping every table. Before you rely on it, read the `--drop-tables=0` and `--stream` options, because both change the behaviour of a restore in ways that matter, and note that the underlying dump tooling is what actually talks to your database.

Frequently asked questions

How do I create and restore a database snapshot in Laravel?

Run `php artisan snapshot:create my-first-dump` to create one, and `php artisan snapshot:load my-first-dump` to restore it, or `php artisan snapshot:load --latest` for the most recent. `php artisan snapshot:list` shows them all and `php artisan snapshot:cleanup --keep=2` removes all but the two most recent.

Which databases does laravel-db-snapshots support?

MySQL, PostgreSQL and SQLite. The package is installed with `composer require spatie/laravel-db-snapshots`, and you add a disk named `snapshots` to `config/filesystems.php` for the snapshots to be saved on.

How do I snapshot only some tables?

Pass `--table` with a comma separated list or repeat the flag, for example `php artisan snapshot:create --table=posts,users`, and use `--exclude` in the same way for the tables to leave out. If you pass both `--table` and `--exclude`, the include list is used and the exclude list is ignored.

What does the --drop-tables and --stream option do on snapshot:load?

By default `snapshot:load` drops all existing tables in the database and loads the entire snapshot into memory, which can be a problem with large files. Pass `--drop-tables=0` to keep the existing tables and `--stream` to load the snapshot one statement at a time.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. spatie/laravel-db-snapshots on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/spatie-laravel-db-snapshots.svg)](https://hysenlabs.com/projects/spatie-laravel-db-snapshots)

Was this useful?