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

Db Dumper Laravel Package

spatie/db-dumper

A PHP package for dumping MySQL, MariaDB, PostgreSQL, SQLite, and MongoDB databases with a fluent API. It wraps native tools like mysqldump and pg_dump, letting you write a dump straight to a file in just a few lines of code.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a thin, fluent wrapper around the native CLI dump binaries — it does not use a PDO/DBAL connection. So step zero is verifying the binary exists on the machine that runs PHP, not just that Laravel can connect:

composer require spatie/db-dumper

# must exist in PATH (or be pointed at with setDumpBinaryPath)
which mysqldump mariadb-dump pg_dump sqlite3 mongodump gzip

First working call:

use Spatie\DbDumper\Databases\MySql;

MySql::create()
    ->setDbName(config('database.connections.mysql.database'))
    ->setUserName(config('database.connections.mysql.username'))
    ->setPassword(config('database.connections.mysql.password'))
    ->setHost(config('database.connections.mysql.host'))
    ->dumpToFile(storage_path('app/backups/dump.sql'));

Where to look first

  1. src/Databases/{MySql,MariaDb,PostgreSql,Sqlite,MongoDb}.php — the real API surface; each dumper only implements the flags its binary supports.
  2. src/Dumper.php — the shared base: dumpToFile(), getDumpCommand(), includeTables(), excludeTables(), useCompressor(), addExtraOption(), setDatabaseUrl(), setTimeout().
  3. src/Compressors/ — GzipCompressor, Bzip2Compressor, and the two-method Compressor interface.
  4. CHANGELOG.md — behaviour changes here are subtle but real (e.g. 2.13.1 removed pipefail when compressing).

First real use case: a queued/scheduled backup of a production DB to S3, or a db:pull command that grabs a fresh dump from staging into storage/app/local.sql so you can db:wipe + import on your laptop. Both are ~30 lines.

Implementation Patterns

1. Resolve the dumper from the Laravel connection config, not from hardcoded classes

Most teams support more than one driver across environments, so wrap the selection:

function dumperFor(string $connection): Dumper
{
    $c = config("database.connections.$connection");

    $dumper = match ($c['driver']) {
        'mysql'    => MySql::create(),
        'mariadb'  => MariaDb::create(),
        'pgsql'    => PostgreSql::create(),
        'sqlite'   => Sqlite::create()->setDbName($c['database']),
        'mongodb'  => MongoDb::create(),
        default    => throw new InvalidArgumentException("Unsupported driver {$c['driver']}"),
    };

    // SQLite has no credentials/host
    if ($c['driver'] === 'sqlite') {
        return $dumper;
    }

    return $dumper
        ->setDbName($c['database'])
        ->setUserName($c['username'])
        ->setPassword($c['password'])
        ->setHost($c['host'])
        ->setPort($c['port'] ?? 3306)
        ->setTimeout(0); // no timeout for big prod DBs
}

2. Prefer setDatabaseUrl() when the platform hands you a DSN

Heroku, Railway, Fly, Render and most managed Postgres/MySQL providers expose a single URL. The package parses scheme, host, port, user, password and database for you:

MySql::create()
    ->setDatabaseUrl(config('database.connections.mysql.url'))
    ->dumpToFile($path);

This is the cleanest path for ephemeral/preview environments and avoids re-parsing the URL yourself.

3. Schedule it, keep it out of the request cycle

// routes/console.php (Laravel 11+) or Console\Kernel::schedule()
Schedule::command('db:backup --connection=mysql')
    ->dailyAt('02:30')
    ->withoutOverlapping()
    ->runInBackground();

Inside the command, stream straight to a Flysystem disk:

$tmp = tempnam(sys_get_temp_dir(), 'dump');   // local scratch space

dumperFor($connection)
    ->useCompressor(new GzipCompressor())
    ->dumpToFile($tmp);

Storage::disk('s3')->put(
    'backups/'.now()->format('Y-m-d_His').'.sql.gz',
    fopen($tmp, 'r')
);

unlink($tmp);

Compression matters — an uncompressed 4 GB dump uploaded from a small worker will blow the disk quota in sys_get_temp_dir() before S3 ever sees it. Also, don't dumpToFile() directly to an S3 path; the binary needs a real filesystem path.

4. Partial dumps are the daily bread-and-butter

  • Schema-only snapshots for seeding local dev: ->doNotDumpData().
  • Data-only for a fixture refresh: ->doNotCreateTables().
  • Structure without the noise: ->excludeTables(['cache','sessions','telescope_entries']).
  • Structure but no rows for append-only tables: ->excludeTablesData(['logs','audit_trail']) (MySQL/MariaDB only).
  • Two-pass dumps using append mode — dump schema, then append a subset of live data into the same file:
$d = dumperFor('mysql');
$d->doNotDumpData()->dumpToFile($file);           // pass 1: schema
$d->doNotCreateTables()->includeTables(['products'])->useAppendMode()->dumpToFile($file);

