wp-dev-sync
v1.1.1
Published
CLI for syncing WordPress themes to remote servers via SSH or FTP
Maintainers
Readme
The Problem
😩 The Old Way ✨ The WP Dev Sync Way
───────────────────── ─────────────────────
1. Edit theme file locally 1. Edit theme file locally
2. Open FileZilla 2. That's it. It's already
3. Navigate to remote dir on your server.
4. Upload file manually
5. Refresh browser
6. Repeat 500 times a dayNo more FTP clients. No more manual uploads. Save a file locally and it's on your server in seconds.
📦 Install
npm install -g wp-dev-sync# Use without installing
npx wp-dev-sync <command>
# Manual install (clone + link)
git clone https://github.com/reandimo/wp-dev-sync.git
cd wp-dev-sync && npm linkRequirements: Bash (Git Bash on Windows) +
rsync(SSH) orlftp(FTP)
🚀 Quick Start
cd /path/to/my-wordpress-site # 1. Go to your project
wp-dev-sync init # 2. Creates .env config file
nano .env # 3. Set your server credentials
wp-dev-sync setup # 4. Verify everything works
wp-dev-sync watch # 5. Start syncing! 🎉That's it. Every file you save now appears on your server automatically.
📋 Commands
╭──────────────────────────────────────────────────────╮
│ │
│ ⟳ watch Watch + auto-sync on file changes │
│ ↑ push One-time upload to remote │
│ ↓ pull One-time download from remote │
│ ◇ diff Compare local vs remote (dry-run) │
│ ◈ env Manage multiple environments │
│ ★ tunnel Public URL for client previews │
│ ◆ setup Preflight check (deps + connection) │
│ ◇ init Create .env from template │
│ │
╰──────────────────────────────────────────────────────╯⚙️ Configuration
All config lives in a .env file in your project root:
# ── What to sync ─────────────────────────────────────
LOCAL_PATH=./wp-content/themes/my-theme # Local dir
REMOTE_PATH=/var/www/html/wp-content/themes/my-theme # Remote dir
# ── Connection ───────────────────────────────────────
SYNC_PROTOCOL=ssh # ssh or ftp
REMOTE_USER=deploy
REMOTE_HOST=myserver.com
REMOTE_PORT=22 # 22 for SSH, 21 for FTP
REMOTE_PASSWORD= # FTP only
# ── Behavior ─────────────────────────────────────────
SYNC_EXCLUDE=.git,node_modules,.DS_Store,*.log,.env
SYNC_DELETE=false # true = mirror exact state.syncignore
For more control, create a .syncignore file in your project root (or run wp-dev-sync init). One pattern per line, like .gitignore:
# .syncignore
node_modules
vendor
.git
.env
*.log
*.map
public/hot
public/.vite
.idea
.vscodeBoth .syncignore and SYNC_EXCLUDE work together — patterns from both are merged.
Example Setups
LOCAL_PATH=./wp-content/themes/my-theme
REMOTE_PATH=/home/user/public_html/wp-content/themes/my-theme
SYNC_PROTOCOL=ftp
[email protected]
REMOTE_HOST=ftp.domain.com
REMOTE_PORT=21
REMOTE_PASSWORD=your-ftp-passwordLOCAL_PATH=./web/app/themes/my-theme
REMOTE_PATH=/var/www/mysite/current/web/app/themes/my-theme
SYNC_PROTOCOL=ssh
REMOTE_USER=deploy
REMOTE_HOST=myserver.com
REMOTE_PORT=22LOCAL_PATH=./wp-content/themes/my-theme
REMOTE_PATH=/var/www/html/wp-content/themes/my-theme
SYNC_PROTOCOL=ssh
REMOTE_USER=root
REMOTE_HOST=203.0.113.10
REMOTE_PORT=22
SYNC_DELETE=trueLOCAL_PATH=./wp-content/themes/my-theme
REMOTE_PATH=/sites/mysite/wp-content/themes/my-theme
SYNC_PROTOCOL=ssh
REMOTE_USER=mysite
REMOTE_HOST=mysite.ssh.wpengine.net
REMOTE_PORT=22LOCAL_PATH=./wp-content/plugins/my-plugin
REMOTE_PATH=/var/www/html/wp-content/plugins/my-plugin
SYNC_PROTOCOL=ssh
REMOTE_USER=deploy
REMOTE_HOST=myserver.com
REMOTE_PORT=22
SYNC_EXCLUDE=.git,node_modules,tests,vendor🔄 Protocols
┌──────────────────────┬──────────────────────┐
│ SSH (rsync) │ FTP (lftp) │
│ ══════════ │ ═════════ │
│ ✔ Delta transfer │ ✔ Universal access │
│ ✔ Encrypted │ ✔ No server setup │
│ ✔ Passwordless │ ✔ Works everywhere │
│ ✔ ~200 bytes/edit │ ✘ Full file upload │
│ │ ✘ Unencrypted │
│ ★ Recommended │ ○ Fallback option │
└──────────────────────┴──────────────────────┘choco install rsync # Windows
brew install rsync # macOS
sudo apt install rsync # LinuxSSH key setup (one-time):
ssh-keygen -t ed25519
ssh-copy-id -p 22 [email protected]choco install lftp # Windows
brew install lftp # macOS
sudo apt install lftp # Linux👀 Watch Mode
wp-dev-sync watch starts with a smart reconciliation that compares local and remote files before syncing:
╭── info ──────────────────────────────────────────────────╮
│ │
│ The files listed below differ between the local and │
│ remote versions. What would you like to do? │
│ ● assets/style.css │
│ ● templates/page.json │
│ │
╰──────────────────────────────────────────────────────────╯
? Reconciliation Strategy:
▸ Keep the local version
Keep the remote versionEach category gets its own prompt: local-only (upload or delete), remote-only (download or delete), and differing (keep local or keep remote). Then syncs with a gradient progress bar and starts watching for changes.
┌───────────┬──────────────┬───────────┐
│ OS │ Watcher │ Latency │
├───────────┼──────────────┼───────────┤
│ macOS │ fswatch │ ~0.5s │
│ Linux │ inotifywait │ ~0.5s │
│ Windows │ Polling │ ~2s │
└───────────┴──────────────┴───────────┘Pro tip: Pair with Vite HMR for the ultimate WordPress dev experience:
# Terminal 1 # Terminal 2
npm run dev wp-dev-sync watch
# Vite handles CSS/JS HMR # WP Dev Sync handles PHP uploads🚇 Tunnels
Share your staging site with clients via a public URL:
# .env
TUNNEL_TOOL=cloudflared # or: ngrok
TUNNEL_DOMAIN=staging.mysite.comwp-dev-sync tunnel
# → https://random-words.trycloudflare.com🔍 Diff
Compare local and remote without syncing — like git status for your server:
wp-dev-sync diff Local only (1 files) — present locally, missing on remote
+ index.php
Remote only (3 files) — present on remote, missing locally
- .editorconfig
- README.md
- setup.js
Modified (2 files) — differ between local and remote
~ assets/style.css
~ templates/page.json
1 local only 3 remote only 2 modified🌍 Environments
Manage multiple server configs without editing .env manually:
wp-dev-sync env create staging # Create from current .env
wp-dev-sync env create production # Create another
# Edit each: .env.staging, .env.production
wp-dev-sync env switch staging # Activate staging
wp-dev-sync env switch # Interactive picker
wp-dev-sync env list # Show all environments ▸ staging ssh@[email protected]:/themes/my-theme
production ftp@[email protected]:/public_html/themes/my-theme🔥 SYNC_DELETE
| Value | What happens | Safety |
|:------|:-------------|:------:|
| false | Only uploads new/changed files. Never deletes remotely. | ✅ Safe |
| true | Mirrors local state exactly. Remote-only files get deleted. | ⚠️ Careful |
🔧 Troubleshooting
The CLI auto-adds Chocolatey/Scoop paths. If it still fails:
export PATH="/c/ProgramData/chocolatey/bin:$PATH"- Check SSH is enabled on your server
- Try alternative ports:
2222,7822,18765 - SSH hostname may differ from FTP (
ssh.host.comvsftp.host.com)
- Some hosts require full email as username (
[email protected]) - Check IP whitelisting in your hosting panel
- Test with FileZilla first to isolate the issue
- Switch from FTP to SSH if possible (10x faster)
- Exclude large dirs:
SYNC_EXCLUDE=.git,node_modules,vendor,public/fonts - Windows polling (2s) is normal behavior
🏗️ Architecture
wp-dev-sync/
├── bin/wp-dev-sync # CLI entry point
├── lib/
│ ├── _env.sh # .env loader + Windows PATH fix
│ ├── _ui.sh # Terminal UI (colors, banners, spinners)
│ └── _sync.sh # Core sync engine (rsync + lftp)
├── commands/
│ ├── watch.sh # File watcher + auto-sync
│ ├── push.sh # One-time upload
│ ├── pull.sh # One-time download
│ ├── diff.sh # Compare local vs remote
│ ├── env.sh # Multi-environment manager
│ ├── tunnel.sh # Cloudflare / ngrok tunnel
│ └── setup.sh # Preflight dependency check
└── docs/ # Full wiki documentation💡 Not just WordPress
While wp-dev-sync is built for WordPress theme development, it works with any directory you need to sync remotely. Just set LOCAL_PATH and REMOTE_PATH to whatever you need.
# Sync a React app
LOCAL_PATH=./build
REMOTE_PATH=/var/www/html/myapp
# Sync a Jekyll site
LOCAL_PATH=./_site
REMOTE_PATH=/var/www/html/blog