{"id":13,"date":"2026-08-21T18:11:08","date_gmt":"2026-08-21T16:11:08","guid":{"rendered":"https:\/\/linuxundich.de\/en\/?p=13"},"modified":"2026-10-03T16:58:35","modified_gmt":"2026-10-03T14:58:35","slug":"mounting-network-shares-automatically-what-can-go-wrong-with-the-networkmanager-dispatcher","status":"publish","type":"post","link":"https:\/\/linuxundich.de\/en\/gnu-linux\/mounting-network-shares-automatically-what-can-go-wrong-with-the-networkmanager-dispatcher\/","title":{"rendered":"Mounting Network Shares Automatically: What Can Go Wrong with the NetworkManager Dispatcher"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">You may know the situation: at home you have a NAS or a cloud storage that you mount via Samba or WebDAV. But only when you are actually on your home network. Nobody wants to sit in a caf\u00e9 waiting because the laptop is desperately trying to mount a share that is simply not reachable right now.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The obvious solution: <code>\/etc\/fstab<\/code> plus a dispatcher script for NetworkManager that mounts automatically based on the SSID or on whether the server is reachable. Sounds like an evening project. It isn&#8217;t quite. I tripped over three pitfalls that you only notice when the machine shuts down, wakes up, or is supposed to go into standby. Those are the three I&#8217;ll walk you through here.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I tested all of this on Arch Linux, but the setup is not Arch-specific. As long as your distro ships systemd and NetworkManager (so Fedora, Ubuntu, Debian or openSUSE, for example), everything here works one to one.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The basic setup<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Network shares traditionally get <code>noauto<\/code> in the fstab, so they are not mounted immediately at boot but only when needed:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/server\/home \/mnt\/server\/home cifs credentials=\/etc\/samba\/server.credentials,rw,uid=1000,gid=100,noauto,_netdev 0 0\n\/\/server\/data \/mnt\/server\/data cifs credentials=\/etc\/samba\/server.credentials,rw,uid=1000,gid=100,noauto,_netdev 0 0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For the mount type itself, the Arch Wiki has good guides depending on the protocol, for example on <a href=\"https:\/\/wiki.archlinux.org\/title\/Samba\">Samba\/CIFS<\/a> and on <a href=\"https:\/\/wiki.archlinux.org\/title\/Davfs2\">davfs2 for WebDAV<\/a>. Both work on any distribution, as long as <em>cifs-utils<\/em> or <em>davfs2<\/em> are installed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The actual mounting is then handled by a script in <code>\/etc\/NetworkManager\/dispatcher.d\/<\/code>. NetworkManager calls such scripts on every connection change and passes along the interface and the status (up, down, pre-down and a few more). All the details are in the <a href=\"https:\/\/man.archlinux.org\/man\/NetworkManager-dispatcher.8.en\">NetworkManager dispatcher man page<\/a>. A simple script checks the active Wi-Fi SSID and mounts if it matches:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\nIFACE=&quot;$1&quot;\nSTATUS=&quot;$2&quot;\n\ncase &quot;$STATUS&quot; in\n    up)\n        SSID=$(nmcli -t -f active,ssid dev wifi | awk -F: '$1 == &quot;yes&quot; {print $2; exit}')\n        if &amp;#91;&amp;#91; &quot;$SSID&quot; == &quot;MeinHeimnetz&quot; ]]; then\n            mount \/mnt\/server\/home\n            mount \/mnt\/server\/data\n        fi\n        ;;\n    down)\n        umount \/mnt\/server\/home\n        umount \/mnt\/server\/data\n        ;;\nesac<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Done, right? Not quite. As soon as you shut down the system, wake it from standby or put it into standby, it turns out that this approach has its catches.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why not just use GVfs?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">You may now be wondering why I put myself through fstab and dispatcher scripts at all. GNOME offers \u201cConnect to Server\u201d in the file manager, which is a much easier way to mount network shares via SMB or WebDAV. That is exactly where my problem lies.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Such mounts don&#8217;t go through the kernel but through GVfs, GNOME&#8217;s virtual file system. Programs that use the GIO library (almost all GTK apps, first and foremost the file manager itself) talk to shares directly through their own URIs like <code>smb:\/\/<\/code> or <code>dav:\/\/<\/code>, and accordingly display thumbnails and metadata correctly. For programs that can&#8217;t do that, GVfs additionally exposes the share as a perfectly normal path under <code>\/run\/user\/&lt;uid&gt;\/gvfs<\/code>. This bridge does run through FUSE. GVfs itself is not a classic FUSE file system but its own architecture with a daemon process per protocol, yet for exactly this case it provides a POSIX-compatible path via gvfsd-fuse.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This very bridge is the sticking point for me: not every program copes with it reliably. In my case it shows up in a Markdown editor where I also embed images for articles. The file manager displays the images on the network drive just fine, because it speaks GVfs natively. The Markdown editor, on the other hand, shows only a placeholder for the same image. Access via the FUSE path simply doesn&#8217;t work reliably, even though the file is demonstrably there.<\/p>\n\n\n\n<figure data-wp-context=\"{&quot;imageId&quot;:&quot;6ac177107fd03&quot;}\" data-wp-interactive=\"core\/image\" data-wp-key=\"6ac177107fd03\" class=\"wp-block-image wp-lightbox-container\"><img decoding=\"async\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on--pointerdown=\"actions.preloadImage\" data-wp-on--pointerenter=\"actions.preloadImageWithDelay\" data-wp-on--pointerleave=\"actions.cancelPreload\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/linuxundich.de\/wp-content\/uploads\/2026\/08\/apostrophe-webdav-bild-1280x720.webp\" alt=\"Two windows are open on a computer screen. On the left is a text editor with a file showing structured text with headings and paragraphs. On the right is a document viewer with a longer body of text that also contains paragraphs and a heading. In the background a desktop with a gradient of dark and purple tones is visible. At the top, the interface shows a menu bar with icons and the time.\" style=\"width:100%\"\/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\tdata-wp-bind--aria-label=\"state.thisImage.triggerButtonAriaLabel\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"state.thisImage.buttonRight\"\n\t\t\tdata-wp-style--top=\"state.thisImage.buttonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">A classic kernel mount via CIFS or davfs2, as described in the rest of this article, shows up as a perfectly normal, stable POSIX path in the file system, with no special treatment and the same for every program. So the extra effort with fstab and dispatcher scripts pays off precisely when you want to use shares reliably not just in the file manager but in all kinds of apps.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Problem 1: The computer doesn&#8217;t shut down cleanly anymore<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If the fstab lacks the option _netdev, systemd doesn&#8217;t know that this is a network share. On shutdown, systemd may then try to unmount <em>after<\/em> the network is already gone. The umount blocks until the job timeout kicks in, and your system hangs on shutdown.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/wiki.archlinux.org\/title\/Fstab#Remote_filesystem\">_netdev<\/a> fixes that, because systemd then knows the right order: unmount first, then cut the network. It is also important that a dispatcher script reacts not just to down but to pre-down. That status fires before the connection actually drops, while down comes afterward and is too late for a clean unmount. For this to work, you additionally need a symlink in the subfolder <code>pre-down.d\/<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Problem 2: The file manager freezes after waking up<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A mount call for CIFS or WebDAV runs in the kernel and can block for quite a while if the server is unreachable, especially right after waking up, when DNS or routing aren&#8217;t fully back yet. By default, NetworkManager waits synchronously until such a dispatcher script has finished before it carries on. If the mount call hangs, suddenly your file manager or the network indicator hangs too, because they depend on the connection state.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The fix: just run the mount attempts in the background, so the dispatcher script itself returns immediately, no matter how long the actual mount takes.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>run_async() {\n    ( &quot;$@&quot; ) &amp;&gt;\/dev\/null &amp;\n    disown\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Problem 3: Suspend suddenly stops working<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">And this is where it gets interesting. With the fix from problem 2 you stumble into a new and much nastier problem, which in my case looked like this: the screen turns off, but the keyboard backlight and LEDs stay on, and you have to force a hard power-off. What happened?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The mount syscall for CIFS or davfs blocks with an unreachable server in the so-called <a href=\"https:\/\/eklitzke.org\/uninterruptible-sleep\">D state (uninterruptible sleep)<\/a>, a state that no signal can interrupt, not even SIGKILL. Before the kernel actually goes to sleep, it tries to freeze all processes. A process in D state can&#8217;t be frozen, so the whole suspend hangs until the network operation in the kernel gives up on its own, which can easily take a few minutes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Because the mount attempts from problem 2 now keep running in the background for longer, the chance also goes up that such a blocking connection attempt is active at exactly the moment you want to suspend.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The reliable way out: before every mount, first check with a real userspace connection test whether the server is reachable at all. That kind of test can actually be aborted with a timeout, for example via Bash&#8217;s built-in \/dev\/tcp:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>tcp_reachable() {\n    local HOST=&quot;$1&quot;\n    local PORT=&quot;$2&quot;\n    local TIMEOUT_SECONDS=&quot;${3:-2}&quot;\n    timeout &quot;${TIMEOUT_SECONDS}s&quot; bash -c &quot;echo &gt;\/dev\/tcp\/${HOST}\/${PORT}&quot; &amp;&gt;\/dev\/null\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Only if the test succeeds does the actual mount follow. That way you almost never end up in the risky, blocking kernel connect, because by then you already know the server is there.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The combination that works for me<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In the end, a robust setup needs four ingredients:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li><strong>_netdev<\/strong> in the fstab, so systemd classifies network shares correctly on shutdown.<\/li>\n\n\n<li><strong>pre-down handling<\/strong> plus a symlink in pre-down.d\/, so the unmount is guaranteed to happen before the connection is torn down, with a timeout and lazy unmount as a backup in case the normal umount acts up.<\/li>\n\n\n<li><strong>Asynchronous mounts<\/strong> on up, so NetworkManager and everything that depends on it doesn&#8217;t block.<\/li>\n\n\n<li><strong>A reachability test<\/strong> before every mount, so the actual, potentially blocking kernel connect never happens in the first place.<\/li>\n\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">On top of that, a small health check via a systemd timer that looks a few minutes after boot and regularly afterward to see whether the shares are really there, and nudges things along if needed. Handy in case the triggering network event comes too early or too late.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">All files at a glance<\/h3>\n\n\n\n<figure data-wp-context=\"{&quot;imageId&quot;:&quot;6ac17710807d3&quot;}\" data-wp-interactive=\"core\/image\" data-wp-key=\"6ac17710807d3\" class=\"wp-block-image wp-lightbox-container\"><img decoding=\"async\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on--pointerdown=\"actions.preloadImage\" data-wp-on--pointerenter=\"actions.preloadImageWithDelay\" data-wp-on--pointerleave=\"actions.cancelPreload\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/linuxundich.de\/wp-content\/uploads\/2026\/08\/nm-dispatcher-helper.webp\" alt=\"A computer screen shows a terminal window with a black background and white text. At the top is a tree structure of the directories and files under '\/etc\/NetworkManager\/dispatcher.d'. The structure contains several subdirectories and shell scripts. On the left, timestamps and commands are visible, on the right the respective outputs. The window is framed by a gray menu bar.\" style=\"width:100%\"\/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\tdata-wp-bind--aria-label=\"state.thisImage.triggerButtonAriaLabel\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"state.thisImage.buttonRight\"\n\t\t\tdata-wp-style--top=\"state.thisImage.buttonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><figcaption class=\"wp-element-caption\">This is what it looks like on my machine: the NetworkManager dispatcher mounts shares automatically as soon as it finds the right server on the network or connects via Wi-Fi to a specific SSID.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">So you can rebuild all of this right away, here is the complete set of files. I replaced the example names from my own setup (a Samba server called bender, WebDAV via tuxedo) with generic placeholders. You&#8217;ll of course have to adapt them to your own environment.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\/etc\/fstab<\/strong>: the two shares, one Samba, one WebDAV:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/server\/home \/mnt\/server\/home cifs credentials=\/etc\/samba\/server.credentials,rw,uid=1000,gid=100,noauto,_netdev,x-systemd.mount-timeout=15s,soft 0 0\n\/\/server\/data \/mnt\/server\/data cifs credentials=\/etc\/samba\/server.credentials,rw,uid=1000,gid=100,noauto,_netdev,x-systemd.mount-timeout=15s,soft 0 0\nhttps:\/\/cloud.example.com\/remote.php\/dav\/files\/user \/mnt\/webdav davfs noauto,_netdev,uid=1000,gid=100,x-systemd.automount,x-systemd.idle-timeout=300,x-systemd.mount-timeout=15s 0 0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The fstab only references the credentials; they are stored in separate files, if only so the password doesn&#8217;t sit in plain text in a file that everyone can read.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\/etc\/samba\/server.credentials<\/strong>: credentials for the Samba share:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>username=maxmuster\npassword=GeheimesPasswort123<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\/etc\/davfs2\/secrets<\/strong>: credentials for the WebDAV share. Here the mount point is referenced instead of an option in the fstab:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/mnt\/webdav maxmuster GeheimesPasswort123<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Both files belong to root and must not be readable by anyone else:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sudo chmod 600 \/etc\/samba\/server.credentials \/etc\/davfs2\/secrets\n$ sudo chown root:root \/etc\/samba\/server.credentials \/etc\/davfs2\/secrets<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\/etc\/NetworkManager\/dispatcher.d\/lib\/mount-helpers.sh<\/strong>: the shared library that all the other scripts source:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\nlog() {\n    &amp;#91;&amp;#91; &quot;$DEBUG&quot; == &quot;1&quot; ]] &amp;&amp; logger -t &quot;$LOG_TAG&quot; -- &quot;$1&quot;\n}\n\nrun_async() {\n    ( &quot;$@&quot; ) &amp;&gt;\/dev\/null &amp;\n    disown\n}\n\ntcp_reachable() {\n    local HOST=&quot;$1&quot;\n    local PORT=&quot;$2&quot;\n    local TIMEOUT_SECONDS=&quot;${3:-2}&quot;\n    timeout &quot;${TIMEOUT_SECONDS}s&quot; bash -c &quot;echo &gt;\/dev\/tcp\/${HOST}\/${PORT}&quot; &amp;&gt;\/dev\/null\n}\n\nmount_with_retry() {\n    local MOUNT=&quot;$1&quot;\n    local MAX_ATTEMPTS=&quot;${2:-3}&quot;\n    local SLEEP_SECONDS=&quot;${3:-2}&quot;\n    local MOUNT_TIMEOUT=&quot;${4:-10}&quot;\n\n    if mountpoint -q &quot;$MOUNT&quot;; then\n        log &quot;$MOUNT ist bereits eingeh\u00e4ngt&quot;\n        return 0\n    fi\n\n    log &quot;H\u00e4nge $MOUNT ein&quot;\n\n    local OUT ATTEMPT\n    for ((ATTEMPT = 1; ATTEMPT &lt;= MAX_ATTEMPTS; ATTEMPT++)); do\n        if OUT=$(timeout &quot;${MOUNT_TIMEOUT}s&quot; mount &quot;$MOUNT&quot; 2&gt;&amp;1); then\n            log &quot;$MOUNT erfolgreich eingeh\u00e4ngt&quot;\n            return 0\n        fi\n        log &quot;Versuch $ATTEMPT\/$MAX_ATTEMPTS f\u00fcr $MOUNT fehlgeschlagen (evtl. Timeout nach ${MOUNT_TIMEOUT}s): $OUT&quot;\n        &amp;#91;&amp;#91; &quot;$ATTEMPT&quot; -lt &quot;$MAX_ATTEMPTS&quot; ]] &amp;&amp; sleep &quot;$SLEEP_SECONDS&quot;\n    done\n\n    log &quot;Alle Versuche f\u00fcr $MOUNT fehlgeschlagen&quot;\n    return 1\n}\n\numount_with_fallback() {\n    local MOUNT=&quot;$1&quot;\n    local TIMEOUT_SECONDS=&quot;${2:-5}&quot;\n\n    if ! mountpoint -q &quot;$MOUNT&quot;; then\n        log &quot;$MOUNT ist nicht eingeh\u00e4ngt&quot;\n        return 0\n    fi\n\n    log &quot;H\u00e4nge $MOUNT aus&quot;\n\n    if timeout &quot;${TIMEOUT_SECONDS}s&quot; umount &quot;$MOUNT&quot;; then\n        log &quot;$MOUNT erfolgreich ausgeh\u00e4ngt&quot;\n        return 0\n    fi\n\n    log &quot;Normaler Unmount von $MOUNT fehlgeschlagen, versuche Lazy Unmount&quot;\n    if umount -l &quot;$MOUNT&quot;; then\n        log &quot;$MOUNT per Lazy Unmount ausgeh\u00e4ngt&quot;\n        return 0\n    fi\n\n    log &quot;$MOUNT konnte nicht ausgeh\u00e4ngt werden&quot;\n    return 1\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\/etc\/NetworkManager\/dispatcher.d\/90-server-mount<\/strong>: Samba share, bound to a specific Wi-Fi SSID or to the wired network:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n\nDEBUG=1\nLOG_TAG=&quot;server-mount&quot;\n\nLIB=&quot;\/etc\/NetworkManager\/dispatcher.d\/lib\/mount-helpers.sh&quot;\nif &amp;#91;&amp;#91; -r &quot;$LIB&quot; ]]; then\n    source &quot;$LIB&quot;\nelse\n    logger -t &quot;$LOG_TAG&quot; -- &quot;Bibliothek $LIB nicht gefunden, breche ab&quot;\n    exit 1\nfi\n\nIFACE=&quot;$1&quot;\nSTATUS=&quot;$2&quot;\n\nMOUNTS=(\n    &quot;\/mnt\/server\/home&quot;\n    &quot;\/mnt\/server\/data&quot;\n)\n\nmount_server() {\n    if ! tcp_reachable server 445 2; then\n        log &quot;server auf Port 445 nicht erreichbar, \u00fcberspringe Mount-Versuch&quot;\n        return 1\n    fi\n\n    for MOUNT in &quot;${MOUNTS&amp;#91;@]}&quot;; do\n        mount_with_retry &quot;$MOUNT&quot; 3 2 10\n    done\n}\n\numount_server() {\n    for MOUNT in &quot;${MOUNTS&amp;#91;@]}&quot;; do\n        umount_with_fallback &quot;$MOUNT&quot; 5\n    done\n}\n\nlog &quot;Dispatcher gestartet: Interface=$IFACE, Status=$STATUS&quot;\n\ncase &quot;$STATUS&quot; in\n    up)\n        if &amp;#91;&amp;#91; &quot;$IFACE&quot; == wl* ]]; then\n            SSID=$(nmcli -t -f active,ssid dev wifi |\n                awk -F: '$1 == &quot;yes&quot; {print $2; exit}')\n\n            log &quot;Aktive WLAN-SSID: $SSID&quot;\n\n            if &amp;#91;&amp;#91; &quot;$SSID&quot; == &quot;MeinHeimnetz&quot; ]]; then\n                run_async mount_server\n            fi\n\n        elif &amp;#91;&amp;#91; &quot;$IFACE&quot; == enp* || &quot;$IFACE&quot; == eth* ]]; then\n            run_async mount_server\n        fi\n        ;;\n\n    pre-down|vpn-pre-down)\n        log &quot;Netzwerkverbindung wird beendet \u2013 Mounts aush\u00e4ngen&quot;\n        umount_server\n        ;;\nesac\n\nlog &quot;Dispatcher beendet&quot;\nexit 0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\/etc\/NetworkManager\/dispatcher.d\/90-webdav-mount<\/strong>: WebDAV share, which simply requires a reachable cloud server:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n\nDEBUG=1\nLOG_TAG=&quot;webdav-mount&quot;\n\nLIB=&quot;\/etc\/NetworkManager\/dispatcher.d\/lib\/mount-helpers.sh&quot;\nif &amp;#91;&amp;#91; -r &quot;$LIB&quot; ]]; then\n    source &quot;$LIB&quot;\nelse\n    logger -t &quot;$LOG_TAG&quot; -- &quot;Bibliothek $LIB nicht gefunden, breche ab&quot;\n    exit 1\nfi\n\nIFACE=&quot;$1&quot;\nSTATUS=&quot;$2&quot;\n\nMOUNTPOINT=&quot;\/mnt\/webdav&quot;\nHOST=&quot;cloud.example.com&quot;\n\nmount_webdav() {\n    if ! tcp_reachable &quot;$HOST&quot; 443 3; then\n        log &quot;$HOST auf Port 443 nicht erreichbar, \u00fcberspringe Mount-Versuch&quot;\n        return 1\n    fi\n\n    mount_with_retry &quot;$MOUNTPOINT&quot; 3 2 10\n}\n\nlog &quot;Dispatcher gestartet: Interface=$IFACE, Status=$STATUS&quot;\n\ncase &quot;$STATUS&quot; in\n    up)\n        run_async mount_webdav\n        ;;\n\n    pre-down|vpn-pre-down)\n        log &quot;Netzwerkverbindung wird beendet \u2013 Mount aush\u00e4ngen&quot;\n        umount_with_fallback &quot;$MOUNTPOINT&quot; 5\n        ;;\nesac\n\nlog &quot;Dispatcher beendet&quot;\nexit 0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For both scripts you also need the matching symlinks, so that pre-down is guaranteed to fire before the connection is torn down:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sudo ln -s \/etc\/NetworkManager\/dispatcher.d\/90-server-mount \n             \/etc\/NetworkManager\/dispatcher.d\/pre-down.d\/90-server-mount\n$ sudo ln -s \/etc\/NetworkManager\/dispatcher.d\/90-webdav-mount \n             \/etc\/NetworkManager\/dispatcher.d\/pre-down.d\/90-webdav-mount<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\/usr\/local\/bin\/mount-healthcheck.sh<\/strong>: the safety net that checks regularly:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\nDEBUG=1\nLOG_TAG=&quot;mount-healthcheck&quot;\n\nLIB=&quot;\/etc\/NetworkManager\/dispatcher.d\/lib\/mount-helpers.sh&quot;\nif &amp;#91;&amp;#91; -r &quot;$LIB&quot; ]]; then\n    source &quot;$LIB&quot;\nelse\n    logger -t &quot;$LOG_TAG&quot; -- &quot;Bibliothek $LIB nicht gefunden, breche ab&quot;\n    exit 1\nfi\n\nlog &quot;Healthcheck gestartet&quot;\n\nSSID=$(nmcli -t -f active,ssid dev wifi 2&gt;\/dev\/null |\n    awk -F: '$1 == &quot;yes&quot; {print $2; exit}')\n\nif &amp;#91;&amp;#91; &quot;$SSID&quot; == &quot;MeinHeimnetz&quot; ]] || ping -c 1 -W 1 server &gt;\/dev\/null 2&gt;&amp;1; then\n    mount_with_retry &quot;\/mnt\/server\/home&quot; 2 3\n    mount_with_retry &quot;\/mnt\/server\/data&quot; 2 3\nelse\n    log &quot;server im aktuellen Netz nicht erreichbar, \u00fcberspringe&quot;\nfi\n\nmount_with_retry &quot;\/mnt\/webdav&quot; 2 3\n\nlog &quot;Healthcheck beendet&quot;\nexit 0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\/etc\/systemd\/system\/mount-healthcheck.service<\/strong> and <strong>mount-healthcheck.timer<\/strong>: systemd units that trigger the health check script 60 seconds after boot and every 10 minutes after that:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&amp;#91;Unit]\nDescription=Prueft und stellt Netzwerk-Mounts sicher\nAfter=network-online.target\nWants=network-online.target\n\n&amp;#91;Service]\nType=oneshot\nTimeoutStartSec=120\nExecStart=\/usr\/local\/bin\/mount-healthcheck.sh<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>&amp;#91;Unit]\nDescription=Fuehrt den Mount-Healthcheck 60s nach Boot und danach alle 10 Minuten aus\n\n&amp;#91;Timer]\nOnBootSec=60s\nOnUnitActiveSec=10min\nAccuracySec=30s\n\n&amp;#91;Install]\nWantedBy=timers.target<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Don&#8217;t forget to enable it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sudo systemctl daemon-reload\n$ sudo systemctl enable --now mount-healthcheck.timer<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\/usr\/lib\/systemd\/system-sleep\/90-cancel-network-mounts<\/strong>: an optional safety net before suspend:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\ncase &quot;$1\/$2&quot; in\n    pre\/*)\n        logger -t sleep-mount-guard &quot;Breche evtl. laufende Netzwerk-Mount-Versuche vor Suspend ab&quot;\n        pkill -9 -f 'mount.cifs' 2&gt;\/dev\/null\n        pkill -9 -f 'mount.davfs' 2&gt;\/dev\/null\n        ;;\nesac\n\nexit 0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">All scripts have to be executable:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sudo chmod +x \/etc\/NetworkManager\/dispatcher.d\/lib\/mount-helpers.sh \n              \/etc\/NetworkManager\/dispatcher.d\/90-server-mount \n              \/etc\/NetworkManager\/dispatcher.d\/90-webdav-mount \n              \/usr\/local\/bin\/mount-healthcheck.sh \n              \/usr\/lib\/systemd\/system-sleep\/90-cancel-network-mounts<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Long story short<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Automatically mounting network shares via NetworkManager works well, but it is not a five-minute job, as the docs might make you think. If you just put the mount command into an up script, sooner or later one of the three problems will catch up with you, usually exactly when you&#8217;re on the road and have no log at hand.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With _netdev, correct pre-down timing, asynchronous execution and a real reachability test before every mount, you&#8217;re on the safe side. And the pattern carries over one to one to other network-dependent automations, such as VPN connections or backup jobs that should only run on the right network.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>All my mount script was supposed to do was one thing: mount the Samba share when I&#8217;m at home. In the end it broke shutdown, resume and standby for me, and taught me a few things about D-state processes and the Linux freezer.<\/p>\n","protected":false},"author":2,"featured_media":12,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"lui_source_id":45301,"lui_source_hash":"d0ee3ff8cb9aa36e8b72c5cdac70181ae95679c5476de04317f7bbc4fb5e8610","lui_source_translated":"2026-10-03","lui_source_reviewed":true,"lui_via_url":"","lui_via_label":"","lui_source_url":"","lui_source_label":"","footnotes":""},"categories":[2],"tags":[],"class_list":["post-13","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-gnu-linux"],"_links":{"self":[{"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/posts\/13","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/comments?post=13"}],"version-history":[{"count":1,"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/posts\/13\/revisions"}],"predecessor-version":[{"id":47,"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/posts\/13\/revisions\/47"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/media\/12"}],"wp:attachment":[{"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/media?parent=13"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/categories?post=13"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/linuxundich.de\/en\/wp-json\/wp\/v2\/tags?post=13"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}