How to save and archive files in real time

Monitor a local directory for changes and automatically archive every new or modified file to a remote server — with zero manual intervention.

inotify-toolsrsyncsystemdDebian · Ubuntu · RHELBash

Table of contents

  1. Prerequisites
  2. The rsync script
  3. The inotify watcher script
  4. systemd service unit
  5. Managing the service
  6. Logs & monitoring
  7. File summary

01 Prerequisites

Install the required packages on the source machine (the one being watched):

terminalbash

# Debian / Ubuntu
sudo apt update && sudo apt install -y inotify-tools rsync

# RHEL / CentOS / Fedora
sudo dnf install -y inotify-tools rsync

🔑rsync transfers files over SSH. You’ll need passwordless SSH key authentication between the source machine and the archive server so the service can run unattended. See my dedicated article on SSH key setup for the full walkthrough.

02 The rsync script

This script does the actual file transfer. It will be called by the inotify watcher every time a change is detected. Create it at /usr/local/bin/sync-to-archive.sh:

/usr/local/bin/sync-to-archive.shbash

#!/usr/bin/env bash
# ================================================================
#  sync-to-archive.sh
#  Syncs SOURCE_DIR to DEST_SERVER:DEST_DIR via rsync over SSH.
#  Called by the inotify watcher on every detected event.
# ================================================================

# ── Configuration (edit these) ──────────────────────────────────
SOURCE_DIR="/path/to/watched/directory"
DEST_USER="user"
DEST_SERVER="archive-server"
DEST_DIR="/path/to/archive"
SSH_KEY="/home/$DEST_USER/.ssh/archive_key"
LOG_FILE="/var/log/rsync-archive.log"
# ────────────────────────────────────────────────────────────────

TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

echo "[$TIMESTAMP] Starting sync..." >> "$LOG_FILE"

rsync -rvaz \
    -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" \
    "$SOURCE_DIR/" \
    "$DEST_USER@$DEST_SERVER:$DEST_DIR/" >> "$LOG_FILE" 2>&1

EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
    echo "[$TIMESTAMP] Sync successful." >> "$LOG_FILE"
else
    echo "[$TIMESTAMP] ERROR: rsync exited with code $EXIT_CODE." >> "$LOG_FILE"
fi

exit $EXIT_CODE

What do the rsync flags do?

FlagMeaning
-rRecursive — process all subdirectories.
-vVerbose — log every transferred file.
-aArchive mode — preserves permissions, timestamps, symlinks, and ownership.
-zCompress data in transit to reduce bandwidth usage.

Make it executable and run a quick manual test:

terminalbash

sudo chmod +x /usr/local/bin/sync-to-archive.sh

# Manual test — check the log afterwards
sudo /usr/local/bin/sync-to-archive.sh
cat /var/log/rsync-archive.log

03 The inotify watcher script

This script runs in a loop, watching your directory with inotifywait and triggering the rsync script on every relevant filesystem event. Create it at /usr/local/bin/watch-directory.sh:

/usr/local/bin/watch-directory.shbash

#!/usr/bin/env bash
# ================================================================
#  watch-directory.sh
#  Watches SOURCE_DIR with inotifywait and triggers rsync on
#  every file creation, modification, move, or deletion.
# ================================================================

SOURCE_DIR="/path/to/watched/directory"
SYNC_SCRIPT="/usr/local/bin/sync-to-archive.sh"
LOG_FILE="/var/log/inotify-watch.log"

echo "[$(date '+%Y-%m-%d %H:%M:%S')] Watcher started: $SOURCE_DIR" >> "$LOG_FILE"

inotifywait -m -r \
    -e close_write -e create -e delete -e moved_to \
    --format '%T %w%f %e' --timefmt '%Y-%m-%d %H:%M:%S' \
    "$SOURCE_DIR" 2>> "$LOG_FILE" | \
while read -r DATE TIME FILEPATH EVENT; do
    echo "[$DATE $TIME] Event: $EVENT on $FILEPATH" >> "$LOG_FILE"
    # Run rsync in the background so the loop isn't blocked
    bash "$SYNC_SCRIPT" &
done

Watched inotify events

EventTriggers when…
close_writeA file is closed after being written. More reliable than IN_MODIFY because it fires once the write is complete.
createA new file or directory is created.
deleteA file or directory is removed.
moved_toA file is moved into the watched directory (e.g. via mv or a rename).

💡rsync is launched in the background with & so the watcher loop is never blocked. If many files change at once, multiple rsync processes may run concurrently — that’s fine for most workloads. For high-frequency writes you may want to add a small debounce delay (sleep 2) before calling rsync.

terminalbash

sudo chmod +x /usr/local/bin/watch-directory.sh

04 systemd service unit

Wrapping the watcher in a systemd unit lets you enable/disable it at will, have it start automatically on boot, and benefit from automatic restarts on failure. Create the file below:

/etc/systemd/system/inotify-rsync.serviceini

[Unit]
Description=inotify directory watcher + rsync archive sync
Documentation=man:inotifywait(1) man:rsync(1)
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/watch-directory.sh
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=inotify-rsync

# Optional hardening — uncomment if running as a dedicated user
# User=archive-user
# Group=archive-group
# NoNewPrivileges=true

[Install]
WantedBy=multi-user.target
DirectivePurpose
Type=simplesystemd manages the main process started by ExecStart directly.
Restart=on-failureAutomatically restarts the watcher if it exits with a non-zero code.
RestartSec=10Waits 10 seconds before each restart attempt.
After=network-online.targetEnsures the network is available before rsync tries to connect.
SyslogIdentifierTags all log lines with inotify-rsync for easy filtering with journalctl.

05 Managing the service

Enable & start

terminalbash

# Reload systemd after creating or editing the unit file
sudo systemctl daemon-reload

# Enable auto-start at boot
sudo systemctl enable inotify-rsync.service

# Start it right now
sudo systemctl start inotify-rsync.service

# Check status
sudo systemctl status inotify-rsync.service

Stop & disable

terminalbash

# Stop the service (boot auto-start unchanged)
sudo systemctl stop inotify-rsync.service

# Remove from boot auto-start
sudo systemctl disable inotify-rsync.service

# Stop AND disable in one command
sudo systemctl disable --now inotify-rsync.service

Restart after changes

terminalbash

# After editing a script or the .service file
sudo systemctl daemon-reload
sudo systemctl restart inotify-rsync.service

06 Logs & monitoring

systemd journal

terminalbash

# Follow logs in real time
sudo journalctl -u inotify-rsync.service -f

# Last 50 lines
sudo journalctl -u inotify-rsync.service -n 50

# Logs since today
sudo journalctl -u inotify-rsync.service --since today

Application log files

terminalbash

# inotify events log
tail -f /var/log/inotify-watch.log

# rsync transfer log
tail -f /var/log/rsync-archive.log

⚠️Don’t forget to set up logrotate to prevent these log files from growing unbounded. Create /etc/logrotate.d/rsync-archive with rotate 7, daily, compress, and missingok.

07 File summary

Here’s every file created during this tutorial:

/usr/local/bin/sync-to-archive.sh

rsync script — performs the actual file transfer.

/usr/local/bin/watch-directory.sh

inotify script — watches the directory and triggers rsync.

/etc/systemd/system/inotify-rsync.service

systemd unit — manages the service lifecycle.

/var/log/inotify-watch.log

Log of filesystem events detected by inotify.

/var/log/rsync-archive.log

Log of all rsync transfer operations and outcomes.

✅Once everything is in place, activate the full setup with a single command:
sudo systemctl enable --now inotify-rsync.service
Your automated archive sync is now live.