Putting IPv6 on my home network, finally
I had been putting off IPv6 at home for the laziest reason: things worked. My ISP has offered a /56 for years. My router supports it. I just never got around to enabling it. This past weekend I did, and here is what I learned.
Prerequisites that surprised me
My ISP’s CPE does IPv6 prefix delegation out of the box, but only if you ask for it. The setting was buried two menus deep in “WAN settings” under “DHCPv6 client mode”. I set it to “request PD” with a hint of /56. The CPE came back with 2001:db8:cafe:4e00::/56. Good start.
From there I wanted my own router (an x86 box running OPNsense) to get a delegated prefix, and distribute a /64 to each internal VLAN. The dance is:
- WAN: DHCPv6 client requesting prefix.
- LAN, IoT, Guest, Lab VLANs: Track Interface with prefix IDs 0, 1, 2, 3 respectively.
- DHCPv6 server optional if you want stable addresses, otherwise SLAAC.
My initial OPNsense config was:
WAN:
IPv6 Configuration Type: DHCPv6
DHCPv6 Prefix Delegation size: 56
LAN:
IPv6 Configuration Type: Track Interface
Track IPv6 Interface: WAN
IPv6 Prefix ID: 0
After saving, my laptop got 2001:db8:cafe:4e00:e8cf:bdff:fed6:1234 from SLAAC and a gateway from router advertisements. ping google.com -6 worked. Neat.
Where it got bumpy
The lab VLAN hosts some services that I want reachable by hostname. Under IPv4 I run dnsmasq on my router for that. dnsmasq happily serves AAAA records, but my homelab services were behind static IPv4 addresses, and I did not want to statically assign IPv6 addresses because SLAAC gives them new addresses regularly through privacy extensions.
I had two options:
- Assign stable addresses via DHCPv6 with known host identifiers.
- Disable privacy extensions on servers and rely on the EUI-64 derived address.
I picked (1) because privacy extensions are reasonable defaults on clients. My DHCPv6 config on OPNsense reserves specific addresses for specific DUIDs. Finding DUIDs is tedious; every server gave its DUID differently. On Debian bookworm:
cat /var/lib/dhcpcd/*.lease | grep -i duid
# Or with systemd-networkd:
cat /var/lib/systemd/network/state*.* | grep -i duid
I gave each homelab box a deterministic 2001:db8:cafe:4e03::N/64 and updated my dnsmasq to serve both A and AAAA records.
The device that refused
A small hardware thing I will not name here (a video camera that came with my alarm system) would not speak IPv6 at all, even though its docs claimed to. It got a SLAAC address, got RAs, and then silently used IPv4 for everything. Fine. I stopped caring. Not every device is going to move, and the answer “use dual stack” is fine on a home network that speaks both.
A bigger surprise was my Synology. DSM 7 speaks IPv6 everywhere, but the Samba module needed a config change to bind to both stacks:
interfaces = bond0
bind interfaces only = yes
smb ports = 445
That bound to IPv4 only. I had to add:
interfaces = bond0 ipv6.*
Or more accurately use the DSM web UI to enable “IPv6 service binding”. Took me an hour to figure that out because the error messages on the client side were unhelpful.
Firewall, the scary part
IPv4 NAT provides accidental defense. Without NAT, every host on my /64 is reachable from the internet by its address unless my firewall says otherwise. My firewall rules under OPNsense need to explicitly drop inbound on all of them. The default rule set already does this, but I checked:
pass in on LAN from LAN_net to any
pass in on LAN from LAN_net to (LAN address)
block in on WAN from any to any
And I added a rule to allow ICMPv6 types 1, 2, 3, 4, 128, 129, 133-137 across the firewall. This is required for IPv6 to work. A lot of “IPv6 is broken at my house” stories are people who dropped ICMPv6 wholesale. Don’t do that.
What I gained
- My phone can reach my homelab without any VPN when I am on a network that supports v6 outbound (most of them, these days). I previously used Tailscale for this.
- Per-service addressing. Each VM can have its own address, no more sharing a host with port-forwarded IPv4.
- My grafana dashboards picked up IPv6 transit metrics and I can see how much of my outbound traffic is v6. About 40% once Netflix and YouTube negotiated v6 with my laptop.
What I still use IPv4 for
- Legacy homelab stuff: my older Zigbee hub, the aforementioned camera, some printers.
- Everything going to my ISP DNS, because I have not bothered moving that.
- Port forwards to my public home service (a jellyfin). I could do port forwarding in v6 too but NAT4 still works so why change.
Reflection
IPv6 at home took me maybe four hours total, spread over a weekend. The hardest parts were (a) remembering to allow ICMPv6 and (b) getting DHCPv6 reservations to work with each server’s weird DUID story. SLAAC is fine for clients. Servers want stable addresses; use DHCPv6 or static.
If your ISP offers PD and you have any router newer than 2016, you can probably do this today. Related: see my post on WireGuard vs Tailscale for what this replaced on my phone.