Add and verify a NetworkManager dispatcher script
You will create a small root-owned hook that records NetworkManager events in the system journal, verify that the dispatcher can run it, and remove the hook without leaving a service change behind. Allow about 10 minutes. You need NetworkManager 1.46.0 or a compatible release, a shell account with sudo access, and a connection you can safely activate or deactivate for testing.
The route
Jump straight to the step you need, or tick off Done means at the end.
What the dispatcher does
NetworkManager-dispatcher is a D-Bus activated service. When NetworkManager reports a relevant change, it runs regular executable files from /etc/NetworkManager/dispatcher.d and /usr/lib/NetworkManager/dispatcher.d, including their subdirectories, in alphabetical order. The installed package here is network-manager 1.46.0-1ubuntu2.8.
Each script receives two positional arguments. The first is the interface involved, and the second is an action such as up, down, vpn-up, dhcp4-change, connectivity-change or dns-change. The first argument is none for hostname, and is empty for connectivity-change and dns-change. Do not assume that every event has a usable interface name.
Checkpoint: choose a narrowly scoped hook
Use /etc/NetworkManager/dispatcher.d for local administrator scripts. A two-digit prefix makes ordering obvious; it does not make a script run before every possible file, because all names are sorted together. The example below only logs the event. It does not change routes, restart services or alter DNS.
Read the script before installing it. The command changes system state and requires elevated privileges.
sudo install -d -m 0755 /etc/NetworkManager/dispatcher.d
sudo tee /etc/NetworkManager/dispatcher.d/90-log-network-event >/dev/null <<'EOF'
#!/bin/sh
set -eu
interface=$1
action=$2
logger -t nm-dispatcher -- "interface=$interface action=$action connection=${CONNECTION_ID-unknown}"
EOF
sudo chown root:root /etc/NetworkManager/dispatcher.d/90-log-network-event
sudo chmod 0755 /etc/NetworkManager/dispatcher.d/90-log-network-event
install, tee, chown and chmod are prefixed with sudo because the target is under /etc. The quoted here-document keeps the shell from expanding variables while the file is being created. At event time, NetworkManager supplies CONNECTION_ID when it has one; the unknown fallback keeps the logger call safe for events without connection data.
Verify the file before triggering anything
The dispatcher requires a regular executable file owned by root. It must not be writable by group or other users and must not have the set-user-ID bit. Check those properties before testing.
stat -c '%A %U:%G %n' /etc/NetworkManager/dispatcher.d/90-log-network-event
sudo sh -n /etc/NetworkManager/dispatcher.d/90-log-network-event
systemctl status NetworkManager-dispatcher.service --no-pager
Expected output includes permissions equivalent to -rwxr-xr-x, root:root, and the expected path. The service may show as inactive before an event: it is D-Bus activated, so inactivity by itself is not a failure. A successful shell syntax check produces no output.
Checkpoint: trigger and inspect one event
Choose a connection that can be interrupted. Bringing a production connection down can disconnect your session, so use a local console or a test connection if possible. The following command is an example only; replace the placeholder with an existing connection name, and expect a brief network interruption.
nmcli connection down id "TEST_CONNECTION_NAME"
nmcli connection up id "TEST_CONNECTION_NAME"
journalctl -t nm-dispatcher -n 20 --no-pager
The journal should contain a line similar to nm-dispatcher: interface=... action=down ... and another for up. The exact interface, connection ID and ordering depend on the connection and other network activity. If you cannot safely interrupt a connection, wait for an ordinary DHCP or DNS change and inspect the journal instead.
NetworkManager runs dispatcher scripts one at a time and asynchronously from its main process. A script queued for execution is still run even when a later event makes it stale; an up handler can therefore run after the interface has already gone down. Keep handlers short and make them tolerate the current state not matching the event that caused them.
Use event-specific data carefully
The environment contains values such as DEVICE_IFACE, DEVICE_IP_IFACE, CONNECTION_UUID, CONNECTION_ID, IP4_GATEWAY, IP4_NUM_ADDRESSES, IP4_NAMESERVERS and their IPv6 equivalents. VPN events additionally expose VPN_IP_IFACE and VPN-prefixed address variables. A DHCP option can appear as a variable such as DHCP4_HOST_NAME. Test that a variable is set before using it, because event types provide different subsets of the environment.
For connectivity-change, inspect CONNECTIVITY_STATE, which can be UNKNOWN, NONE, PORTAL, LIMITED or FULL. For dns-change, a script can inspect /run/NetworkManager/resolv.conf, even when NetworkManager is configured not to manage the usual resolver file.
Pre-events are a special case. Put scripts for pre-up or vpn-pre-up in /etc/NetworkManager/dispatcher.d/pre-up.d. Put pre-down or vpn-pre-down scripts in /etc/NetworkManager/dispatcher.d/pre-down.d. NetworkManager waits for these scripts before completing the corresponding clean transition. Forced disconnects, such as carrier loss, do not emit the pre-down events.
Keep the handler reliable
Do not put arbitrary long-running work directly in a dispatcher script. NetworkManager can kill scripts that take too long. If work may take an unpredictable time, have the script start a separate process and return promptly, with its own logging and failure handling. Scripts linked into /etc/NetworkManager/dispatcher.d/no-wait.d are run in parallel without waiting for earlier scripts; use that only when concurrency is deliberate and safe.
For the device-add and device-delete actions, only the configured generic-device handler is run. A successful device-add handler must write an IFINDEX value to standard output. Standard output is reserved for these result keys, so send diagnostic messages to standard error instead. Ordinary event handlers should avoid treating standard output as a log.
Remove the test hook
When testing is complete, remove only the file created in this guide. This is an irreversible deletion of that file, so confirm the path first.
sudo test -f /etc/NetworkManager/dispatcher.d/90-log-network-event
sudo rm /etc/NetworkManager/dispatcher.d/90-log-network-event
test ! -e /etc/NetworkManager/dispatcher.d/90-log-network-event && echo "test hook removed"
No restart is required to remove a dispatcher script. Existing events already queued may still be processed according to the dispatcher's queue; future events will not find the removed file.
Done means
- The hook is a root-owned regular executable with no group or other write permission.
- The script accepts the two dispatcher arguments and handles missing environment values.
- A safe test event produced a matching
nm-dispatcherjournal entry. - Any pre-event or parallel execution choice is intentional.
- The test file has been removed, or its ongoing purpose and recovery plan are documented.