⚠️ Vibe coded — use with caution. This project was built entirely through AI-assisted vibe coding and has not been audited. The implementation may contain subtle bugs or edge cases that were never considered. Review carefully and use at your own risk.
OVH gives you a single public IPv4 (bound to your server's MAC) and an IPv6 /64 (here
2001:db8:1234:5678::/64). Neither an extra IPv4 subnet nor the /64 is routed to you — OVH filters
IPv4 by MAC, and treats the /64 as on-link on the host's WAN interface, resolved per-address via NDP.
So guests get network in two independent ways:
- IPv4 — private NAT. OVH won't route extra IPv4 to guest MACs, so guests live on a private
subnet (
10.10.10.0/24) behind the host's public IP via MASQUERADE. A DHCP server (dnsmasq) hands out those addresses. - IPv6 — public SLAAC. The /64 is publicly routable. radvd advertises it on the bridge so VMs auto-configure a public address via SLAAC; ndppd answers NDP on the WAN so the outside world can reach them.
- radvd without ndppd: VMs get v6 addresses but return traffic is dropped by the OVH gateway — it won't actually work.
- No DHCP server: guests with
ip=dhcpnever get an IPv4 address. This is the single most common gotcha.
A stock OVH Proxmox install puts the public IP on vmbr0 and bridges vmbr0 to the physical
NIC (bridge-ports enp1s0f0). This guide needs the opposite layout:
- Public IPv4/IPv6 live directly on the physical NIC (
enp1s0f0). vmbr0is a pure internal bridge (bridge-ports none) that guests attach to — it carries the private NAT gateway (10.10.10.1) and the IPv6 prefix gateway (::ffff/64).
If you are on the stock bridged layout, you must convert to this first (see step 1). Guests always
attach to vmbr0.
┌─ IPv6: radvd advertises the /64 → VM SLAACs a PUBLIC address
OVH gw ──NDP──▶ enp1s0f0 (WAN) ──forward──▶ vmbr0 ─────────────────────────────▶ guest
└ ndppd replies for /64 (10.10.10.1, ::ffff/64)
└─ IPv4: MASQUERADE + dnsmasq DHCP → VM gets 10.10.10.x (private)
Substitute for your setup: WAN NIC enp1s0f0, public v4 203.0.113.10/24, prefix
2001:db8:1234:5678::/64, bridge vmbr0, NAT subnet 10.10.10.0/24.
⚠️ This step can drop your SSH. Moving the public IP from the bridge to the physical NIC changes the default route. Do it with OVH KVM/IPMI (or the rescue console) available, and back up first:cp /etc/network/interfaces /etc/network/interfaces.bak.
Target file (public IPs on the NIC, vmbr0 internal with NAT + SLAAC gateway):
auto lo
iface lo inet loopback
# --- WAN: public IPs live directly on the physical NIC ---
auto enp1s0f0
iface enp1s0f0 inet static
address 203.0.113.10/24
gateway 203.0.113.254
hwaddress aa:bb:cc:dd:ee:ff # keep the OVH-registered MAC of this server
iface enp1s0f0 inet6 static
address 2001:db8:1234:5678::1/128
gateway 2001:db8:1234:56ff:ff:ff:ff:ff # OVH's v6 gateway (see the OVH panel)
# --- vmbr0: internal bridge for guests (NAT IPv4 + SLAAC IPv6) ---
auto vmbr0
iface vmbr0 inet static
address 10.10.10.1/24
bridge-ports none
bridge-stp off
bridge-fd 0
post-up sysctl -w net.ipv4.ip_forward=1
post-up iptables -t nat -A POSTROUTING -s 10.10.10.0/24 ! -d 10.10.10.0/24 -o enp1s0f0 -j MASQUERADE
post-down iptables -t nat -D POSTROUTING -s 10.10.10.0/24 ! -d 10.10.10.0/24 -o enp1s0f0 -j MASQUERADE
iface vmbr0 inet6 static
address 2001:db8:1234:5678::ffff/64
post-up sysctl -w net.ipv6.conf.all.forwarding=1
Apply:
ifreload -aNotes:
hwaddressmust stay the MAC OVH registered for the server, otherwise OVH's gateway drops your traffic. On a stock install it's the physical NIC's own MAC (whatvmbr0was spoofing before).! -d 10.10.10.0/24skips NAT for guest-to-guest traffic inside the subnet.- Both
net.ipv4.ip_forward(for NAT) andnet.ipv6.conf.all.forwarding(for IPv6 routing) are set here viapost-up, so they persist across reboots.
apt install -y ndppd
cat > /etc/ndppd.conf <<'EOF'
route-ttl 30000
proxy enp1s0f0 {
router yes
timeout 500
ttl 30000
rule 2001:db8:1234:5678::/64 {
static
}
}
EOF
systemctl enable --now ndppdndppd ships as a SysV init script, not a native systemd unit.
systemctl enable --now ndppdenables it at boot but thesystemd-sysv-installshim often does not actually start the running process — you'll seeenabledbutinactive. Verify and start it explicitly if needed:systemctl is-active ndppd # if this prints "inactive": /etc/init.d/ndppd start systemctl is-active ndppd # should now be "active"
proxymust be the WAN NIC (that's where the NDP queries arrive), not the bridge.staticanswers NDP for the entire /64, which suits SLAAC's dynamic addresses (a harmless "low prefix" warning is expected).
apt install -y radvd
cat > /etc/radvd.conf <<'EOF'
interface vmbr0
{
AdvSendAdvert on;
MinRtrAdvInterval 3;
MaxRtrAdvInterval 10;
prefix 2001:db8:1234:5678::/64
{
AdvOnLink on;
AdvAutonomous on;
AdvRouterAddr on;
};
RDNSS 2001:4860:4860::8888 2606:4700:4700::1111 { };
};
EOF
systemctl enable --now radvdradvd must run on the internal bridge (
vmbr0), never on a bridge that still has a physical port to OVH — otherwise its Router Advertisements leak onto OVH's segment (rogue RA).
Guests configured with ip=dhcp (the Proxmox default) get an IPv4 address only if a DHCP server is
listening on vmbr0. Without this they stay address-less on IPv4 no matter how correct the NAT is.
apt install -y dnsmasq
cat > /etc/dnsmasq.d/vmbr0-nat.conf <<'EOF'
# NAT DHCP for vmbr0 (managed manually)
interface=vmbr0
bind-interfaces
# do NOT run a DNS server (avoid an open resolver on a public IP)
port=0
# --- IPv4 DHCP ---
dhcp-range=10.10.10.50,10.10.10.200,255.255.255.0,12h
dhcp-option=option:router,10.10.10.1
dhcp-option=option:dns-server,1.1.1.1,8.8.8.8
# --- IPv6 RA + DHCPv6 (ULA for stable internal addressing) ---
enable-ra
dhcp-range=fd00:10:10:10::50,fd00:10:10:10::200,slaac,ra-names,64,12h
dhcp-option=option6:dns-server,[2606:4700:4700::1111],[2001:4860:4860::8888]
EOF
systemctl enable --now dnsmasq
port=0makes dnsmasq a DHCP/RA-only server — it does not answer DNS, so you don't expose an open resolver on your public IP.- IPv6 is handled by radvd (public /64 SLAAC). dnsmasq additionally advertises a ULA (
fd00:…) and hands out DNS servers over DHCPv6 — useful for stable internal addressing. Running both RA sources onvmbr0is intentional and coexists fine.- After starting dnsmasq, existing guests may need a lease refresh: reboot the guest, or inside it run
dhclient -r eth0 && dhclient eth0(orifreload/netplan apply).
Host:
ip -br addr show enp1s0f0 # public v4 + v6 on the NIC
ip -br addr show vmbr0 # 10.10.10.1/24 + ::ffff/64
sysctl net.ipv4.ip_forward net.ipv6.conf.all.forwarding # both = 1
systemctl is-active ndppd radvd dnsmasq # all active
ss -lunp | grep ':67' # dnsmasq listening for DHCP on vmbr0
cat /var/lib/misc/dnsmasq.leases # leases handed to guestsGuest (NIC bridged to vmbr0):
ip -4 addr show dev eth0 # should get a 10.10.10.x (private, via DHCP)
ping -c3 1.1.1.1 # IPv4 outbound via NAT
ip -6 addr show dev eth0 # should auto-get a 2001:db8:1234:5678:... PUBLIC address (SLAAC)
ping6 -c3 2001:4860:4860::8888 # IPv6 outbound = whole path (incl. ndppd) worksInbound IPv6: from an external host (not the Proxmox host — it reaches the VM directly over the bridge, bypassing ndppd) ping the VM's public address:
ping6 2001:db8:1234:5678:xxxx:xxxx:xxxx:xxxxAll of the above working = done. A reboot re-check is recommended to confirm it all comes up automatically.
- VM has no IPv4 / no
10.10.10.x: dnsmasq isn't running, isn't bound tovmbr0, or the guest isn't onvmbr0. Checksystemctl is-active dnsmasqandss -lunp | grep :67. - ndppd shows
enabledbutinactive: it's a SysV service —systemctl --nowmay not have started it. Run/etc/init.d/ndppd start. - VM got no public IPv6 address: check the VM NIC is on
vmbr0with SLAAC enabled (accept_ra=1) andsystemctl is-active radvd. - No IPv6 outbound:
net.ipv6.conf.all.forwardingmust be 1 and ndppd must be active. - No IPv6 inbound: test from an external host (the Proxmox host reaches VMs directly, bypassing
ndppd); confirm ndppd
proxyis the WAN NIC and the guest firewall allows ICMPv6.
Security: IPv4 guests are behind NAT, but every guest's IPv6 is a public, directly reachable address — firewall your guests accordingly (guest-side, or Proxmox's
firewall=1on the VM NIC).