Enabling IPv6 on Your LAN Behind a MikroTik: From Delegated Prefix to a Working Client
Your ISP gives you IPv6. Your MikroTik even pulls a prefix from it without complaint. And yet not a single device on your LAN can reach the IPv6 internet, and test-ipv6.com sits at a flat 0/10.
This is the most common IPv6 dead end on RouterOS, and the frustrating part is that the WAN side is usually fine from the start. The blockers are a small set of LAN side gaps in the default config. This is a step by step tutorial that takes you from "the ISP delegates a prefix" all the way to "a client on my LAN has a real IPv6 address and browses over IPv6", in a way that applies to any RouterOS box and any ISP that does DHCPv6 prefix delegation.
How IPv6 reaches your LAN (the model)
Before touching the config, get the model straight, because IPv6 does not work like IPv4.
There is no NAT. Your ISP does not hand you one address to hide a private network behind. It delegates a whole prefix to your router via DHCPv6-PD (Prefix Delegation), commonly a /56 or a /60. A /60 is sixteen /64 subnets; a /56 is 256. You carve one /64 out of that prefix for each LAN segment, and every device gets a real, globally routable address.
Clients then configure themselves with SLAAC (Stateless Address Autoconfiguration): the router advertises the prefix on the LAN through Router Advertisements (RA), and each device builds its own address from it. No DHCP server handing out addresses one by one (though DHCPv6 exists too); the router just announces "here is the prefix, help yourself."
So the full path is four links in a chain:
- The router requests and receives a delegated prefix on the WAN (DHCPv6-PD).
- The router puts a
/64from that prefix on the LAN interface and advertises it (SLAAC). - The firewall permits the LAN to forward IPv6 traffic out.
- The client autoconfigures an address and resolves names over IPv6.
Break any one link and you get the 0/10 symptom. Let us build the chain.
Step 1: Request the prefix on the WAN (DHCPv6-PD)
Add a DHCPv6 client on your WAN interface that asks for both an address and a prefix, and store the prefix in a named pool:
/ipv6 dhcp-client add interface=<wan> request=address,prefix \
pool-name=ipv6-pool pool-prefix-length=64 \
use-interface-duid=yes add-default-route=yes
A couple of these flags matter:
request=address,prefixasks for the delegated prefix (the part you actually need) and optionally a WAN address.add-default-route=yesinstalls the IPv6 default route so the router itself can reach the v6 internet.
Many ISPs do not give the router its own global address on the WAN, only the delegated prefix. That is fine. Add accept-prefix-without-address=yes so the client does not get stuck waiting for a WAN address it will never receive. The router will source its own traffic from the LAN prefix.
Check it came up:
/ipv6 dhcp-client print # status should be "bound"
/ipv6 pool print # ipv6-pool should show your delegated prefix, e.g. 2001:db8:abcd:ef00::/60
If the client sits in searching or the pool is empty, see the global switch in the next step before anything else.
A note on the WAN itself: if your uplink is a GPON SFP "stick" plugged straight into the MikroTik's SFP cage (bypassing your ISP's ONT box entirely), the module has to be registered on the ISP network first, which usually means setting its serial number, PLOAM password and VLAN over a serial console. Getting that console on an SFP ONU module is its own rabbit hole; I wrote a separate GPON SFP serial console and SFP-to-TTL guide for exactly that.
Step 2: Put a /64 on the LAN and advertise it (SLAAC)
Make sure IPv6 is enabled at all
RouterOS has a global IPv6 master switch, and on a lot of devices it ships disabled. If it is off, the entire stack is dead: the pool stays empty and the client never binds.
/ipv6 settings print # check disable-ipv6
/ipv6 settings set disable-ipv6=no
/ipv6 dhcp-client renew [find] # re-trigger the client after enabling
Hand the LAN an address from the pool
This is the SLAAC step. Add an address to the LAN interface that is carved from-pool, with advertise=yes:
/ipv6 address add interface=<lan> from-pool=ipv6-pool advertise=yes
from-pool=ipv6-pool takes one /64 out of your delegated prefix, and advertise=yes is the magic flag: it tells the router to emit Router Advertisements on that interface, which is what triggers SLAAC on the clients. Without advertise=yes you have an address but clients never hear about the prefix.
The neighbor discovery defaults (/ipv6 nd) are usually fine for SLAAC out of the box. If you want to push a DNS resolver over IPv6 you can set advertise-dns=yes, but you do not have to: clients will happily resolve over your existing IPv4 DNS and still connect over IPv6 (normal dual stack).
Step 3: Let the LAN forward IPv6 (the firewall gap that catches everyone)
This is the step most people miss, because it is invisible until you look. The RouterOS default configuration includes an IPv6 firewall rule that drops everything in the forward chain that does not come from the LAN interface list:
/ipv6 firewall filter print
# look for: chain=forward action=drop in-interface-list=!LAN
The catch: by default the LAN interface list often contains only the bridge, not your individual VLAN or LAN interfaces. So traffic your clients try to forward out hits that drop rule and dies. Add your LAN interface to the list:
/interface list member add interface=<lan> list=LAN
This is a frequent source of the "ping6 to a literal address works but nothing else does" confusion, because the router itself can reach v6 while client forwarding is blocked.
The IPv4 vs IPv6 asymmetry worth remembering: on a default MikroTik, IPv4 forwarding is usually not gated on the
LANlist, but IPv6 forwarding is. That is why your IPv4 can work with interfaces missing fromLAN, while IPv6 silently does not. Adding an interface to theLANlist has no effect on IPv4, so the change is safe.
Step 4: Verify on a client
Force the client to re-read the network (toggle Wi-Fi, or renew the lease) so it picks up the new RA, then check three things in order:
# 1. Did the client get a global address (2000::/3), not just a link-local fe80:: one?
ip -6 addr # Linux
ifconfig # macOS
# 2. Can it reach the v6 internet by literal address (tests the network, not DNS)?
ping6 2001:4860:4860::8888 # Google Public DNS
# 3. Does name resolution return IPv6?
dig AAAA www.google.com +short # should return 2001:... addresses
Then load test-ipv6.com in a browser. A healthy dual stack client scores 10/10.
The order matters, because it isolates the layer that is broken: a global address proves SLAAC worked, a literal ping6 proves routing and firewall work, and the AAAA lookup proves DNS is not the problem. Always test with a literal IPv6 address before blaming the router. If ping6 <literal> works but names do not resolve over v6, your problem is the resolver, not the network.
Note: if you run a DNS filter like AdGuard or Pi-hole
There is one resolver side trap that produces a very convincing "IPv6 is broken" symptom while the network is perfect. Some DNS filters have an option to disable resolving of IPv6 addresses (in AdGuard Home it is aaaa_disabled). When it is on, the resolver strips every AAAA record from responses, so every dual stack name silently falls back to IPv4 and anything that requires IPv6 has no address to connect to.
The giveaway is curl -6 example.com connecting to an IPv4-mapped address like ::ffff:142.x.x.x, which is IPv4 wearing an IPv6 costume rather than a real AAAA.
If you use such a filter, check that IPv6 resolution is enabled. In AdGuard Home, set aaaa_disabled: false and restart it, then confirm:
dig AAAA www.google.com @<your-resolver-ip> +short # should return 2001:... addresses
It does not matter if the resolver itself runs on an IPv4-only host. An AAAA record is just data the resolver fetches from upstream (over IPv4) and copies back to the client; the client is the one that opens the IPv6 connection.
Extending to more LAN segments
The recipe is per interface and boringly repeatable. For each VLAN or LAN you want on IPv6:
/ipv6 address add interface=<lan-n> from-pool=ipv6-pool advertise=yes
/interface list member add interface=<lan-n> list=LAN
A /60 gives you sixteen /64s and a /56 gives you 256, so there is plenty to cover every segment you have.
The whole thing is additive and IPv6-only: it does not touch IPv4, so SSH, internet, and everything v4 keep working throughout, and it is fully reversible (disable-ipv6=yes plus removing the addresses and list members).
Once your LAN speaks IPv6, the natural next step is letting the outside reach a service over IPv6, and that is a genuinely different beast, because IPv6 has no NAT and no port forwarding. I cover that in a follow up: exposing a self-hosted Docker service over IPv6, with a real global address routed straight to the container.