The remote gateway reboots, comes back within a minute, and the local strongSwan daemon keeps running with the tunnel still down. A cron job that greps for ESTABLISHED and restarts the daemon will eventually paper over that, and it will also interrupt every unrelated tunnel on the machine while possibly missing the one that failed. Recovery belongs to the connection that has to return, expressed in settings that survive a reload. This runbook sets that up for a single connection and proves the result by name.

Retire the global restart, keep its evidence

A daemon restart carries two specific defects: it destroys state for every connection, and it tells you nothing about which event actually failed. The status text you grepped may have come from a different connection, while the required child sat idle because nothing triggered it.

Read the loaded connections and the active associations before changing anything, so you own a before picture to compare against after the peer returns.

swanctl --list-conns
swanctl --list-sas

Put initiation and retries on the returning connection

In a swanctl.conf deployment these settings live at connection and child level rather than in a global policy. The fragment below is an initiator-side example that assumes proposals, authentication and selectors are already correct.

connections {
    branch {
        keyingtries = 0
        dpd_delay = 30s
        children {
            private-lan {
                start_action = trap|start
                dpd_action = restart
            }
        }
    }
}

start_action decides what happens when the child is loaded: start initiates immediately, while trap installs a policy that triggers initiation when matching traffic appears. Writing trap|start needs strongSwan 5.9.6 or later, so check the version before copying the line. dpd_action=restart requests fresh negotiation once the peer is declared dead, and connection-level keyingtries=0 allows further attempts until success or a permanent error. Under IKEv2, dpd_timeout has no effect at all, because ordinary retransmission decides when the peer is unresponsive.

The delay in that example is a starting point rather than a universal deadline. Choose it from how long the peer's reboot takes and how much retransmission you are willing to sit through.

Know which event you are recovering from

Three failures look identical on a dashboard and need different settings. An initial connection that never came up is a configuration or reachability problem. A peer that stopped answering is the DPD case. A child that the peer closed explicitly is a policy event with its own close_action, which DPD never observes. Reading the log around the outage tells you which of the three you have, and the answer changes the fix.

Do not add aggressive re-initiation to a connection that operators close deliberately for maintenance, or the tunnel reappears in the middle of the window. For on-demand designs a retained trap is enough, because matching traffic re-establishes the child without any timer.

Verify by name, not by daemon status

Read the connection and its child by name while the peer is still down, then again once it returns; the difference between those two outputs is the evidence.

swanctl --initiate --child private-lan
swanctl --terminate --ike branch

Then confirm the service rather than the association. A fresh child security association plus a successful request to the private destination behind it is the pair of facts that matters, and a running daemon with a healthy unrelated tunnel proves neither.

If the child never initiates, check that the test traffic matches the configured selectors exactly, including protocol and port. If it initiates and fails, read the authentication or proposal error instead of hiding it behind an endless loop, because keyingtries=0 will happily repeat a permanent rejection forever.

Load the change in a window and re-test

Reloading is safer than restarting the daemon, and it is still not free: replacing or unloading a child can close a started association or drop its trap.

Load during a maintenance window, keep the previous file, and confirm that the connection appears with the settings you intended rather than with defaults.

swanctl --load-conns
swanctl --list-conns

Roll back by restoring the previous file and loading it again, then repeat the same two commands. A tunnel that only returns after a daemon restart has not been repaired, it has been reset.

Makomi ya ndenge moko