lsyncd Examples: Real-Time Directory Sync on RHEL/CentOS Linux

On RHEL and CentOS, lsyncd watches local directories with inotify and triggers rsync (or rsync over SSH) to mirror changes to another path or host. It is a lightweight live-sync daemon for slowly changing trees.

Published

Updated

Read time 10 min read

Reviewed byDeepak Prasad

lsyncd Examples: Real-Time Directory Sync on RHEL/CentOS Linux
About On RHEL and CentOS, lsyncd watches local directories with inotify and triggers rsync (or rsync over SSH) to mirror changes to another path or host. It is a lightweight live-sync daemon for slowly changing trees.
Tested on RHEL 9.4; lsyncd 2.3.1
Man page lsyncd(8)
Privilege root / sudo
Distros

RHEL, CentOS, Rocky Linux, AlmaLinux (EPEL package). Common on enterprise mirror and DR setups.

One-shot or cron sync without a daemon: rsync.

Related guide

lsyncd — quick reference

One-shot CLI (testing)

Quick rsync-over-inotify tests without a config file — good for lab validation.

When to use Command
Show lsyncd version lsyncd --version
Sync two local dirs (daemon mode) lsyncd -rsync /source/ /target/
Foreground mode with live log lines lsyncd -nodaemon -rsync /source/ /target/
Sync to remote host via rsync lsyncd -nodaemon -rsync /source/ host:/target/
Remote sync with SSH for moves/deletes lsyncd -nodaemon -rsyncssh /source host /target/

Trailing slashes on source matter — /source/ syncs contents; /source may sync the directory node differently.

Global settings (/etc/lsyncd.conf)

When to use Setting
Log file path logfile = "/var/log/lsyncd/lsyncd.log"
Periodic status snapshot statusFile = "/var/log/lsyncd-status.log"
Status write interval (seconds) statusInterval = 20
Stay in foreground (debug) nodaemon = true
Max open inotify watches maxProcesses = 1 (default varies)

Sync blocks — local

When to use Directive
Local mirror with rsync at startup, cp/rm/mv live default.direct
Local mirror using rsync for all events default.rsync
Source directory (absolute path) source = "/source"
Target directory (absolute path) target = "/target"
Batch delay before sync (seconds) delay = 10
Do not delete extra files on target delete = false
Exclude patterns inline exclude = { 'file*', '*tmp' }
Exclude from file (rsync-style) excludeFrom = "/etc/lsyncd.exclude"

Sync blocks — remote

When to use Directive
Remote rsync target target = "host:/path" under default.rsync
rsync + SSH for renames on remote default.rsyncssh with host= and targetdir=
Enable compression in rsync rsync = { archive = true, compress = true }
Custom rsync binary or wrapper script rsync = { binary = "/path/to/wrapper.sh" }

Service management

When to use Command
Validate config and start sudo systemctl restart lsyncd
Check service status sudo systemctl status lsyncd
Enable at boot sudo systemctl enable lsyncd
Tail sync log sudo less /var/log/lsyncd/lsyncd.log

Install on RHEL-family systems: sudo dnf install lsyncd (requires EPEL on many releases).


Package installs and updates in this section use dnf command.

lsyncd — command syntax

One-liner synopsis from lsyncd(1):

text
lsyncd [OPTIONS] -rsync SOURCEDIR TARGET ...
lsyncd [OPTIONS] -rsyncssh SOURCEDIR TARGETHOST TARGETDIR ...
lsyncd [OPTIONS] CONFIGFILE

Config file default: /etc/lsyncd.conf (Lua syntax). lsyncd uses inotify to watch source, collates events for delay seconds, then spawns rsync (or local cp/rm/mv with default.direct).


lsyncd — command examples

Essential Check version and run a local one-liner

Confirm lsyncd is installed, then mirror /source/ to /target/ in the foreground so log lines print to the terminal.

Run the commands:

bash
lsyncd --version
sudo mkdir -p /source /target
sudo lsyncd -nodaemon -rsync /source/ /target/

In another terminal, create a file:

bash
sudo touch /source/demo.txt

Sample output:

text
Version: 2.2.2
20:36:36 Normal: --- Startup ---
20:36:36 Normal: recursive startup rsync: /source/ -> /target/
20:36:36 Normal: Startup of /source/ -> /target/ finished.
20:37:21 Normal: Calling rsync with filter-list of new/modified files/dirs
/demo.txt
/
20:37:21 Normal: Finished a list after exitcode: 0

Press Ctrl+C to stop foreground mode. Without -nodaemon, lsyncd backgrounds itself and logs to syslog or logfile.

Essential Minimal /etc/lsyncd.conf for local rsync

Production setups use a config file with logging and a sync block — this pattern matches most RHEL/CentOS guides.

Create /etc/lsyncd.conf:

Enable or disable the unit at boot with systemctl enable; the systemctl command documents enable --now, symlinks under /etc/systemd/system, and masks.

