Rsync is a lightweight, proven tool for reliable file replication, migration, and backup. It is also a Linux staple.
Unfortunately, VMware never offered rsync in ESXi, perhaps in favour of commercial backup solutions. This project reinstates that fundamental Linux functionality with a clean and ESXi-compatible static rsync build.
💡 If you don’t want to build your own, you can download a prebuilt binary here:
➡️ Latest version: rsync v3.4.1 for ESXi
➡️ For older ESXi6: rsync v3.2.7 for ESXi
Any RedHat flavored OS that offers ALL the following packages: curl python3-pip automake perl gcc glibc-static
.
As of Sept '25 the list of RedHat derived OS with these packages includes:
Alama Linux 8,9,10
Amazon Linux 2 & 2023
Centos 9
Centos 10
Fedora 41 & 42
OpenSuse 15.6 & Tumbleweed
Oracle Linux 8,9,10
Rocky Linux 8,9,10
-
On a fresh supported system, run the build script (not as sudo or root):
./rsync-esxi-builder-multiOS.sh
-
When the script completes, copy the compiled
rsync
binary from$HOME/build-static/rsync/bin
to all ESXi hosts (note the install path of each - you will need this later). -
On each ESXi host, set execute permissions on the binary:
chmod 755 /path/to/rsync
-
ESXi 8 and above only – allow execution of non-native binaries:
esxcli system settings advanced set -o /User/execInstalledOnly -i 0
-
Configure RSA SSH keys for passwordless host-to-host authentication. (VMware does not currently support Ed25519 keys for Esxi host to host sessions.)
-
See below usage Examples for host-to-host and datastore-to-datastore rsync replication.
rsync -rltDv --progress --sparse --partial --append-verify /vmfs/source_path/* /vmfs/destination_path
# =============================================================================================================
#!/bin/sh
# ESXi host to host network backup with rsync
# Run this script on the SOURCE ESXi host
# =============================================================================================================
# Paths & hosts
SOURCE_DIR="/vmfs/volumes/Host1SourceDatastore/" # Source location SOURCE esxi host
DEST_DIR="/vmfs/volumes/Host2DestDatastore/" # Destination location on DEST host
ESXI_DEST_HOST="[email protected]" # Destination Esxi host
PRIVKEY="/vmfs/volumes/Host1SourceDatastore/privkey" # SSH private sshkey stored on SOURCE to access DEST ESXi host
SOURCE_RSYNC_BIN="/vmfs/volumes/Host1SourceDatastore/rsync" # SORCE rsync binary location
DEST_RSYNC_BIN="/vmfs/volumes/Host2DestDatastore/rsync" # DEST rsync binary location
EXCLUDE_FILE="/vmfs/volumes/Datastore1/rsync_excludes.txt" # Optional filter, one entry per line
LOG_DIR="/vmfs/volumes/Datastore1/rsync_logs" # SOURCE host log location
LOG_FILE="${LOG_DIR}/rsync_$(date '+%Y%m%d_%H%M%S').log" # Log file name and format
# Preliminary checks
[ -x "$SOURCE_RSYNC_BIN" ] || { echo && echo "Source rsync not found: $SOURCE_RSYNC_BIN" && echo; exit 1; }
[ -f "$PRIVKEY" ] || { echo && echo "SSH private key not found: $PRIVKEY" && echo; exit 1; }
[ -f "$EXCLUDE_FILE" ] && RSYNC_EXCLUDES="--exclude-from=${EXCLUDE_FILE}" && echo && echo "Using exclude file: ${EXCLUDE_FILE}" | tee -a "$LOG_FILE"
mkdir -p "${LOG_DIR}" || { echo && echo "Failed to create log directory ${LOG_DIR}" && echo; exit 1; }
# Ensure remote directory exists
ssh -i "${PRIVKEY}" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR \
"${ESXI_DEST_HOST}" "mkdir -p \"${DEST_DIR}\"" || {
echo "Failed to create destination directory on remote host" | tee -a "${LOG_FILE}"
exit 1
}
# Cleanup any lingering rsync processes on error or interrupt (local + remote) ---
cleanup() {
echo "Cleaning up rsync processes (local + remote)..." | tee -a "$LOG_FILE"
# Kill local rsync
for pid in $(ps | grep '[r]sync' | awk '{print $1}'); do
echo "Killing local rsync PID $pid" | tee -a "$LOG_FILE"
kill -9 "$pid" 2>/dev/null
done
# Kill remote rsync
ssh -i "$PRIVKEY" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR "$ESXI_DEST_HOST" "
for pid in \$(ps | grep '[r]sync' | awk '{print \$1}'); do
echo \"Killing remote rsync PID \$pid\"
kill -9 \"\$pid\" 2>/dev/null
done
"
echo
exit 1
}
trap cleanup INT TERM
# Perform the rsync copy operation
echo
echo "Starting rsync copy from ${SOURCE_DIR} to ${ESXI_DEST_HOST}:${DEST_DIR} on $(date)" | tee -a "${LOG_FILE}"
echo
{
"${SOURCE_RSYNC_BIN}" -rltDv --progress --sparse --partial --append-verify \
${RSYNC_EXCLUDES} \
-e "ssh -i \"${PRIVKEY}\" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR" \
--rsync-path="${DEST_RSYNC_BIN}" \
"${SOURCE_DIR}" "${ESXI_DEST_HOST}:${DEST_DIR}"
echo $? > /tmp/rsync_exit.$$
} 2>&1 | tee -a "${LOG_FILE}"
RSYNC_EXIT=$?
cleanup
if [ "$RSYNC_EXIT" -ne 0 ]; then
cleanup
echo
echo "Rsync failed with exit code $RSYNC_EXIT" | tee -a "${LOG_FILE}"
echo
exit "$RSYNC_EXIT"
fi
cleanup
echo
echo "Rsync copy completed successfully at $(date)" | tee -a "${LOG_FILE}"
echo
exit 0