Sync database and files from remote Laravel hosts using laravel-db-snapshots and rsync
jorisnoo/laravel-remote-sync is a Laravel package for sync database and files from remote laravel hosts using laravel-db-snapshots and rsync.
It currently has 0 GitHub stars and 1.524 downloads on Packagist (latest version 0.7.1).
Install it with composer require jorisnoo/laravel-remote-sync.
Discover more Laravel packages by jorisnoo
or browse all Laravel packages to compare alternatives.
Last updated
Pull a remote Laravel app's database and storage files to your local machine, or push them the other way. Database dumps use spatie/laravel-db-snapshots on the remote; file transfers use rsync over SSH.
Locally:
mysql or pgsql database (imports pipe through the mysql/psql CLI)ssh, rsync, and gzip on your PATHOn each remote:
spatie/laravel-db-snapshots and laravel/tinker installedrsyncphp is tried first, then versioned binaries (php8.5, php8.4, ...). You can also pin one with php_binary in the config.The remote's snapshot location, database driver, and deployment layout are discovered automatically on every run - the remote's own configuration is what counts, so local and remote snapshot disks do not need to match.
composer require jorisnoo/laravel-remote-sync
php artisan vendor:publish --tag="remote-sync-config"
Configure a snapshots disk for spatie/laravel-db-snapshots in config/filesystems.php (on both sides, but they may differ):
'disks' => [
'snapshots' => [
'driver' => 'local',
'root' => database_path('snapshots'),
],
],
Then point your .env at the remote:
[email protected]
REMOTE_SYNC_PRODUCTION_PATH=/home/forge/your-app.com
Verify the whole setup before the first sync:
php artisan remote-sync:doctor
Doctor checks your local binaries and, for each configured remote, the SSH host key, application path, PHP binary, installed packages, rsync, and driver compatibility.
The published config/remote-sync.php:
return [
'remotes' => [
'production' => [
'host' => env('REMOTE_SYNC_PRODUCTION_HOST'),
'path' => env('REMOTE_SYNC_PRODUCTION_PATH'),
'push' => (bool) env('REMOTE_SYNC_PRODUCTION_PUSH', false),
// 'php_binary' => '/usr/bin/php8.4',
],
],
// Used when several remotes are configured and none is passed.
'default' => env('REMOTE_SYNC_DEFAULT'),
// Storage-relative paths synced by the files scope.
'paths' => ['app'],
// Extra rsync excludes. Dotfiles and snapshot directories are always excluded.
'exclude_paths' => [],
// Synced as empty tables on pull, preserved on push. The migrations
// table is always synced and `migrate --force` runs after every import.
'exclude_tables' => ['cache', 'sessions', 'jobs', /* ... */],
// false, or emails / *-wildcards of users to KEEP locally after a pull.
'filter_users' => false,
// Pull and push refuse to run when app.env is production unless this is true.
'allow_production' => env('REMOTE_SYNC_ALLOW_PRODUCTION', false),
'timeouts' => [
'remote' => 300, // short remote commands
'transfer' => 1800, // dumps, imports, rsync transfers
],
];
Add more remotes by repeating the pattern - the env var names follow the remote's key (staging reads REMOTE_SYNC_STAGING_HOST, and so on). Remotes with missing or placeholder values are rejected with a message naming the exact env var to set.
php artisan remote-sync:pull
Interactively this asks at most three things: which remote (only when several are configured), what to pull (database, files, or both), and one final confirmation - after showing you a plan of exactly what will happen: which tables are imported and truncated, which files are transferred, what a --delete pass would remove, and which backup is created.
Everything else is a flag:
| Flag | Effect |
| --- | --- |
| --database / --files | Select the scope (skips the prompt; neither means both) |
| --full | Import all tables, dropping local tables first |
| --no-backup | Skip the local pre-pull-* backup snapshot |
| --keep-snapshot | Keep the downloaded snapshot file after import |
| --delete | Delete local files that no longer exist on the remote |
| --path=app/public | Sync only specific storage-relative paths (repeatable) |
| --dry-run | Print the plan and exit without changing anything |
| --force / -f | Answer the final confirmation with yes |
A standard pull creates a local backup, dumps the remote database (minus exclude_tables), downloads and integrity-checks the snapshot, imports it through the mysql/psql CLI, truncates the excluded tables, then runs migrate --force and optimize:clear. If the import fails, the downloaded snapshot is kept and the exact restore command for your backup is printed.
Deletion is strictly opt-in: --force never implies --delete, and the plan preview tells you how many local-only files a --delete pass would remove before you commit to anything.
For a cron or CI refresh:
php artisan remote-sync:pull production --database --files --force
Without --force, non-interactive runs print the plan and exit with an error, so a misconfigured cron line cannot sync anything by accident.
Pushing overwrites data on the remote, so it is stricter:
php artisan remote-sync:push staging --database
'push' => true in its config.--database and/or --files).yes.A database push backs up the remote first (pre-push-*), uploads a local snapshot, loads it on the remote without dropping tables (excluded tables are preserved), and runs remote migrations. If the load fails, the restore command for the remote backup is printed. Flags mirror pull: --no-backup, --delete, --path=, --dry-run, --force.
Transfer snapshots are removed automatically after each run, but backups (pre-pull-*, pre-push-*) accumulate:
php artisan remote-sync:prune # local + remote, keep the 5 newest
php artisan remote-sync:prune --local --keep=3
php artisan remote-sync:prune production --remote --dry-run
Prune only touches snapshots created by this package (remote-sync-*, pre-pull-*, pre-push-*). Pass --all to include your own spatie snapshots as well.
REMOTE_SYNC_ALLOW_PRODUCTION=true in your .env only when that is intentional (the confirmation then requires a typed yes).--dry-run everywhere.filter_users refuses to act when its patterns would delete every user.mysql/psql via the MYSQL_PWD/PGPASSWORD environment variables, never as command-line arguments.composer test
The MIT License (MIT). Please see License File for more information.
Built with Claude Code.
See RELEASING.md for versioning, changelog entries and the release command.