bash
sudo tee /etc/lsyncd.conf <<'EOF'
settings {
    logfile    = "/var/log/lsyncd/lsyncd.log",
    statusFile = "/var/log/lsyncd-status.log",
    statusInterval = 20,
    nodaemon   = false,
}

sync {
    default.rsync,
    source = "/source",
    target = "/target",
    delay  = 10,
}
EOF
sudo mkdir -p /var/log/lsyncd
sudo systemctl enable --now lsyncd

Verify sync after writing a file:

bash
sudo touch /source/file1
sleep 12
ls /target/
sudo tail -5 /var/log/lsyncd/lsyncd.log

Sample output:

text
file1
Sun Jan 19 16:24:35 2020 Normal: Calling rsync with filter-list of new/modified files/dirs
/file1
/
Sun Jan 19 16:24:35 2020 Normal: Finished a list after exitcode: 0

The delay value batches rapid saves before rsync runs.

Common Sync to a remote host with default.rsync

Push changes to a remote directory over rsync — SSH keys or ssh-agent must work non-interactively for the lsyncd user.

Config excerpt:

lua
sync {
    default.rsync,
    source = "/source",
    target = "backup-host:/target",
    delay  = 10,
}

Restart and check logs:

bash
sudo systemctl restart lsyncd
sudo tail -10 /var/log/lsyncd/lsyncd.log

Sample output:

text
Normal: --- Startup ---
Normal: recursive startup rsync: /source/ -> backup-host:/target/
Normal: Startup of /source/ -> backup-host:/target/ finished.

Ensure backup-host resolves (DNS or /etc/hosts) and that passwordless SSH/rsync works as root or the service user.

Common Use rsyncssh for efficient remote renames

default.rsyncssh runs rsync for bulk sync but uses SSH mv/rm locally on the remote side for renames — faster than re-transferring large files.

Config excerpt:

lua
sync {
    default.rsyncssh,
    source    = "/source",
    host      = "backup-host",
    targetdir = "/target",
    delay     = 10,
}

Note: host and targetdir are separate — unlike default.rsync, which uses a single target = "host:/path" string.

Sample log line after startup:

text
Normal: recursive startup rsync: /source/ -> backup-host:/target/
Normal: Startup of "/source/" finished: 0
Common Exclude patterns and control deletes

Skip transient files and optionally keep extra files on the target when sources are deleted.

Config excerpt:

lua
sync {
    default.direct,
    source = "/source",
    target = "/target1",
    delay  = 10,
    exclude = { 'file*', '*tmp' },
    delete  = false,
}

Restart and inspect startup log:

bash
sudo systemctl restart lsyncd
sudo grep -A2 'excluding' /var/log/lsyncd/lsyncd.log

Sample output:

text
Normal: recursive startup rsync: /source/ -> /target1/ excluding
file*
*tmp
delete value Behaviour
true (default) Remove target files not in source
false Never delete on target
'startup' Delete extras only at daemon start
'running' Delete during runtime, not at startup
Advanced Mirror one source to several targets

Replicate the same tree to local and remote destinations with separate sync blocks.

Config excerpt:

lua
sync {
    default.rsync,
    source = "/source",
    target = "/target1",
    delay  = 10,
}

sync {
    default.rsync,
    source = "/source",
    target = "backup-host:/target",
    delay  = 10,
}

sync {
    default.rsync,
    source = "/source",
    target = "/target2",
    delay  = 10,
}

After restart, logs show each startup rsync:

text
Normal: Startup of /source/ -> /target1/ finished.
Normal: Startup of /source/ -> /target2/ finished.
Normal: Startup of /source/ -> backup-host:/target/ finished.

Alternatively, use a Lua for loop over a targets table when many destinations share identical options.

Advanced Enable archive and compression

Tune rsync flags inside the sync block for WAN links or large static trees.

Config excerpt:

lua
sync {
    default.rsync,
    source = "/source",
    target = "/target",
    delay  = 10,
    rsync  = {
        archive  = true,
        compress = true,
    },
}

This maps to rsync -a and -z behaviour during each triggered sync. Check resulting rsync command lines in the log when debugging.

Advanced Run a script after successful rsync

Wrap rsync with a custom script to alert monitoring when a sync completes — the wrapper must exec rsync and preserve exit codes.

Example wrapper /usr/local/bin/rsync-notify.sh:

bash
#!/bin/bash
/usr/bin/rsync "$@"
result=$?
if [ $result -eq 0 ]; then
  echo "$(date): Completed sync" >> /var/log/lsyncd-sync.ok
fi
exit $result

Reference it in config:

lua
rsync = {
    binary = "/usr/local/bin/rsync-notify.sh",
}

Sample line in /var/log/lsyncd-sync.ok:

text
Wed Jul  1 14:05:01 IST 2026: Completed sync
Advanced Troubleshoot a failing lsyncd service

When sync stops, check systemd, config syntax, and whether rsync/SSH works manually.

