paulocastellano / laravel-database-backup
Back up your Laravel database (PostgreSQL, MySQL, MariaDB) to a private S3-compatible bucket on a schedule, with email and Discord notifications.
Package info
github.com/paulocastellano/laravel-database-backup
pkg:composer/paulocastellano/laravel-database-backup
Fund package maintenance!
Requires
- php: ^8.3
- ext-zlib: *
- guzzlehttp/guzzle: ^7.8||^8.0
- illuminate/console: ^12.0||^13.0
- illuminate/database: ^12.0||^13.0
- illuminate/filesystem: ^12.0||^13.0
- illuminate/http: ^12.0||^13.0
- illuminate/mail: ^12.0||^13.0
- illuminate/process: ^12.0||^13.0
- illuminate/support: ^12.0||^13.0
- league/flysystem-aws-s3-v3: ^3.0
Requires (Dev)
- larastan/larastan: ^3.9
- laravel/pao: ^1.0
- laravel/pint: ^1.29
- orchestra/testbench: ^10.0||^11.0
- pestphp/pest: ^4.6||^5.0
- pestphp/pest-plugin-laravel: ^4.1||^5.0
- pestphp/pest-plugin-type-coverage: ^4.0||^5.0
- phpstan/extension-installer: ^1.4
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-13 15:10:22 UTC
README
Laravel Database Backup
Back up your Laravel database (PostgreSQL, MySQL, MariaDB) to a private S3-compatible bucket on a schedule, with email and Discord notifications.
Why
- Database only. Your code lives in Git; this backs up the one thing Git does not have.
- Streams the dump.
pg_dump/mysqldumpoutput is gzipped while it streams, so a 1 GB database never sits in memory. - Restores, tested. The test suite dumps a real PostgreSQL and MySQL database, restores the file into an empty one and compares every row.
- Private by default. The file is uploaded private, the password never reaches the command line, and notifications never link to the file.
- Tells you. Every run, successful or failed, by email and on Discord.
Requirements
- PHP 8.3+ and Laravel 12 or 13.
- PostgreSQL (
pg_dump), MySQL or MariaDB (mysqldump) on the server that runs the scheduler.pg_dumpmust be the same major version as the server or newer. - A private S3-compatible bucket: Amazon S3, Cloudflare R2, DigitalOcean Spaces, MinIO...
Installation
composer require paulocastellano/laravel-database-backup
Then set your bucket and turn the schedule on in .env:
BACKUP_ENABLED=true BACKUP_INTERVAL_HOURS=6 # every N hours on the hour, UTC (1-24) BACKUP_KEEP_DAYS=30 # 0 keeps every backup BACKUP_BUCKET=my-backups # required BACKUP_DIRECTORY=my-app # required: one bucket can hold several apps BACKUP_ENDPOINT=https://<account>.r2.cloudflarestorage.com BACKUP_REGION=auto BACKUP_ACCESS_KEY_ID= BACKUP_SECRET_ACCESS_KEY= BACKUP_NOTIFY_EMAIL=ops@example.com BACKUP_NOTIFY_DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/... BACKUP_DB_CONNECTION= # optional: the connection to dump, else the default one BACKUP_TIMEOUT=3600 # optional: seconds the dump may take
BACKUP_ENDPOINT picks the provider; leave it empty for Amazon S3. The scheduler must be running (php artisan schedule:run every minute), as for any Laravel scheduled task.
The email goes through your app's mailer (MAIL_*). The dump is written to the system temporary directory (TMPDIR), readable by its owner only, before it is uploaded and deleted, so that directory needs room for one compressed dump.
Usage
Back up now, whether or not the schedule is enabled:
php artisan backup:database
Each run:
- dumps the database (the default connection, or
BACKUP_DB_CONNECTION) to<BACKUP_DIRECTORY>/YYYYMMDDHHMMSS.sql.gz, named after the UTC time; - deletes the backups in
BACKUP_DIRECTORYolder thanBACKUP_KEEP_DAYS. The directory belongs to the backups alone; the rest of the bucket is never listed; - sends the result to
BACKUP_NOTIFY_EMAILand the Discord webhook. Either left blank is skipped, and a notification that fails never fails the backup.
A failed run (a missing bucket or directory, a dump error, an upload error) throws PauloCastellano\DatabaseBackup\Exceptions\DatabaseBackupException or the filesystem's own exception, exits with an error, is reported like any exception, and is announced by email and on Discord. The email is sent at once, not queued, so you hear about it even when your queue workers are down.
Restoring a backup
There is no restore command, on purpose: one wrong argument would overwrite production. Download the file from your bucket, restore it into a new database, check it, then point your app at it:
createdb my_app_restored gunzip -c 20261113135350.sql.gz | psql -v ON_ERROR_STOP=1 my_app_restored # MySQL / MariaDB gunzip -c 20261113135350.sql.gz | mysql my_app_restored
Configuration
Publish the config file to change anything beyond the environment variables:
php artisan vendor:publish --tag="laravel-database-backup-config"
Customizing the emails
The emails are plain Blade views. Publish them to resources/views/vendor/laravel-database-backup and edit them there:
php artisan vendor:publish --tag="laravel-database-backup-views"
Testing
composer test
The restore test runs only against a real server. Point it at an empty PostgreSQL or MySQL database:
DB_CONNECTION=pgsql DB_HOST=127.0.0.1 DB_PORT=5432 DB_DATABASE=testing DB_USERNAME=postgres DB_PASSWORD= vendor/bin/pest
Built by Paulo Castellano
Hi, I'm Paulo. I built this package for TryPost, where it backs up the production database every few hours. Follow me for what is coming next.
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Thank you for considering contributing to Laravel Database Backup! Please review our contributing guide to get started.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
Laravel Database Backup is open-sourced software licensed under the MIT license.
