The interface is listed in the namespace you wanted, the application is bound to it, and no tunnel traffic ever appears. This is the signature of a Linux kernel WireGuard device that was created in one namespace and moved to another: the interface moved and its encrypted UDP socket did not. Both have to live in the right place before the inner traffic matters. The check is quick once you know to look at two contexts instead of one.

The symptom: right namespace, no packets

Inside the namespace, the tunnel looks healthy. The device exists, addresses are assigned, and the peer configuration is present. Outside, the expected UDP exchange never shows up on the interface you were watching, and the far side reports no handshake.

The reason is that the application's inner packets reach the moved device, and the device tries to send them through a socket that belongs to a different network context. The underlay route it uses, and every firewall rule that applies to it, are the ones in the namespace where the device was created.

That is not a bug in the move, and it is not a misconfiguration of the peer. It is what the kernel implementation does, and the namespace documentation for WireGuard describes the creation rule directly.

Why the socket has a birthplace

A WireGuard device owns one UDP socket for its encrypted transport, and that socket is opened in the namespace where the device is created. Moving the interface with a link command relocates the visible device while leaving the transport where it started. The two objects then have different owners.

The consequence is easy to miss because the halves are inspected in different places. The inner route is decided in the namespace where the application runs, and the outer route is decided where the socket lives. Checking only one of them produces a confident, wrong conclusion. Checking both usually ends the investigation in a single pass.

Create it where you want the socket

The fix is to create the device inside the target namespace rather than creating it outside and moving it. Assuming an approved namespace already exists and the name is unused:

ip netns exec vpn-apps ip link add wg-demo type wireguard
ip netns exec vpn-apps wg show

Both the interface and its socket now belong to that context, so the underlay route and the applicable filter rules are the ones configured there. If the design needs the cleartext side elsewhere, put the underlying interface in the namespace first and create the tunnel after it. Order is part of the design, not an implementation detail.

Treat these as network administration commands, not as a recipe. They create a device and show state; keys, peers, addresses and routes still have to come from an approved configuration, and an existing production interface should never be substituted into an example.

Read both sides of the path

Once the device is in the right place, verify the two decisions separately. Start with the inner lookup from the application's context, then check the outer path from the context that owns the socket:

ip netns exec vpn-apps ip route get 10.30.0.9
ip netns exec vpn-apps wg show
tcpdump -ni any udp port 51820

If the inner lookup resolves through the tunnel and the outer capture shows the encrypted exchange, both halves agree. If the capture is empty, the socket is still somewhere else, or the underlay route in its namespace does not reach the endpoint.

One more check belongs in the record: how the device is created after a restart. A manager that recreates the interface with a different creation context changes the socket's birthplace. A manual move that worked once is not evidence about the managed path.

Allakkat attuumassuteqartut