Practical Examples
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.
- Examples are grouped by watch directive so you can jump directly to the pattern you need.
- Each example follows the same structure:
.pathfile →.servicefile → 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.
[Unit]
Description=Watch /var/www/drop for incoming files
[Path]
DirectoryNotEmpty=/var/www/drop
MakeDirectory=yes
[Install]
WantedBy=paths.target
[Unit]
Description=Process one file from the drop folder
[Service]
Type=oneshot
User=www-data
ExecStart=/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"
sudo systemctl daemon-reload
sudo systemctl enable --now process-drop.path
systemctl status process-drop.path --no-pager
● 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.
[Unit]
Description=Watch for incoming CSV imports
[Path]
DirectoryNotEmpty=/mnt/sftp/imports
MakeDirectory=yes
[Install]
WantedBy=paths.target
[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/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/"
cp /tmp/data.csv /mnt/sftp/imports/
sleep 3
journalctl -u csv-import.service -n 10 --no-pager
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.
[Unit]
Description=Watch for incoming reports
[Path]
DirectoryNotEmpty=/opt/reports/incoming
MakeDirectory=yes
DirectoryMode=0775
[Install]
WantedBy=paths.target
[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/.
[Unit]
Description=Watch ~/Downloads for new files
[Path]
DirectoryNotEmpty=%h/Downloads
MakeDirectory=yes
[Install]
WantedBy=default.target
[Unit]
Description=Process a new download
[Service]
Type=oneshot
ExecStart=%h/bin/process-download.sh
mkdir -p ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now process-dl.path
systemctl --user status process-dl.path
● 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.
[Unit]
Description=Watch image optimization queue
[Path]
DirectoryNotEmpty=/var/media/queue
MakeDirectory=yes
DirectoryMode=0775
[Install]
WantedBy=paths.target
[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/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"
cp photo1.jpg photo2.png /var/media/queue/
sleep 3
journalctl -u image-queue.service -n 20 --no-pager
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.
[Unit]
Description=Watch for deploy signal file
[Path]
PathExists=/var/www/html/deploy.me
[Install]
WantedBy=paths.target
[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/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"
touch /var/www/html/deploy.me
sleep 5
journalctl -u deploy-trigger.service -n 10 --no-pager
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.
[Unit]
Description=Watch for cache flush signal file
[Path]
PathExists=/var/www/html/clear_cache.txt
[Install]
WantedBy=paths.target
[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
touch /var/www/html/clear_cache.txt
sleep 1
journalctl -u clear-cache.service -n 5 --no-pager
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.
[Unit]
Description=Run WP maintenance on signal file
[Path]
PathExists=/var/www/html/run-maintenance.txt
[Install]
WantedBy=paths.target
[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.
[Unit]
Description=Wait for OpenVPN config before starting
[Path]
PathExists=/etc/openvpn/client/config.ovpn
[Install]
WantedBy=paths.target
[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.
[Unit]
Description=Watch for catalog.sqlite update
[Path]
PathChanged=/var/data/catalog.sqlite
[Install]
WantedBy=paths.target
[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
cp /tmp/catalog-new.sqlite /var/data/catalog.sqlite
sleep 1
journalctl -u sync-data.service -n 5 --no-pager
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.
[Unit]
Description=Watch Nginx conf.d for changes
[Path]
PathChanged=/etc/nginx/conf.d
Unit=nginx-reload.service
[Install]
WantedBy=paths.target
[Unit]
Description=Reload Nginx after config change
[Service]
Type=oneshot
ExecStart=/usr/bin/nginx -t
ExecStart=/bin/systemctl reload nginx
echo "# comment" >> /etc/nginx/conf.d/default.conf
sleep 1
journalctl -u nginx-reload.service -n 5 --no-pager
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
[Unit]
Description=Watch php.ini for changes
[Path]
PathChanged=/etc/php/8.2/fpm/php.ini
[Install]
WantedBy=paths.target
[Unit]
Description=Restart PHP-FPM after php.ini change
[Service]
Type=oneshot
ExecStart=/bin/systemctl restart php8.2-fpm
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.
[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
[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/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.
[Unit]
Description=Watch .env file for changes
[Path]
PathChanged=/var/www/html/.env
[Install]
WantedBy=paths.target
[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.
[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.
[Unit]
Description=Watch WordPress uploads for suspicious changes
[Path]
PathChanged=/var/www/html/wp-content/uploads
[Install]
WantedBy=paths.target
[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.
[Unit]
Description=Watch urgent.log for new entries
[Path]
PathModified=/var/log/urgent.log
[Install]
WantedBy=paths.target
[Unit]
Description=Send alert on urgent log entry
[Service]
Type=oneshot
ExecStart=/usr/local/bin/send-alert.sh /var/log/urgent.log
#!/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\"}"
echo "CRITICAL: Disk 98% full" >> /var/log/urgent.log
sleep 1
journalctl -u urgent-alert.service -n 5 --no-pager
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.
[Unit]
Description=Sync uploads to object storage on change
[Path]
PathModified=/var/www/html/wp-content/uploads
[Install]
WantedBy=paths.target
[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
cp /tmp/image.jpg /var/www/html/wp-content/uploads/
sleep 2
journalctl -u upload-sync.service -n 5 --no-pager
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.
[Unit]
Description=Watch for incoming SQL dump files
[Path]
PathExistsGlob=/mnt/import/*.sql
[Install]
WantedBy=paths.target
[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/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"
scp backup.sql user@vps:/mnt/import/
sleep 2
journalctl -u db-import.service -n 10 --no-pager
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.
[Unit]
Description=Deploy WordPress theme from zip drop
[Path]
PathExistsGlob=/var/www/dropzone/*.zip
[Install]
WantedBy=paths.target
[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/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"
cp mytheme.zip /var/www/dropzone/
sleep 3
journalctl -u theme-deploy.service -n 10 --no-pager
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
| # | Pattern | Directive | Key Behavior |
|---|---|---|---|
| 1 | Simple drop folder | DirectoryNotEmpty= | Process and remove files |
| 2 | CSV import queue | DirectoryNotEmpty= | Sequential processing with move |
| 3 | Auto-create directory | DirectoryNotEmpty= | MakeDirectory=yes |
| 4 | User-level monitor | DirectoryNotEmpty= | No root, %h specifier |
| 5 | Image optimization | DirectoryNotEmpty= | Queue pattern with move |
| 6 | Deploy on signal | PathExists= | CI/CD trigger, must delete file |
| 7 | Cache flush signal | PathExists= | SFTP-friendly, no SSH needed |
| 8 | WP maintenance | PathExists= | Multi-task WP-CLI |
| 9 | Wait for VPN config | PathExists= | Conditional service start |
| 10 | Safe upload sync | PathChanged= | Waits for fd close |
| 11 | Nginx config reload | PathChanged= | Directory-level watch |
| 12 | PHP-FPM restart | PathChanged= | Config file monitoring |
| 13 | WordPress security | PathChanged= | Multi-file watch |
| 14 | Auto-backup .env | PathChanged= | Timestamped copies |
| 15 | TLS cert reload | PathChanged= | certbot integration |
| 16 | ClamAV scan | PathChanged= | On-demand malware scanning |
| 17 | Log alerting | PathModified= | Instant notification |
| 18 | S3 upload sync | PathModified= | Real-time cloud sync |
| 19 | SQL glob import | PathExistsGlob= | Type-specific processing |
| 20 | Theme zip deploy | PathExistsGlob= | Unpack and install |
What's Next
- Production Patterns — hardening, rate limiting, and security sandboxing for production deployments.