Your tunnel worked yesterday, the machine rebooted after an update, and now the interface will not come up. On a system where WireGuard is built as an external module, the usual cause is that the module on disk belongs to a different kernel release than the one now running. Headers with a similar name on the same machine prove nothing about that match. The release string is the only thing that decides it.

Running kernel or installed kernel

An update installs a new kernel package and leaves the old one on disk until the machine restarts. After the restart the running release changes, and any module built against the previous one becomes unusable even though its files are still there. That gap explains the familiar report of a machine that was fine before a reboot and broken after it, with no configuration change in between. It also explains why reinstalling headers sometimes appears to fix the problem and sometimes does not.

The header package you install may correspond to the newest kernel package rather than to the kernel that actually booted, and nothing in the package name distinguishes those cases; only the release string does. Check what is running before you check what is installed.

Three checks before you rebuild

The first check is which kernel is running. The second is whether a module for that release exists and can be found. The third is whether the kernel has already loaded it. A fourth question, whether the interface can be created, tests privileges rather than the module, so it belongs after the rebuild rather than in this list.

The commands below answer the first three without changing anything. Read each result before moving on, because every answer rules out a different repair.

uname -r
ls -l /lib/modules/$(uname -r)/build
modinfo wireguard
lsmod | grep wireguard

If the second line reports that the path does not exist, the build environment for the running kernel is missing and nothing you compile will match it. If modinfo fails while the path exists, the module may not be installed at all, which is normal on kernels that ship the implementation in-tree. If modinfo succeeds and lsmod shows nothing, the module exists but is not loaded, which is a load-time problem rather than a build one. Each outcome sends you somewhere different, which is why the reading matters more than the commands.

Build against the running release

An external module compiles against a prepared kernel tree, and the kernel's external-module guide describes using the build directory that belongs to the running kernel rather than whichever directory carries the highest-looking version number. That build directory is a symlink maintained by the kernel package, so it points at the tree for the release currently booted. The indirection is the point: it removes a choice you would otherwise get wrong. Naming it explicitly makes the target unambiguous.

One detail matters when the tree was only prepared rather than fully built. The prepare target does not produce the symbol versioning file that some external builds need, so a tree full of headers is not automatically a complete build environment. If a build fails on missing symbols, that file is the first place to look.

sudo make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
sudo make -C /lib/modules/$(uname -r)/build M=$(pwd) modules_install
sudo depmod -a

When packaged headers lag

On distributions and single-board computers that package their own kernels, headers can trail the release by days or weeks. The header package can be correct in name and older in content, which produces a build that compiles and then fails at load time rather than during the compile. Mixing sources makes it worse: a distribution kernel with headers from a third-party repository, or a vendor kernel with generic headers, can compile cleanly and still be rejected by the running kernel because the configuration differs. Version numbers on their own do not settle that.

If the machine follows an experimental kernel channel, decide deliberately whether to stay there with matching build artefacts or return to the supported package set. Repeating the update and hoping the pieces align is not a plan, because the same mismatch reproduces every time.

Verify the interface after the rebuild

After a successful install, creating the interface is the test that matters, followed by reading what the kernel actually loaded and what the log says if it refused. A module that loads and an interface that cannot be created are different failures with different causes. The three commands below separate them.

sudo ip link add wg9 type wireguard
lsmod | grep wireguard
sudo dmesg | tail -5

If the module refuses to load, the kernel log names the reason, and a version mismatch is only one of the possibilities. Read that line before rebuilding again, because a second identical build spends the same ten minutes and produces the same refusal. Remove the throwaway interface afterwards so the next test starts from a clean state.

ບົດຄວາມທີ່ກ່ຽວຂ້ອງ