Skip to main content

Practical Examples

Learning Focus

This page contains 20 complete examples, each with the .path unit, the .service unit, activation commands, and expected output. Every example is copy-paste-ready for a Linux server.

How To Use This Page
  • Examples are grouped by watch directive so you can jump directly to the pattern you need.
  • Each example follows the same structure: .path file → .service file → activation → test → expected output.
  • Adapt the paths, users, and scripts to your environment.

DirectoryNotEmpty= — Drop Folder Patterns

1. Simple Drop Folder — Process and Remove

The most basic pattern. Files arrive in a folder, the service processes and removes them.

/etc/systemd/system/process-drop.path
[Unit]
Description=Watch /var/www/drop for incoming files

[Path]
DirectoryNotEmpty=/var/www/drop
MakeDirectory=yes

[Install]
WantedBy=paths.target
/etc/systemd/system/process-drop.service
[Unit]
Description=Process one file from the drop folder

[Service]
Type=oneshot
User=www-data
ExecStart=/usr/local/bin/process-drop.sh
/usr/local/bin/process-drop.sh
#!/usr/bin/env bash
set -euo pipefail
FILE=$(ls /var/www/drop/ | head -1)
[ -z "$FILE" ] && exit 0
echo "[$(date -Is)] Processing: $FILE"
# ... your processing logic here ...
rm "/var/www/drop/$FILE"
echo "[$(date -Is)] Done"
activate.sh
sudo systemctl daemon-reload
sudo systemctl enable --now process-drop.path
systemctl status process-drop.path --no-pager
expected-status.txt
● process-drop.path - Watch /var/www/drop for incoming files
Loaded: loaded (/etc/systemd/system/process-drop.path; enabled)
Active: active (waiting) since Mon 2026-03-02 00:00:00 UTC; 1min ago
Triggers: ● process-drop.service
Watch: /var/www/drop (DirectoryNotEmpty)

Workflow: Drop 5 files into /var/www/drop. The service runs, processes and removes one file, exits. The path detects 4 files remain and triggers again immediately — repeating until the folder is empty.


2. Sequential CSV Import Queue

Clients drop CSV files via SFTP. Process them one at a time in arrival order.

/etc/systemd/system/csv-import.path
[Unit]
Description=Watch for incoming CSV imports

[Path]
DirectoryNotEmpty=/mnt/sftp/imports
MakeDirectory=yes

[Install]
WantedBy=paths.target
/etc/systemd/system/csv-import.service
[Unit]
Description=Import one CSV file

