Event System
β Validated against Unraid 7.2.3 - Event names and execution order verified against
/usr/local/sbin/emhttp_event.
Unraid plugins can respond to system events by placing executable scripts in their event/ directory. This allows plugins to perform actions when the array starts, Docker launches, or the system is shutting down.
How Events Work
The emhttp process calls /usr/local/sbin/emhttp_event when system events occur. This script then looks for and executes event handlers in each pluginβs event/ directory.
β οΈ Caution: The emhttp process blocks until all event scripts complete. Long-running scripts will delay system operations. Use background processes for lengthy tasks.
Event Execution Order
For each event, scripts are executed in this order:
- any_event handlers - Scripts in
event/any_event/orevent/any_eventreceive ALL events - Specific event handlers - Scripts matching the event name
Plugins are processed alphabetically by plugin name.
Available Events
Startup Events
| Event | Description | When to Use |
|---|---|---|
driver_loaded |
Early initialization, INI files are valid | Load kernel modules |
starting |
Array start begins | Pre-start preparation |
array_started |
Array devices (/dev/md*) are valid |
Access array devices |
disks_mounted |
Disks and user shares are mounted | Access /mnt/user/ |
svcs_restarted |
Network services started/restarted | Configure network services |
docker_started |
Docker service is running | Start Docker containers |
libvirt_started |
VM service is running | Start VMs |
started |
Array start complete | Most common - general startup |
Shutdown Events
| Event | Description | When to Use |
|---|---|---|
stopping |
Array stop begins | Pre-shutdown tasks |
stopping_libvirt |
About to stop VMs | Gracefully stop VMs |
stopping_docker |
About to stop Docker | Stop Docker containers |
stopping_svcs |
About to stop network services | Cleanup network resources |
unmounting_disks |
Disks about to unmount | Final disk access |
stopping_array |
Disks unmounted, array stopping | Last chance before stopped |
stopped |
Array fully stopped | Post-stop cleanup |
flowchart LR
subgraph Startup["Array Start Sequence"]
direction TB
S1[driver_loaded] --> S2[starting]
S2 --> S3[array_started]
S3 --> S4[disks_mounted]
S4 --> S5[svcs_restarted]
S5 --> S6[docker_started]
S6 --> S7[libvirt_started]
S7 --> S8[started]
end
subgraph Shutdown["Array Stop Sequence"]
direction TB
P1[stopping] --> P2[stopping_libvirt]
P2 --> P3[stopping_docker]
P3 --> P4[stopping_svcs]
P4 --> P5[unmounting_disks]
P5 --> P6[stopping_array]
P6 --> P7[stopped]
end
Startup -.->|π Reboot/Stop| Shutdown
style S8 fill:#4caf50,color:#fff
style P7 fill:#f44336,color:#fff
π· Screenshot needed: Syslog output showing event/plugin messages
Other Events
| Event | Description | When to Use |
|---|---|---|
poll_attributes |
SMART data has been polled | Monitor disk health |
Creating Event Handlers
See the DocTest validation plugin event handlers for working examples of all 16 documented events.
Directory Structure
/usr/local/emhttp/plugins/myplugin/
βββ event/
βββ started # Single script for 'started' event
βββ stopping_docker # Single script for 'stopping_docker' event
βββ any_event/ # Directory for scripts that handle ALL events
βββ logger.sh
You can use either:
- A single executable file named after the event
- A directory containing multiple scripts
Basic Event Script
Create an executable script at event/started:
#!/bin/bash
# event/started - Runs when the array has fully started
# Source configuration
source /usr/local/emhttp/plugins/myplugin/default.cfg
source /boot/config/plugins/myplugin/myplugin.cfg 2>/dev/null
# Log that we're starting
logger "myplugin: Array started, initializing..."
# Do your startup tasks here
# ...
# For long-running tasks, background them
/usr/local/emhttp/plugins/myplugin/scripts/background_task.sh &
logger "myplugin: Initialization complete"
Event Script Arguments
All event scripts receive the event name as the first argument:
#!/bin/bash
# event/any_event - Handles all events
EVENT_NAME="$1"
case "$EVENT_NAME" in
started)
logger "myplugin: Array started"
;;
stopping)
logger "myplugin: Array stopping"
;;
*)
# Ignore other events
;;
esac
Common Patterns
Starting Services on Array Start
#!/bin/bash
# event/started
source /boot/config/plugins/myplugin/myplugin.cfg 2>/dev/null
# Only start if enabled in settings
if [ "${SERVICE_ENABLED}" = "true" ]; then
logger "myplugin: Starting service..."
/usr/local/emhttp/plugins/myplugin/scripts/start_service.sh &
fi
Autostarting Docker Compose Stacks
Real-world example from Compose Manager:
#!/bin/bash
# event/started
source /usr/local/emhttp/plugins/compose.manager/default.cfg
source /boot/config/plugins/compose.manager/compose.manager.cfg
COMPOSE_ROOT=$PROJECTS_FOLDER
COMPOSE_WRAPPER=/usr/local/emhttp/plugins/compose.manager/scripts/compose.sh
# Process each project directory
for dir in $COMPOSE_ROOT/*; do
if [ -d "$dir" ]; then
if [ -f "$dir/docker-compose.yml" ]; then
if [ -f "$dir/autostart" ]; then
name=$(< "${dir}/name")
logger "Starting compose stack: ${name}"
$COMPOSE_WRAPPER -c up -d "${dir}" &
fi
fi
fi
done
Graceful Shutdown
#!/bin/bash
# event/stopping_docker
source /usr/local/emhttp/plugins/myplugin/default.cfg
source /boot/config/plugins/myplugin/myplugin.cfg
# Stop all managed containers gracefully
for container in $MANAGED_CONTAINERS; do
logger "myplugin: Stopping container ${container}"
docker stop "$container" 2>/dev/null
done
Handling Multiple Scripts
If you need multiple scripts for one event, use a directory:
event/
βββ started/
βββ 01-load-config.sh
βββ 02-start-services.sh
βββ 03-notify.sh
Scripts are executed in alphabetical order.
Best Practices
1. Keep Scripts Fast
The emhttp process waits for event scripts to complete. For long tasks:
#!/bin/bash
# Bad - blocks emhttp
sleep 60
do_long_task
# Good - runs in background
do_long_task &
2. Use Logger for Debugging
Log messages appear in /var/log/syslog:
logger "myplugin: Event $1 received"
logger -t myplugin "More specific tag"
View with: tail -f /var/log/syslog | grep myplugin
3. Handle Missing Config Gracefully
#!/bin/bash
# Source config files safely
source /usr/local/emhttp/plugins/myplugin/default.cfg
# User config might not exist yet
if [ -f /boot/config/plugins/myplugin/myplugin.cfg ]; then
source /boot/config/plugins/myplugin/myplugin.cfg
fi
4. Check Dependencies
#!/bin/bash
# event/started
# Only run if Docker is actually running
if ! docker info &>/dev/null; then
logger "myplugin: Docker not running, skipping startup"
exit 0
fi
# Proceed with Docker-dependent tasks
5. Make Scripts Executable
In your package build script:
chmod +x /path/to/plugin/event/*
chmod +x /path/to/plugin/event/started
6. Be Idempotent
Scripts may run multiple times (reboots, manual array start/stop):
#!/bin/bash
# event/started
PIDFILE=/var/run/myplugin.pid
# Check if already running
if [ -f "$PIDFILE" ] && kill -0 $(cat "$PIDFILE") 2>/dev/null; then
logger "myplugin: Already running"
exit 0
fi
# Start the service
start_my_service &
echo $! > "$PIDFILE"
Testing Events
Manually Trigger Events
You can simulate events for testing:
# Simulate the 'started' event
/usr/local/sbin/emhttp_event started
# Watch syslog for output
tail -f /var/log/syslog | grep myplugin
Test Without Rebooting
During development, you can directly execute your script:
# Run your event script directly
/usr/local/emhttp/plugins/myplugin/event/started
Check for Errors
# Run with bash debugging
bash -x /usr/local/emhttp/plugins/myplugin/event/started
Reference: emhttp_event Script
Hereβs the core logic from /usr/local/sbin/emhttp_event:
#!/bin/bash
# Invoke all 'any_event' scripts that might exist
for Dir in /usr/local/emhttp/plugins/* ; do
if [ -d $Dir/event/any_event ]; then
for File in $Dir/event/any_event/* ; do
if [ -x $File ]; then
$File "$@"
fi
done
elif [ -x $Dir/event/any_event ]; then
$Dir/event/any_event "$@"
fi
done
# Invoke specific event scripts that might exist for this event
for Dir in /usr/local/emhttp/plugins/* ; do
if [ -d $Dir/event/$1 ]; then
for File in $Dir/event/$1/* ; do
if [ -x $File ]; then
$File "$@"
fi
done
elif [ -x $Dir/event/$1 ]; then
$Dir/event/$1 "$@"
fi
done
Next Steps
- Learn about Page Files for creating the web UI
- See Shell Scripts for more scripting patterns
- Check Best Practices for additional tips
