TEAMS & MULTI-SERVER
August 21, 2026

How to Sync Files Between Two Servers

7 min read
Author
CloudStick Team
WordPress Engineer
Share this article
How to Sync Files Between Two Servers
CloudStick
Server
Sync

Rsync Over SSH Is the Standard

Rsync over SSH is the standard way to sync files between two servers, whether it's a one-time push from staging to production or a job that runs on a schedule for months. It transfers only the bytes that changed since the last run instead of re-copying every file, and the whole transfer travels through an encrypted SSH tunnel rather than an unencrypted protocol.

rsync -avz -e ssh /home/user/apps/mysite/ user@server-b:/home/user/apps/mysite/
# -a preserves permissions, ownership, and timestamps
# -z compresses data in transit over the SSH link

The trailing slashes matter more than they look like they should. `/home/user/apps/mysite/` with a trailing slash syncs the contents of that directory into the destination path directly, while dropping the slash syncs the directory itself into the destination, nesting everything one level deeper than intended. Get that detail wrong once on a live server and every path inside the app breaks.

Mirror Mode and the --delete Flag

Adding `--delete` turns a plain rsync into a true mirror: it removes files on the destination that no longer exist on the source, which is exactly what "keep these two servers identical" syncing requires. Without it, files deleted on the source over time just pile up and drift on the destination forever, and the two servers slowly stop matching each other even though the sync job keeps running successfully.

rsync -avzn --delete -e ssh /source/ user@server-b:/dest/
# -n is a dry run: shows exactly what would change, deletes nothing
WARNING

`--delete` is destructive by design, and a reversed source and destination or a typo in the path can wipe out files on the wrong server in seconds. Always run with `-n` first to see the exact list of what would be removed, read that list, and only drop `-n` once it matches what you expect.

Automating Sync With Cron

A cron job is the right way to run a recurring sync instead of remembering to trigger it by hand every day. Once the rsync command is tested and safe, dropping it into crontab with a fixed interval means the destination server never falls far out of date, and every run gets logged so you can check what happened after the fact.

crontab -e
*/15 * * * * rsync -az -e ssh /home/user/apps/mysite/ user@server-b:/home/user/apps/mysite/ >> /var/log/sync.log 2>&1

A 15-minute interval is a reasonable starting point for keeping a failover server's static files current without hammering either box. Tighten it if the servers can handle more frequent runs, or loosen it for large directories where a full changed-file scan every few minutes adds noticeable I/O load.

Real-Time Sync With lsyncd

`lsyncd` triggers a sync the moment a file changes instead of waiting for the next scheduled cron run. It watches the filesystem using inotify and fires off an rsync push as soon as it sees a write, which closes the gap between a change on the source and that change reaching the destination down to seconds rather than minutes.

That kind of near-real-time behavior is worth the extra setup only when a cron interval — even a tight one — is genuinely too slow, such as keeping a hot standby server current for fast failover, or syncing an upload directory that customers are actively writing to. For most staging-to-production or periodic-backup use cases, a cron-scheduled rsync job is simpler to run and easier to reason about than a persistent inotify watcher.

Bandwidth Limits and Database Caveats

A large sync job can saturate a shared network link and starve every other connection on the same server, which is why `--bwlimit` exists. Capping the transfer rate keeps a big sync from turning into an outage for anything else running on that box at the same time.

rsync -avz --bwlimit=5000 -e ssh /source/ user@server-b:/dest/
# caps the transfer to roughly 5000 KB/s
WARNING

Never rsync `/var/lib/mysql` as raw files while MySQL or MariaDB has the database open. A file-level copy can capture tables mid-write and hand you a corrupted, unusable database on the other end. Use `mysqldump` for database data, or set up proper database replication, and keep rsync for static assets, uploads, and application code only.

Syncing Full Sites With CloudStick

CloudStick's Server Transfer feature moves an entire website — files and database together — between two connected servers directly from the dashboard, coordinating the database dump and restore step for you instead of requiring a hand-built rsync-plus-mysqldump pipeline. For teams running staging and production on separate CloudStick servers, that turns a multi-step manual sync into a single guided action.

Role-based team seats also mean the right person can trigger that transfer without needing SSH access to either box, which matters once more than one person is responsible for keeping servers in sync. If your workflow is a plain rsync cron job for static files and a hot standby that needs to stay current, that setup still works fine and costs nothing extra to run. When the job involves moving a full site with its database between two servers on a recurring basis, that's the point where a guided transfer beats a hand-maintained script — one less pipeline to babysit, and one less place for a stray `--delete` to do damage.

Leave a comment
Full Name
Email Address
Message
Contents