[Service]
Type=oneshot
User=www-data
ExecStart=/usr/local/bin/import-next-csv.sh
StandardOutput=append:/var/log/csv-import.log
StandardError=append:/var/log/csv-import.log
/usr/local/bin/import-next-csv.sh
#!/usr/bin/env bash
set -euo pipefail
FILE=$(ls /mnt/sftp/imports/*.csv 2>/dev/null | sort | head -1)
[ -z "$FILE" ] && exit 0
echo "[$(date -Is)] Importing: $FILE"
/usr/local/bin/import-csv "$FILE"
mv "$FILE" /mnt/sftp/processed/
echo "[$(date -Is)] Import complete, moved to processed/"
test.sh
cp /tmp/data.csv /mnt/sftp/imports/
sleep 3
journalctl -u csv-import.service -n 10 --no-pager
expected-output.txt
Mar 02 01:00:01 vps import-next-csv.sh[1300]: [2026-03-02T01:00:01+00:00] Importing: /mnt/sftp/imports/data.csv
Mar 02 01:00:05 vps import-next-csv.sh[1300]: [2026-03-02T01:00:05+00:00] Import complete, moved to processed/

3. Auto-Create Watched Directory at First Deploy

On a brand-new server, the drop folder may not exist yet. MakeDirectory=yes and DirectoryMode= handle this cleanly.

/etc/systemd/system/report-importer.path
[Unit]
Description=Watch for incoming reports

[Path]
DirectoryNotEmpty=/opt/reports/incoming
MakeDirectory=yes
DirectoryMode=0775

[Install]
WantedBy=paths.target
/etc/systemd/system/report-importer.service
[Unit]
Description=Process incoming report

[Service]
Type=oneshot
User=www-data
ExecStart=/usr/local/bin/process-report.sh

4. User-Level Path Monitor (No Root Required)

A non-root user processes their own downloads. Place the unit in ~/.config/systemd/user/.

~/.config/systemd/user/process-dl.path
[Unit]
Description=Watch ~/Downloads for new files

[Path]
DirectoryNotEmpty=%h/Downloads
MakeDirectory=yes

[Install]
WantedBy=default.target
~/.config/systemd/user/process-dl.service
[Unit]
Description=Process a new download

[Service]
Type=oneshot
ExecStart=%h/bin/process-download.sh
enable-user-path.sh
mkdir -p ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now process-dl.path
systemctl --user status process-dl.path
expected-status.txt
● process-dl.path - Watch ~/Downloads for new files
Loaded: loaded (/home/user/.config/systemd/user/process-dl.path; enabled)
Active: active (waiting)
Watch: /home/user/Downloads (DirectoryNotEmpty)

5. Image Optimization Queue

Optimize images dropped into a queue folder, then move them to a "done" directory.

/etc/systemd/system/image-queue.path
[Unit]
Description=Watch image optimization queue

[Path]
DirectoryNotEmpty=/var/media/queue
MakeDirectory=yes
DirectoryMode=0775

[Install]
WantedBy=paths.target
/etc/systemd/system/image-queue.service
[Unit]
Description=Optimize one image from the queue

[Service]
Type=oneshot
User=www-data
ExecStart=/usr/local/bin/optimize-image.sh
StandardOutput=append:/var/log/image-optimize.log
StandardError=append:/var/log/image-optimize.log
/usr/local/bin/optimize-image.sh
#!/usr/bin/env bash
set -euo pipefail
FILE=$(ls /var/media/queue/*.{jpg,png,webp} 2>/dev/null | head -1)
[ -z "$FILE" ] && exit 0
BASENAME=$(basename "$FILE")
echo "[$(date -Is)] Optimizing: $BASENAME"
# Example: use jpegoptim/optipng/cwebp
case "$FILE" in
*.jpg) jpegoptim --max=85 "$FILE" ;;
*.png) optipng -o2 "$FILE" ;;
*.webp) cwebp -q 80 "$FILE" -o "$FILE" ;;
esac
mv "$FILE" /var/media/done/
echo "[$(date -Is)] Done: $BASENAME"
test.sh
cp photo1.jpg photo2.png /var/media/queue/
sleep 3
journalctl -u image-queue.service -n 20 --no-pager
expected-output.txt
Mar 02 02:00:01 vps optimize-image.sh[1500]: [2026-03-02T02:00:01+00:00] Optimizing: photo1.jpg
Mar 02 02:00:02 vps optimize-image.sh[1500]: [2026-03-02T02:00:02+00:00] Done: photo1.jpg
Mar 02 02:00:03 vps optimize-image.sh[1501]: [2026-03-02T02:00:03+00:00] Optimizing: photo2.png
Mar 02 02:00:04 vps optimize-image.sh[1501]: [2026-03-02T02:00:04+00:00] Done: photo2.png

PathExists= — Signal File Patterns

6. Trigger Deployment via Signal File

A CI pipeline creates deploy.me to signal that a new deployment should begin.

/etc/systemd/system/deploy-trigger.path
[Unit]
Description=Watch for deploy signal file

[Path]
PathExists=/var/www/html/deploy.me

[Install]
WantedBy=paths.target
/etc/systemd/system/deploy-trigger.service
[Unit]
Description=Run deployment on signal

[Service]
Type=oneshot
ExecStart=/usr/local/bin/deploy.sh
StandardOutput=append:/var/log/deploy.log
StandardError=append:/var/log/deploy.log
/usr/local/bin/deploy.sh
#!/usr/bin/env bash
set -euo pipefail
echo "[$(date -Is)] Deployment started"
cd /var/www/html
git pull origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
rm /var/www/html/deploy.me # CRITICAL: remove signal file
echo "[$(date -Is)] Deployment complete"
trigger.sh
touch /var/www/html/deploy.me
sleep 5
journalctl -u deploy-trigger.service -n 10 --no-pager
expected-output.txt
Mar 02 00:01:05 vps deploy.sh[1234]: [2026-03-02T00:01:05+00:00] Deployment started
Mar 02 00:01:08 vps deploy.sh[1234]: [2026-03-02T00:01:08+00:00] Deployment complete

7. Signal-File Cache Flush (No SSH Required)

Let developers flush the WordPress cache over FTP/SFTP by simply creating an empty file.

/etc/systemd/system/clear-cache.path
[Unit]
Description=Watch for cache flush signal file

[Path]
PathExists=/var/www/html/clear_cache.txt

[Install]
WantedBy=paths.target
/etc/systemd/system/clear-cache.service
[Unit]
Description=Flush WordPress object cache

[Service]
Type=oneshot
User=www-data
ExecStart=/bin/sh -c '/usr/local/bin/wp cache flush --path=/var/www/html && rm /var/www/html/clear_cache.txt'
StandardOutput=append:/var/log/cache-flush.log
StandardError=append:/var/log/cache-flush.log
test.sh
touch /var/www/html/clear_cache.txt
sleep 1
journalctl -u clear-cache.service -n 5 --no-pager
expected-output.txt
Mar 02 00:30:00 vps sh[1450]: Success: The cache was flushed.

8. WP-CLI Maintenance on a Trigger File

A monitoring script creates run-maintenance.txt when it detects something stale.

/etc/systemd/system/wp-maintenance.path
[Unit]
Description=Run WP maintenance on signal file

[Path]
PathExists=/var/www/html/run-maintenance.txt

[Install]
WantedBy=paths.target
/etc/systemd/system/wp-maintenance.service
[Unit]
Description=Run WordPress maintenance tasks

[Service]
Type=oneshot
User=www-data
ExecStart=/bin/sh -c '\
/usr/local/bin/wp cache flush --path=/var/www/html && \
/usr/local/bin/wp cron event run --due-now --path=/var/www/html && \
rm /var/www/html/run-maintenance.txt'
StandardOutput=append:/var/log/wp-maintenance.log
StandardError=append:/var/log/wp-maintenance.log

9. Wait for Admin to Upload a VPN Config

Start OpenVPN only after the admin securely SCPs a .ovpn file onto the server.

/etc/systemd/system/vpn-start.path
[Unit]
Description=Wait for OpenVPN config before starting

[Path]
PathExists=/etc/openvpn/client/config.ovpn

[Install]
WantedBy=paths.target
/etc/systemd/system/vpn-start.service
[Unit]
Description=Start OpenVPN client after config upload

[Service]
Type=oneshot
ExecStart=/bin/systemctl start openvpn@client

Use case: On a freshly provisioned VPS, the VPN should not start until the admin manually uploads the config file. No cron needed — the path unit waits indefinitely with zero CPU usage.


PathChanged= — Safe File Monitoring

10. Safe Upload Processing (Waits for File Close)

Wait for a data file to be fully written before processing it.

/etc/systemd/system/sync-data.path
[Unit]
Description=Watch for catalog.sqlite update

[Path]
PathChanged=/var/data/catalog.sqlite

[Install]
WantedBy=paths.target
/etc/systemd/system/sync-data.service
[Unit]
Description=Sync catalog database to staging

[Service]
Type=oneshot
ExecStart=/usr/local/bin/sync-catalog.sh
StandardOutput=append:/var/log/sync-catalog.log
StandardError=append:/var/log/sync-catalog.log
test.sh
cp /tmp/catalog-new.sqlite /var/data/catalog.sqlite
sleep 1
journalctl -u sync-data.service -n 5 --no-pager
expected-output.txt
Mar 02 00:05:12 vps sync-catalog.sh[1289]: Sync complete. 4821 rows transferred.

11. Auto-Reload Nginx on Config Change

Reload Nginx automatically whenever a file inside conf.d is modified.

/etc/systemd/system/nginx-conf-watcher.path
[Unit]
Description=Watch Nginx conf.d for changes

[Path]
PathChanged=/etc/nginx/conf.d
Unit=nginx-reload.service

[Install]
WantedBy=paths.target
/etc/systemd/system/nginx-reload.service
[Unit]
Description=Reload Nginx after config change

[Service]
Type=oneshot
ExecStart=/usr/bin/nginx -t
ExecStart=/bin/systemctl reload nginx
test.sh
echo "# comment" >> /etc/nginx/conf.d/default.conf
sleep 1
journalctl -u nginx-reload.service -n 5 --no-pager
expected-output.txt
Mar 02 00:20:01 vps nginx[1400]: nginx: configuration file /etc/nginx/nginx.conf test is successful
Mar 02 00:20:01 vps systemd[1]: Reloading nginx.service...

12. Restart PHP-FPM When php.ini Changes

/etc/systemd/system/php-ini-watcher.path
[Unit]
Description=Watch php.ini for changes

[Path]
PathChanged=/etc/php/8.2/fpm/php.ini

[Install]
WantedBy=paths.target
/etc/systemd/system/php-ini-watcher.service
[Unit]
Description=Restart PHP-FPM after php.ini change

[Service]
Type=oneshot
ExecStart=/bin/systemctl restart php8.2-fpm
activate.sh
sudo systemctl daemon-reload
sudo systemctl enable --now php-ini-watcher.path

13. Watch Multiple Critical WordPress Files

Trigger a security audit script whenever wp-config.php or .htaccess is modified.

/etc/systemd/system/wp-core-watcher.path
[Unit]
Description=Watch critical WordPress files for tampering

[Path]
PathChanged=/var/www/html/wp-config.php
PathChanged=/var/www/html/.htaccess

[Install]
WantedBy=paths.target
/etc/systemd/system/wp-core-watcher.service
[Unit]
Description=Audit WordPress core file change

[Service]
Type=oneshot
User=www-data
ExecStart=/usr/local/bin/audit-wp-change.sh
StandardOutput=append:/var/log/wp-audit.log
StandardError=append:/var/log/wp-audit.log
/usr/local/bin/audit-wp-change.sh
#!/usr/bin/env bash
set -euo pipefail
echo "[$(date -Is)] ALERT: Critical WordPress file changed"
echo "Modified files:"
stat /var/www/html/wp-config.php /var/www/html/.htaccess 2>/dev/null | grep -E "File:|Modify:"
# Optional: send notification
# curl -s -X POST "https://hooks.slack.com/services/..." -d '{"text":"WP core file changed!"}'

14. Auto-Backup .env on Change

Every time .env is saved, copy it to the backup volume.

/etc/systemd/system/backup-env.path
[Unit]
Description=Watch .env file for changes

[Path]
PathChanged=/var/www/html/.env

[Install]
WantedBy=paths.target
/etc/systemd/system/backup-env.service
[Unit]
Description=Back up .env on change

[Service]
Type=oneshot
ExecStart=/bin/sh -c 'cp /var/www/html/.env /mnt/backups/env-$(date +%%F-%%T).bak'
StandardOutput=append:/var/log/env-backup.log
StandardError=append:/var/log/env-backup.log

15. Watch TLS Certificate Renewal and Reload Nginx

After certbot renews a certificate and writes a new fullchain.pem, immediately reload Nginx.

/etc/systemd/system/cert-watcher.path
[Unit]
Description=Watch for renewed TLS certificates

[Path]
PathChanged=/etc/letsencrypt/live/example.com/fullchain.pem
Unit=nginx-reload.service

[Install]
WantedBy=paths.target

Workflow:

certbot renew → writes new fullchain.pem → PathChanged fires → nginx-reload.service → nginx reloads with new cert

16. Malware Scan on Uploads Directory Change

Trigger an on-demand ClamAV scan whenever the uploads directory changes.

/etc/systemd/system/scan-uploads.path
[Unit]
Description=Watch WordPress uploads for suspicious changes

[Path]
PathChanged=/var/www/html/wp-content/uploads

[Install]
WantedBy=paths.target
/etc/systemd/system/scan-uploads.service
[Unit]
Description=ClamAV scan on uploads directory

[Service]
Type=oneshot
ExecStart=/usr/bin/clamscan --recursive --move=/tmp/quarantine /var/www/html/wp-content/uploads
StandardOutput=append:/var/log/clamscan.log
StandardError=append:/var/log/clamscan.log

PathModified= — Instant Reaction

17. React Immediately on Any Write (Log Alerting)

Trigger an alert the instant a specific log receives a new line.

/etc/systemd/system/urgent-alert.path
[Unit]
Description=Watch urgent.log for new entries

[Path]
PathModified=/var/log/urgent.log

[Install]
WantedBy=paths.target
/etc/systemd/system/urgent-alert.service
[Unit]
Description=Send alert on urgent log entry

[Service]
Type=oneshot
ExecStart=/usr/local/bin/send-alert.sh /var/log/urgent.log
/usr/local/bin/send-alert.sh
#!/usr/bin/env bash
set -euo pipefail
LAST_LINE=$(tail -1 "$1")
echo "[$(date -Is)] Alert: $LAST_LINE"
# Send to Slack, email, or PagerDuty
curl -s -X POST "https://hooks.slack.com/services/T.../B.../xxx" \
-H 'Content-type: application/json' \
-d "{\"text\": \"ALERT: $LAST_LINE\"}"
test.sh
echo "CRITICAL: Disk 98% full" >> /var/log/urgent.log
sleep 1
journalctl -u urgent-alert.service -n 5 --no-pager
expected-output.txt
Mar 02 00:10:01 vps send-alert.sh[1301]: [2026-03-02T00:10:01+00:00] Alert: CRITICAL: Disk 98% full

18. Sync Media to Object Storage on Upload

Every time new files land in the uploads directory, sync them to S3.

/etc/systemd/system/upload-sync.path
[Unit]
Description=Sync uploads to object storage on change

[Path]
PathModified=/var/www/html/wp-content/uploads

[Install]
WantedBy=paths.target
/etc/systemd/system/upload-sync.service
[Unit]
Description=Sync uploads to S3

[Service]
Type=oneshot
User=www-data
ExecStart=/usr/local/bin/rclone sync /var/www/html/wp-content/uploads remote:wp-uploads
StandardOutput=append:/var/log/upload-sync.log
StandardError=append:/var/log/upload-sync.log
test.sh
cp /tmp/image.jpg /var/www/html/wp-content/uploads/
sleep 2
journalctl -u upload-sync.service -n 5 --no-pager
expected-output.txt
Mar 02 01:00:05 vps rclone[1600]: Copied: image.jpg (2.4 MiB, 1.2 MiB/s)
Mar 02 01:00:05 vps rclone[1600]: Transferred: 1 / 1, 100%, 2.4 MiB, 1.2 MiB/s

PathExistsGlob= — Pattern Matching

19. Glob-Based Trigger for SQL Imports

Import any .sql file dropped into a staging folder, ignoring other file types.

/etc/systemd/system/db-import.path
[Unit]
Description=Watch for incoming SQL dump files

[Path]
PathExistsGlob=/mnt/import/*.sql

[Install]
WantedBy=paths.target
/etc/systemd/system/db-import.service
[Unit]
Description=Import a SQL dump into WordPress DB

[Service]
Type=oneshot
User=www-data
ExecStart=/usr/local/bin/import-sql.sh
StandardOutput=append:/var/log/db-import.log
StandardError=append:/var/log/db-import.log
/usr/local/bin/import-sql.sh
#!/usr/bin/env bash
set -euo pipefail
FILE=$(ls /mnt/import/*.sql 2>/dev/null | head -1)
[ -z "$FILE" ] && exit 0
echo "[$(date -Is)] Importing: $FILE"
/usr/local/bin/wp db import "$FILE" --path=/var/www/html
mv "$FILE" /mnt/import/done/
echo "[$(date -Is)] Done: $FILE"
test.sh
scp backup.sql user@vps:/mnt/import/
sleep 2
journalctl -u db-import.service -n 10 --no-pager
expected-output.txt
Mar 02 01:15:00 vps import-sql.sh[1350]: [2026-03-02T01:15:00+00:00] Importing: /mnt/import/backup.sql
Mar 02 01:15:08 vps import-sql.sh[1350]: [2026-03-02T01:15:08+00:00] Done: /mnt/import/backup.sql

20. Auto-Deploy WordPress Theme from a Zip

Drop a .zip into a staging folder to trigger automatic theme unpacking.

/etc/systemd/system/theme-deploy.path
[Unit]
Description=Deploy WordPress theme from zip drop

[Path]
PathExistsGlob=/var/www/dropzone/*.zip

[Install]
WantedBy=paths.target
/etc/systemd/system/theme-deploy.service
[Unit]
Description=Unpack and deploy dropped theme zip

[Service]
Type=oneshot
User=www-data
ExecStart=/usr/local/bin/deploy-theme.sh
StandardOutput=append:/var/log/theme-deploy.log
StandardError=append:/var/log/theme-deploy.log
/usr/local/bin/deploy-theme.sh
#!/usr/bin/env bash
set -euo pipefail
ZIP=$(ls /var/www/dropzone/*.zip 2>/dev/null | head -1)
[ -z "$ZIP" ] && exit 0
BASENAME=$(basename "$ZIP")
echo "[$(date -Is)] Deploying theme: $BASENAME"
unzip -o "$ZIP" -d /var/www/html/wp-content/themes/
rm "$ZIP"
chown -R www-data:www-data /var/www/html/wp-content/themes/
echo "[$(date -Is)] Theme deployed: $BASENAME"
test.sh
cp mytheme.zip /var/www/dropzone/
sleep 3
journalctl -u theme-deploy.service -n 10 --no-pager
expected-output.txt
Mar 02 02:30:01 vps deploy-theme.sh[1700]: [2026-03-02T02:30:01+00:00] Deploying theme: mytheme.zip
Mar 02 02:30:03 vps deploy-theme.sh[1700]: [2026-03-02T02:30:03+00:00] Theme deployed: mytheme.zip

Example Quick Reference

#PatternDirectiveKey Behavior
1Simple drop folderDirectoryNotEmpty=Process and remove files
2CSV import queueDirectoryNotEmpty=Sequential processing with move
3Auto-create directoryDirectoryNotEmpty=MakeDirectory=yes
4User-level monitorDirectoryNotEmpty=No root, %h specifier
5Image optimizationDirectoryNotEmpty=Queue pattern with move
6Deploy on signalPathExists=CI/CD trigger, must delete file
7Cache flush signalPathExists=SFTP-friendly, no SSH needed
8WP maintenancePathExists=Multi-task WP-CLI
9Wait for VPN configPathExists=Conditional service start
10Safe upload syncPathChanged=Waits for fd close
11Nginx config reloadPathChanged=Directory-level watch
12PHP-FPM restartPathChanged=Config file monitoring
13WordPress securityPathChanged=Multi-file watch
14Auto-backup .envPathChanged=Timestamped copies
15TLS cert reloadPathChanged=certbot integration
16ClamAV scanPathChanged=On-demand malware scanning
17Log alertingPathModified=Instant notification
18S3 upload syncPathModified=Real-time cloud sync
19SQL glob importPathExistsGlob=Type-specific processing
20Theme zip deployPathExistsGlob=Unpack and install

What's Next

  • Production Patterns — hardening, rate limiting, and security sandboxing for production deployments.