5. Use getDumpCommand() when you need to control execution

dumpToFile() shells out via Symfony Process. If you want to pipe through your own compressor, run it inside an already-privileged process, or just log what it would run while debugging:

$cmd = dumperFor('mysql')->getDumpCommand('dump.sql', 'credentials.txt');
Log::info('dumping', ['cmd' => $cmd]);

Note the second argument: the package writes credentials to that temp file and references it with --defaults-extra-file, keeping the password out of ps. Preserve that pattern if you execute the string yourself.

6. Use it as a dev-workflow tool, not only for backups

A php artisan db:pull command that dumps staging, drops local tables via db:wipe, and pipes the file back into mysql is often a bigger developer-productivity win than the nightly backup. Wrap it in a confirmation prompt and refuse to run when app()->isProduction().

7. Test fixtures: for integration tests needing a realistic schema, dump once, commit the (small, anonymised) file, and import in a setUpBeforeClass(). Faster and more faithful than migrations when the schema is large — but keep the file under a few MB and scrub PII.

Gotchas and Tips

The binary is not the PHP driver. The classic "works locally, fails in Docker" bug: PHP-FPM talks to MySQL fine over TCP, but mysqldump isn't installed in the app image. Install default-mysql-client / postgresql-client in the runtime image (not just the build stage), and remember gzip/bzip2 if you use a compressor.

Hosts differ inside containers. 127.0.0.1 from inside a PHP container is the container itself. Use the compose service name (db) and the internal port, or use a socket. For socket connections, setHost() with a socket path works for MySQL; if not, pass it explicitly: ->addExtraOption('--socket=/var/run/mysqld/mysqld.sock') and unset the host.

Version skew is the #1 failure. mysqldump 8.x queries information_schema.column_statistics, which doesn't exist on MySQL 5.7 servers — the dump dies mid-run. Fix: ->doNotUseColumnStatistics(). Similarly, pg_dump must be at least the server's major version; older clients refuse to dump newer servers. Pin the client version in your image to the server version.

Passwords never belong in addExtraOption(). Everything you pass there is concatenated raw into the shell command. That means both a credential leak in ps and, potentially, shell injection if the value is user-supplied. Use setPassword()/setDatabaseUrl() and reserve addExtraOption() for static, hardcoded flags (--xml, --skip-comments, --set-gtid-purged=OFF). Same for table names — don't feed request data into includeTables().

--databases overrides setDbName(). The README calls this out because it's surprising: addExtraOption('--databases dbname') or --all-databases supersedes an earlier setDbName(). Order your builder calls accordingly, or you'll dump every database on the server when you meant one.

Timeouts default to something finite. For multi-GB dumps, set ->setTimeout(0) or the process will be killed and you'll get a truncated file that looks fine. Related: check the exit code / catch the exception, and delete the partial file on failure — a half-written dump that silently lands in your backups bucket is worse than no dump.

No pipefail since 2.13.1. When a compressor is in the pipeline, a failing mysqldump can still produce a valid-looking .gz containing a zero-byte or error-only stream. Verify dump integrity separately (non-zero file size, gzip -t, or a header check) rather than trusting "the file exists".

dumpToFile() overwrites unless you ask it not to. It also won't create intermediate directories — mkdir your backup folder first. And it uses the extension you give it purely for naming; compression output only happens if you attach a compressor, and the correct extension comes from Compressor::useExtension().

SQLite quirks. setDbName() takes the file path, not a database name. Concurrent writes and WAL mode can produce a snapshot that omits recent commits — for anything but a dev fixture, either checkpoint WAL or copy the file while the app is quiet.

Extension points worth knowing. Implement Compressor (two methods: useCommand(), useExtension()) for zstd, pigz or xz — a one-liner class gets you parallel compression on a 16-core CI box. For a driver the package doesn't ship (e.g. ClickHouse, DuckDB), extend Dumper and implement getDumpCommand(); you inherit all the table-inclusion, compression and timeout plumbing for free.

Debugging rhythm: when a dump fails, reproduce with getDumpCommand(), run that exact string by hand, then read stderr. The PHP exception wraps the process exit but usually buries the informative line — running the command directly surfaces "Access denied", "Unknown table 'COLUMN_STATISTICS'", or "server version mismatch" in seconds.

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.
illuminate/framework
mrclay/shibalike
consoletvs/links
isapp/laravel-forge-mcp
roundly-consulting/safe-fetch-for-laravel
uthman/hashid
magentron/laravel-html
encoredjw/laravel-admin
roundly-consulting/imports-for-laravel
stillat/blade-directives
stonecoldtea/laravel-permission
flareia/laravel-sdk
trotexnet/laravel-h5p
luzucheng59/voyager
djstarcom/laravel-breadcrumbs
djstarcom/laravel-server-timing
class-atlas/laravel-flitt
ipregistry/ipregistry-laravel
daimakuai-ext/config
tomassalazarlopez/laravel-filemanager