jorisnoo / craft-remote-sync
Sync databases and storage files between remote and local Craft CMS environments
Requires
- php: ^8.2
- craftcms/cms: ^5.0
- laravel/prompts: ^0.3
- symfony/process: ^6.4 || ^7.0
Requires (Dev)
- mockery/mockery: ^1.6
- pestphp/pest: ^2.0
README
A Craft CMS module that syncs databases and storage files between remote and local environments over SSH and rsync.
Features
- Pull a remote database and storage files to your local environment
- Push your local database and files to a remote (disabled per remote by default)
- A plan preview before anything is changed, with a
--dry-runthat stops after it - Backups taken automatically before either side's database is overwritten
- A single SSH round-trip to detect the atomic layout, a compatible PHP binary, and rsync
- File deletion is opt-in: the preview shows what
--deletewould remove, but nothing is deleted unless you ask doctorto check prerequisites andpruneto clean up old snapshots- Support for multiple named remotes and atomic deployments (
current/symlink)
Requirements
| Version | |
|---|---|
| Craft CMS | 5.x |
| PHP | 8.2+ |
| Local tools | ssh, rsync, and the database client/dump tools (mysql+mysqldump or psql+pg_dump) |
| Remote tools | ssh, rsync, and a PHP binary that satisfies the project. If the default php does not, expose a versioned binary such as php8.4 on PATH and it is detected automatically, or pin one with php_binary. |
Run craft remote-sync/doctor to check everything is in place.
Installation
composer require jorisnoo/craft-remote-sync
Register it as a module in config/app.php:
return [ 'modules' => [ 'remote-sync' => \Noo\CraftRemoteSync\Module::class, ], 'bootstrap' => ['remote-sync'], ];
The bootstrap entry is required so the module registers its console controllers.
Configuration
Copy the config file into your project and edit it:
cp vendor/jorisnoo/craft-remote-sync/src/config.php config/remote-sync.php
<?php use craft\helpers\App; return [ 'remotes' => [ 'production' => [ 'host' => App::env('REMOTE_SYNC_PRODUCTION_HOST'), 'path' => App::env('REMOTE_SYNC_PRODUCTION_PATH'), 'push' => (bool) App::env('REMOTE_SYNC_PRODUCTION_PUSH'), // 'php_binary' => '/usr/bin/php8.4', ], ], 'default' => App::env('REMOTE_SYNC_DEFAULT'), 'paths' => ['app'], 'exclude_paths' => ['craft-transforms'], 'allow_production' => false, 'timeouts' => [ 'remote' => 300, 'transfer' => 1800, ], ];
Each remote needs a host (an SSH connection string, optionally user@host:port) and a path (the Craft app root on the server). For an atomic deployment, point path at the release parent; a /current symlink is detected and used automatically. Pushing is refused unless push is true.
The env var names follow REMOTE_SYNC_{REMOTE}_{KEY}, so a staging remote reads REMOTE_SYNC_STAGING_HOST and REMOTE_SYNC_STAGING_PATH.
REMOTE_SYNC_PRODUCTION_HOST=deploy@example.com REMOTE_SYNC_PRODUCTION_PATH=/var/www/html REMOTE_SYNC_PRODUCTION_PUSH=false REMOTE_SYNC_DEFAULT=production
Usage
Pull (remote → local)
craft remote-sync/pull [remote] [--database] [--files] [--no-backup] [--discard-snapshot] [--delete] [--path=app,uploads] [--dry-run] [--force]
Without flags it asks which remote and what to sync, verifies the host key, probes the remote, prints the plan, and asks for confirmation. For the database it backs up your local database (pre-pull-*), takes a snapshot on the remote, downloads and restores it, then runs craft up and clears caches. For files it rsyncs each configured path.
Push (local → remote)
craft remote-sync/push [remote] [--database] [--files] [--no-backup] [--delete] [--path=app,uploads] [--dry-run] [--force]
Push requires the remote to have push enabled, always asks for a typed yes confirmation, and needs an explicit scope when run non-interactively. It backs up the remote database (pre-push-*) before uploading and restoring your local one, then runs craft up on the remote.
Doctor
craft remote-sync/doctor [remote]
Checks local tools and, per remote, the host key, application path, PHP binary, and rsync.
Prune
craft remote-sync/prune [remote] [--local] [--remote] [--keep=5] [--all] [--dry-run] [--force]
Deletes old snapshots created by this module, keeping the newest --keep. It leaves snapshots it did not create alone unless --all is given.
Backups and snapshots
Two kinds of database file are involved:
- A transfer snapshot (
remote-sync-*) is the data being moved. The copy on the remote is always cleaned up, even if you interrupt with Ctrl+C. On a pull, the copy downloaded to your machine is kept. - A backup (
pre-pull-*locally,pre-push-*on the remote) is a safety copy taken before a side is overwritten. It is kept, so you can roll back withcraft db/restore.
Both live in Craft's standard storage/backups directory. Skip the safety backup with --no-backup, and delete the downloaded snapshot once it has been imported with --discard-snapshot. Kept snapshots are subject to remote-sync/prune, which keeps the newest five by default.
A push cleans up both copies of its transfer snapshot, local and remote. Only a pull leaves one behind.
A pull or push refuses to run when the local environment is production, unless you set allow_production to true.
Versioning
This module is maintained on separate branches for each major Craft CMS version:
| Branch | Craft CMS | PHP |
|---|---|---|
main |
5.x | 8.2+ |
craft-4 |
4.x | 8.1+ |
# Craft 5 composer require jorisnoo/craft-remote-sync # Craft 4 composer require jorisnoo/craft-remote-sync:dev-craft-4
Upgrading from 2.x? See UPGRADING.md for the config and command changes.
License
MIT — see LICENSE for details.