Run the commands:

bash
sudo systemctl status lsyncd
sudo lsyncd -nodaemon /etc/lsyncd.conf
sudo rsync -av /source/ /target/
sudo journalctl -u lsyncd --no-pager -n 30

Sample healthy status excerpt:

text
● lsyncd.service - Live Syncing (Mirror) Daemon
     Active: active (running)

Common fixes: absolute paths in source/target, SELinux context on /source, SSH StrictHostKeyChecking for rsyncssh, and typos in Lua commas/braces.


lsyncd — when to use / when not

Use lsyncd whenUse something else when
  • You need near-real-time mirror of a slowly changing directory tree
  • Source and target are on RHEL/CentOS (or similar) and rsync is acceptable
  • You want a lightweight daemon instead of clustered storage or block replication
  • One-way push from a secure zone to a less secure mirror is enough
  • You only need periodic copies — [cron + rsync](/rsync-command-in-linux/) is simpler
  • You need bidirectional sync or conflict handling — consider Syncthing, Gluster, or DRBD
  • Database files change constantly — use DB-native replication, not file sync
  • Zero third-party daemon on Ubuntu is required — lsyncd is EPEL/RHEL-centric; build from source or use alternatives

lsyncd vs rsync alone

lsyncd rsync (cron/manual)
Trigger inotify events + delay batch Schedule or manual
Latency Seconds (configurable delay) Minutes to hours
Daemon Yes No
Remote moves rsyncssh optimizes renames Full re-copy unless --delete
Complexity Lua config + service Single command in crontab

lsyncd calls rsync under the hood for most sync modes — test rsync/SSH alone before debugging lsyncd.


File sync and remote access nearby.

Command One line
lsyncd Live directory mirror daemon (this page)
ssh Remote shell rsyncssh relies on

Browse the full index in our Linux commands reference.


lsyncd — interview corner

What is lsyncd and how does it work?

lsyncd (Live Syncing Daemon) watches directories with inotify, waits a short delay to batch events, then spawns rsync (or local cp/rm/mv with default.direct) to mirror changes to another path or host.

It targets slowly changing trees — config directories, web roots, incremental DR mirrors — not live database files.

A strong answer is:

"lsyncd uses inotify to watch a source tree, batches changes for a few seconds, then runs rsync to push updates to a local or remote target. It's a lightweight live mirror, not a cluster filesystem."

When do you use default.rsync vs default.rsyncssh?
Mode Target syntax Best for
default.rsync target = "host:/path" Simple remote push; rsync over SSH
default.rsyncssh host= + targetdir= Remote renames/moves via SSH without full re-upload

Use rsyncssh when files are renamed often on the source and the remote is SSH-accessible — lsyncd issues remote mv/rm for efficiency.

A strong answer is:

"default.rsync uses a single host:/path target. default.rsyncssh splits host and targetdir and uses SSH for remote file ops — better when renames would otherwise force full re-transfers."

What does delete = false mean in lsyncd?

By default lsyncd deletes files on the target that were removed from the source — true mirror behaviour. delete = false keeps extra files on the target even when they disappear from the source.

Other values:

  • 'startup' — prune extras only when the daemon starts
  • 'running' — prune during runtime but not at startup

Pick false when the target is a backup you never want automatically trimmed.

A strong answer is:

"delete true mirrors removals to the target. false keeps orphan files on the target — I use that for one-way backups where the destination accumulates history."

Why does lsyncd use a delay setting?

delay (seconds) collapses many rapid inotify events (save, temp file, rename) into one rsync call. Without it, editing a large tree could spawn rsync storms.

Trade-off: higher delay = fewer rsync runs but more latency before the target catches up.

A strong answer is:

"delay batches inotify events before rsync runs — reduces load when editors write many files quickly. I tune it between a few seconds for low latency and longer for busy trees."

How do you install lsyncd on RHEL?

lsyncd is commonly packaged in EPEL, not always in base RHEL repos:

bash
sudo dnf install epel-release
sudo dnf install lsyncd
sudo systemctl enable --now lsyncd

Config lives in /etc/lsyncd.conf (Lua). Validate with lsyncd -nodaemon /etc/lsyncd.conf before enabling production sync.

A strong answer is:

"On RHEL I enable EPEL and install the lsyncd RPM, configure /etc/lsyncd.conf, test with -nodaemon, then enable the systemd unit."


Troubleshooting

Symptom Likely cause What to try
Service fails at start Lua syntax error in config lsyncd -nodaemon /etc/lsyncd.conf
Files never appear on target Relative paths, wrong trailing slash Use absolute paths; match /source/ vs /source
Remote sync silent failure SSH host key or auth Manual rsync -av /source/ host:/target/ as service user
inotify limit exceeded Many watches Raise fs.inotify.max_user_watches sysctl
Target deletes unexpected files delete = true default Set delete = false if backup must retain old files
High CPU delay too low on busy tree Increase delay; use default.direct locally

Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …