karim-dev / smart-backup
A Laravel package for creating and managing database and storage backups.
Requires
- php: ^8.2
- illuminate/support: ^11.0|^12.0
- spatie/laravel-backup: ^9.0|^10.0
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0
- phpunit/phpunit: ^11.0|^12.0
README
A simple Laravel package for creating and managing database and storage backups.
Smart Backup uses spatie/laravel-backup internally, but adds a cleaner workflow:
- Creates database and storage backups.
- Saves backup records in the database.
- Provides Artisan commands.
- Provides optional routes for dashboard/API usage.
- Supports single delete and bulk delete.
- Automatically detects
mysqldumpwhen possible. - Saves backups in one managed folder, default:
backups.
Requirements
- PHP
^8.2 - Laravel
^11.0|^12.0|^13.0 - MySQL/MariaDB requires
mysqldump
Installation
composer require karim-dev/smart-backup
If Composer needs to update related dependencies:
composer require karim-dev/smart-backup -W
Publish config and migration:
php artisan smart-backup:install php artisan migrate
smart-backup:installalready publishes the migration. Do not publish the migration again unless you need another copy.
Usage
Create a full backup (database + storage):
php artisan smart-backup:run
Create a database-only backup:
php artisan smart-backup:database
Create a storage-only backup:
php artisan smart-backup:storage
List backups:
php artisan smart-backup:list
Delete a backup:
php artisan smart-backup:delete {id}
Force delete:
php artisan smart-backup:delete {id} --force
What Is Backed Up?
By default, Smart Backup includes:
storage/app
storage/demo
storage/demo is included only if it exists.
Smart Backup does not back up the full Laravel project.
It does not include:
.env
vendor/
app/
routes/
resources/
storage/framework/
storage/logs/
Backup Location
By default, backups are saved in:
storage/app/private/backups
Example:
storage/app/private/
└── backups/
├── 2026-05-09-01-00-51.zip
└── 2026-05-09-01-01-48.zip
Smart Backup avoids keeping extra folders named after the project, such as:
storage/app/private/zamil
storage/app/private/Laravel
The final backup files should be kept inside the managed backups folder.
Configuration
The config file is published here:
config/smart-backup.php
Most projects do not need to add anything to .env.
Optional .env values:
SMART_BACKUP_DISK=local SMART_BACKUP_DIRECTORY=backups SMART_BACKUP_KEEP_BACKUPS=true SMART_BACKUP_PREVENT_DELETE_LAST=true SMART_BACKUP_MYSQL_DUMP_BINARY_PATH= SMART_BACKUP_MYSQL_DUMP_TIMEOUT=300 SMART_BACKUP_MYSQL_SINGLE_TRANSACTION=true
Optional Settings
Change backup folder
Default:
SMART_BACKUP_DIRECTORY=backups
Example:
SMART_BACKUP_DIRECTORY=system-backups
Keep only the latest backup
Default is to keep all backups:
SMART_BACKUP_KEEP_BACKUPS=true
To delete old backups after a successful new backup:
SMART_BACKUP_KEEP_BACKUPS=false
Prevent deleting the last backup
Default:
SMART_BACKUP_PREVENT_DELETE_LAST=true
Custom mysqldump path
Normally, you do not need this.
Smart Backup tries to detect mysqldump automatically from common paths like XAMPP, Laragon, WAMP, /usr/bin, and /usr/local/bin.
If detection fails, set:
SMART_BACKUP_MYSQL_DUMP_BINARY_PATH=C:/xampp/mysql/bin
or on Linux/cPanel:
SMART_BACKUP_MYSQL_DUMP_BINARY_PATH=/usr/bin
You do not need to edit config/database.php.
Routes
Smart Backup registers a route macro.
It does not load routes automatically, so you can register the routes wherever you want in your Laravel application.
Basic usage
use Illuminate\Support\Facades\Route; Route::smartBackup();
This will register the following routes:
GET /backup backup.index
POST /backup backup.store
GET /backup/{backup}/download backup.download
DELETE /backup/{backup} backup.destroy
DELETE /backup backup.bulk-destroy
You can use the route names like this:
route('backup.index'); route('backup.store'); route('backup.download', $backup); route('backup.destroy', $backup); route('backup.bulk-destroy');
Creating Backups via API / Dashboard
When creating a backup via the backup.store endpoint (POST /backup), the process runs as a Queue Job (RunSmartBackupJob) in the background. This ensures your web requests never timeout.
You can specify the backup type by passing it in the JSON payload.
Full Backup:
{
"type": "full"
}
Database Only:
{
"type": "database"
}
Storage Only:
{
"type": "storage"
}
The endpoint will respond instantly with:
{
"success": true,
"queued": true,
"message": "تم وضع النسخة الاحتياطية في الطابور بنجاح وسيتم تنفيذها في الخلفية."
}
Note: If your
.envis set toQUEUE_CONNECTION=sync, the backup will run immediately and wait to finish. If set todatabaseorredis, you must havephp artisan queue:workrunning on your server.
Dashboard usage
You can register Smart Backup routes inside your dashboard route group:
use Illuminate\Support\Facades\Route; Route::prefix('dashboard') ->name('dashboard.') ->middleware(['web', 'auth']) ->group(function () { Route::smartBackup(); });
This will register the following routes:
GET /dashboard/backup dashboard.backup.index
POST /dashboard/backup dashboard.backup.store
GET /dashboard/backup/{backup}/download dashboard.backup.download
DELETE /dashboard/backup/{backup} dashboard.backup.destroy
DELETE /dashboard/backup dashboard.backup.bulk-destroy
You can use the route names like this:
route('dashboard.backup.index'); route('dashboard.backup.store'); route('dashboard.backup.download', $backup); route('dashboard.backup.destroy', $backup); route('dashboard.backup.bulk-destroy');
Bulk delete
The bulk delete route is:
DELETE /backup
If you register the routes inside a dashboard group, it becomes:
DELETE /dashboard/backup
The route name will be:
route('backup.bulk-destroy');
Or inside dashboard:
route('dashboard.backup.bulk-destroy');
It expects an array of backup IDs:
{
"ids": [1, 2, 3]
}
Blade form example:
<form method="POST" action="{{ route('dashboard.backup.bulk-destroy') }}"> @csrf @method('DELETE') <input type="hidden" name="ids[]" value="1"> <input type="hidden" name="ids[]" value="2"> <input type="hidden" name="ids[]" value="3"> <button type="submit">Delete selected backups</button> </form>
AJAX example:
fetch("{{ route('dashboard.backup.bulk-destroy') }}", { method: "DELETE", headers: { "Content-Type": "application/json", "Accept": "application/json", "X-CSRF-TOKEN": "{{ csrf_token() }}" }, body: JSON.stringify({ ids: [1, 2, 3] }) });
Custom prefix and route name
You can customize the route prefix and route name:
use Illuminate\Support\Facades\Route; Route::smartBackup([ 'prefix' => 'backups', 'name' => 'backups.', ]);
This will register routes like:
GET /backups backups.index
POST /backups backups.store
GET /backups/{backup}/download backups.download
DELETE /backups/{backup} backups.destroy
DELETE /backups backups.bulk-destroy
You can use them like this:
route('backups.index'); route('backups.store'); route('backups.download', $backup); route('backups.destroy', $backup); route('backups.bulk-destroy');
Custom dashboard prefix and route name
You can also combine dashboard grouping with custom Smart Backup options:
use Illuminate\Support\Facades\Route; Route::prefix('dashboard') ->name('dashboard.') ->middleware(['web', 'auth']) ->group(function () { Route::smartBackup([ 'prefix' => 'backups', 'name' => 'backups.', ]); });
This will register routes like:
GET /dashboard/backups dashboard.backups.index
POST /dashboard/backups dashboard.backups.store
GET /dashboard/backups/{backup}/download dashboard.backups.download
DELETE /dashboard/backups/{backup} dashboard.backups.destroy
DELETE /dashboard/backups dashboard.backups.bulk-destroy
You can use them like this:
route('dashboard.backups.index'); route('dashboard.backups.store'); route('dashboard.backups.download', $backup); route('dashboard.backups.destroy', $backup); route('dashboard.backups.bulk-destroy');
Notes
Use Smart Backup command:
php artisan smart-backup:run
Do not use Spatie directly when testing this package:
php artisan backup:run
because Smart Backup adds runtime configuration, database records, managed folder behavior, and cleanup logic.
Troubleshooting
mysqldump is not recognized
Set the path manually:
SMART_BACKUP_MYSQL_DUMP_BINARY_PATH=C:/xampp/mysql/bin
Then run:
php artisan optimize:clear php artisan smart-backup:run
Mailer [] is not defined
For local testing, add:
MAIL_MAILER=log
Then run:
php artisan optimize:clear
Duplicate migration
If you published the migration twice, delete the extra migration file before running:
php artisan migrate
Author
Karim Mohamed
Package: karim-dev/smart-backup
GitHub: Kareem154
License
MIT