VPN stuck: the "modprobe tun" fix that always fails in an LXC
Your VPN never comes up. The preflight check flags a missing /dev/net/tun and tells you to run modprobe tun, but inside a Proxmox container that command returns Module tun not found in /lib/modules/<pve-kernel> — and you're stuck in a loop. The detection is right; the advice is aimed at the wrong kind of machine. The real fix lives on the Proxmox host, not inside the container.
Rather not hand-edit container configs? SparkBox ships with the VPN tunnel and its device requirements pre-wired, so you can skip the host-side plumbing. See the walkthrough →
The 10-second version: In an LXC container you can't load kernel modules. Instead, on the Proxmox host, add a device-allow line and a bind mount for /dev/net/tun to the container's config file, then reboot the container. Ignore the "modprobe tun" instruction — it only works on bare metal or full VMs.
What "VPN stuck" actually means here
A VPN tunnel needs a virtual network interface to route your traffic through. On Linux that interface is provided by a device node at /dev/net/tun (the "TUN" device — a software network adapter the kernel exposes to userspace VPN clients). If that device isn't present, the VPN client can't create its tunnel, so the connection sits in a queue and never establishes. That's the "vpn stuck" / "vpn queue" state.
SparkBox's VPN preflight (the pre-launch health check inside the [VPN Tunnel] stage) correctly notices the device is absent with a test equivalent to [[ ! -c /dev/net/tun ]]. The problem is purely in the advice it prints next.
Why the preflight message is wrong for LXC
As of v1.6.105 the preflight emits a single remediation path: load the tun kernel module with modprobe tun, persist it, and restart. That is exactly right on a physical machine or a full virtual machine — those run their own kernel and have a real /lib/modules tree to load from.
An LXC container (a lightweight Linux container that shares the host's kernel rather than booting its own) has no kernel of its own. There is nothing to modprobe against inside it, which is why you get:
modprobe: FATAL: Module tun not found in /lib/modules/<pve-kernel>
The tun module has to be present on the Proxmox node, and the /dev/net/tun device has to be explicitly passed into the container. The container simply cannot do this to itself. This is a known open issue for the v1.7.x line; until the corrected message ships, follow the steps for your host type below.
First, figure out which host you're on. In the Proxmox web UI, a guest labelled VM (KVM/QEMU) runs its own kernel — use Path A. A guest labelled CT or Container is an LXC — use Path B. Bare-metal and other hypervisors (VMware, VirtualBox) follow Path A.
Path A — bare metal or a full VM (the message actually works)
If you're not in an LXC, the printed advice is correct. Run these on the machine SparkBox is installed on, as root or with sudo:
- Load the module now:
modprobe tun - Make it survive reboots by adding it to the modules loaded at boot:
echo tun >> /etc/modules - Confirm the device exists:
You should see a line beginning withls -l /dev/net/tunc(a character device). - Restart the VPN stage and re-run the preflight. The tunnel should now establish instead of queuing.
If modprobe tun succeeds but /dev/net/tun still doesn't appear, reboot the machine once — some setups only create the device node after a clean boot with the module in /etc/modules.
Path B — Proxmox LXC (the real fix)
You'll do this work on the Proxmox host, not inside the container. You need two things: the tun module loaded on the node, and the device passed through to the container.
1. Make sure the node has the tun module
SSH into the Proxmox host (or use the node shell in the web UI) and check:
lsmod | grep tun
If you get a line back, you're set. If it's empty, load and persist it on the host:
modprobe tun
echo tun >> /etc/modules
2. Find your container's ID and edit its config
Each LXC has a numeric CTID (visible in the Proxmox UI, e.g. 101). Its configuration lives on the host at /etc/pve/lxc/<CTID>.conf. Open it in an editor:
nano /etc/pve/lxc/101.conf
Replace 101 with your actual CTID.
3. Add the TUN passthrough lines
Add these two lines to the bottom of the file:
lxc.cgroup2.device.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
What they do, in plain terms:
- The first line grants the container permission to use the TUN character device (major
10, minor200is the fixed identity of/dev/net/tunon Linux). - The second line bind-mounts the host's device node into the container at the same path, so the VPN client can find it.
create=filetells LXC to create the mount point if it doesn't exist yet.
Gotcha: the mount target is written dev/net/tun with no leading slash. That's not a typo — LXC mount entries are relative to the container root. A leading slash there is a common reason this step silently fails.
4. Restart the container and verify
pct stop 101
pct start 101
Then enter the container and confirm the device is now present:
pct enter 101
ls -l /dev/net/tun
You should see the character device. Re-run the SparkBox VPN preflight — it will pass, and the tunnel will move out of the queue and connect.
Unprivileged containers: if your LXC is unprivileged (the Proxmox default for new containers), the passthrough above is still the correct approach. If the device appears but the VPN client reports a permission error opening it, that points to container privilege/ownership rather than the missing device — a separate issue from the "vpn stuck" symptom this guide covers.
Frequently asked
Why does modprobe tun fail inside my container?
An LXC container shares the kernel of its Proxmox host, so it has no /lib/modules tree of its own to load from. The tun module must be loaded on the host and the device passed in from there. Running modprobe tun inside the container will always return Module tun not found.
How do I know if I'm on bare metal, a VM, or an LXC?
If you installed on a physical machine or a full virtual machine (KVM/QEMU, VMware, VirtualBox), the modprobe path works — use Path A. If your Proxmox summary calls the guest a "CT" or "Container" rather than a "VM", you're in an LXC and need the host-side passthrough in Path B.
Do I need to run modprobe tun on the Proxmox host too?
Usually the tun module is already loaded on the node. If lsmod | grep tun shows nothing on the host, load it with modprobe tun and persist it by adding tun to /etc/modules on the host, then add the device passthrough lines to the container config.
Is this a SparkBox bug?
The detection is correct — the preflight really does see that /dev/net/tun is missing. The remediation text is what's wrong for LXC: it only prints the bare-metal fix. This is tracked as an open issue for the v1.7.x line. Until it ships, use the Path B steps above.
Skip the container plumbing
SparkBox ships the VPN tunnel with its device requirements pre-wired, so a healthy install won't leave you queued behind a missing TUN device in the first place.