MikroTik DHCP Relay: Centralized IP Management Across VLANs
1. Why relay exists: the broadcast domain problem
DHCP Discover messages are broadcasts. A broadcast does not cross a Layer 3 boundary, so a DHCP server only ever hears clients that share its broadcast domain. In a flat network that is fine. In a segmented network with one VLAN per department, floor, or tenant, it means either running a DHCP server per VLAN or finding a way to forward the broadcast across the router.
RFC 2131 defines the forwarding mechanism: a relay agent listens on the client-facing segment, converts the broadcast into a unicast packet addressed to a remote DHCP server, and inserts the address of the client-facing interface into the giaddr (gateway IP address) field. The server replies to giaddr, and the relay delivers the reply to the client. RouterOS implements this as /ip/dhcp-relay. The official manual describes its purpose directly: use it when one central DHCP server serves several networks it is not directly connected to.
The architectural payoff is real: one server, one set of pools, one lease database, one place to change DNS or NTP options. The operational cost is that every VLAN becomes a relay configuration object, and the relay must be reachable from the server in both directions.
2. How RouterOS relay works: giaddr is the whole mechanism
RouterOS relay is deliberately simple, and understanding its exact behaviour prevents most misconfigurations.
- The relay does not choose a server. It forwards every request to every address listed in
dhcp-server. The official manual states it plainly: the relay “forwards each request to every DHCP server listed indhcp-server; it does not choose one of them”. Any server-selection logic lives on the server side, not the relay. - The relay writes
giaddr. It writeslocal-addressinto the gateway address field; iflocal-addressis not set (0.0.0.0), it uses an address of the interface the relay is bound to. This is the single most important value in the whole design. - The server replies to
giaddr. The manual confirms the server “sends its replies to that address and uses it to tell relays apart”. - The relay interface is the client-facing side.
interfaceis mandatory and is defined as the interface “on which the relay listens for DHCP requests from clients”. It is not the uplink toward the server. - New relays are created disabled. The manual is explicit: “New relays are created disabled, so enable them after adding.” This is a very common cause of “I configured it and nothing happened”.
| Property | Role | Design note |
|---|---|---|
interface |
Client-facing interface that receives DHCP broadcasts (mandatory) | For VLANs, use the VLAN interface, not the physical parent. |
dhcp-server |
One or more server IPs; requests go to all of them (mandatory) | Comma-separated list enables forwarding to multiple servers. |
local-address |
Value written into giaddr; 0.0.0.0 auto-selects an interface address |
Set it explicitly per VLAN. Auto-selection is fragile with multiple addresses on an interface. |
add-relay-info |
Enables Option 82 (RFC 3046); default no |
Off by default. Needed for per-port or per-circuit policy. |
relay-info-remote-id |
Custom string for the remote ID sub-option | Only meaningful with add-relay-info=yes. |
delay-threshold |
Forwards only requests whose secs field exceeds the threshold; default none |
Useful for slow-booting clients or PXE; none forwards everything. |
local-address-as-src-ip |
Sources forwarded requests from local-address rather than the transit interface; default no |
RouterOS 7.17+; fixes source-IP mismatches on the server path. |
dhcp-server-vrf |
VRF in which the servers are reached; default main |
Requires return-path handling; see §8. |
3. Architecture: where the relay sits in a VLAN design
There are two common topologies, and they have different failure modes.
3.1 Relay on the Layer 3 gateway (recommended)
The router that terminates the VLANs also runs the relay. Each VLAN has an SVI/VLAN interface with a gateway address; the relay binds to that VLAN interface and uses the gateway address as local-address. The server sits in a separate management or server VLAN.
[Client VLAN 10]──┐
[Client VLAN 20]──┼──▶ [Router: VLAN interfaces + /ip dhcp-relay] ──▶ [Central DHCP server]
[Client VLAN 30]──┘ giaddr per VLAN
This is the cleanest model. The giaddr the server sees is the actual default gateway the client will use, the return path is a connected route, and there is exactly one relay instance per VLAN.
3.2 Relay on an access/distribution switch
The relay runs on a downstream RouterOS device, and the DHCP server lives further upstream. This works, but the local-address must be an address the server can route back to, and any NAT between the two will break giaddr. It also multiplies relay objects: one per VLAN per device.
3.3 The non-negotiable rule
One relay instance per client-facing interface. The relay listens on the interface you bind it to, and the interface property is mandatory. A single relay on a bridge carrying ten VLANs will only serve the bridge interface’s own address, not each VLAN’s subnet. For multiple VLANs, create one relay entry per VLAN interface pointing to the same central server.
4. Configuring relay for multiple VLANs
The following assumes VLAN interfaces already exist and have gateway addresses. If they do not, create them first — relay cannot infer a VLAN’s subnet.
4.1 Create the VLAN interfaces and addresses
/interface vlan add name=vlan10-Clients interface=bridge vlan-id=10
/interface vlan add name=vlan20-Voice interface=bridge vlan-id=20
/interface vlan add name=vlan30-Guest interface=bridge vlan-id=30
/ip address add address=10.10.10.1/24 interface=vlan10-Clients
/ip address add address=10.10.20.1/24 interface=vlan20-Voice
/ip address add address=10.10.30.1/24 interface=vlan30-Guest
4.2 Create one relay per VLAN
/ip dhcp-relay add name=relay-vlan10 interface=vlan10-Clients \
dhcp-server=10.10.99.10 local-address=10.10.10.1 disabled=no \
comment="Clients VLAN -> central DHCP"
/ip dhcp-relay add name=relay-vlan20 interface=vlan20-Voice \
dhcp-server=10.10.99.10 local-address=10.10.20.1 disabled=no \
comment="Voice VLAN -> central DHCP"
/ip dhcp-relay add name=relay-vlan30 interface=vlan30-Guest \
dhcp-server=10.10.99.10 local-address=10.10.30.1 disabled=no \
comment="Guest VLAN -> central DHCP"
Every instance points at the same server but carries a different local-address. That difference is what lets the server select a different pool per VLAN. The official configuration example follows the identical pattern: two relays, Local1-Relay with local-address=192.168.1.1 and Local2-Relay with local-address=192.168.2.1.
4.3 Why explicit local-address beats auto-selection
local-address=0.0.0.0 tells RouterOS to pick an address from the interface. If the VLAN interface has exactly one IPv4 address, the result is predictable. If it has a primary and a secondary address, or an address added later for migration, the chosen value can change and the server-side relay= match silently fails. Always set local-address explicitly on VLAN relay instances.
5. Server-side configuration: the relay= property
A relay without matching server configuration produces requests that arrive and are discarded. On a RouterOS DHCP server, the critical property is relay=.
The official example makes the relationship explicit: two servers run on the interface toward the relay, and the relay property “makes each server answer only the requests relayed from its network, identified by the relay’s address in that network”. The configuration pairs each pool with a server whose relay= equals the corresponding relay’s local-address:
/ip pool add name=Pool-VLAN10 ranges=10.10.10.50-10.10.10.200
/ip pool add name=Pool-VLAN20 ranges=10.10.20.50-10.10.20.200
/ip dhcp-server add name=DHCP-VLAN10 interface=To-Relay \
relay=10.10.10.1 address-pool=Pool-VLAN10
/ip dhcp-server add name=DHCP-VLAN20 interface=To-Relay \
relay=10.10.20.1 address-pool=Pool-VLAN20
/ip dhcp-server network add address=10.10.10.0/24 gateway=10.10.10.1 dns-server=10.10.99.53
/ip dhcp-server network add address=10.10.20.0/24 gateway=10.10.20.1 dns-server=10.10.99.53
The CLI reference defines the relay property precisely:
0.0.0.0(default) — only requests from directly connected clients, no relayed requests.255.255.255.255— requests from any DHCP relay, except those answered by another server that has this relay’s address set.- An IP address — only requests relayed by the DHCP relay with this address, which is the relay’s
local-address.
Without relay= set correctly, the server cannot determine which pool to assign from. This is the second most common relay misconfiguration after forgetting to enable the relay instance.
5.1 What about non-RouterOS servers?
On Windows Server, ISC DHCP, or Kea, the equivalent concept is a scope whose subnet matches the giaddr. The server selects the scope by longest-match against giaddr; no explicit “relay” property is needed. The MikroTik-specific relay= property exists because multiple RouterOS server instances can share one interface, so each needs a discriminator. If you replace a RouterOS server with Windows DHCP, you must ensure the scope for 10.10.10.0/24 exists and that the server has a route back to 10.10.10.1.
6. Option 82: circuit ID and remote ID
Option 82 (RFC 3046) lets the relay attach location metadata to the request so the server can make decisions beyond “which subnet”. RouterOS exposes two properties and a fixed encoding.
With add-relay-info=yes, the relay adds the relay agent information option to forwarded requests. The official manual states: “The circuit ID is the MAC address of the relay interface the request came in on, and the remote ID is the MAC address of the client, or the text set in relay-info-remote-id.” The CLI reference confirms the same encoding.
/ip dhcp-relay set [find name=relay-vlan10] \
add-relay-info=yes relay-info-remote-id="vlan10-clients-floor3"
| Sub-option | RouterOS source | Override |
|---|---|---|
| Circuit ID (1) | MAC address of the relay interface | None — derived from the interface |
| Remote ID (2) | Client MAC by default | relay-info-remote-id string |
On the server side, RouterOS records the values in the lease as active-agent-circuit-id and active-agent-remote-id, and passes them to the lease script. A static lease can match on agent-circuit-id and agent-remote-id even when the MAC differs, and dynamic-lease-identifiers=opt-82 makes the server identify clients by Option 82 alone.
6.1 When Option 82 is worth the complexity
- Per-switch-port policy. Circuit ID identifies the ingress interface, so you can pin a device to a specific address regardless of MAC.
- Static lease by location.
agent-circuit-idmatching survives hardware replacement — the port is the identity, not the NIC. - ISP wholesale architectures. Circuit ID maps a subscriber to a pool or policy.
6.2 Option 82 constraints you must design around
- Circuit ID is a MAC address, not a port name. RouterOS encodes the relay interface MAC, not a human-readable port label. If you need readable identifiers, use
relay-info-remote-idand document the mapping. - Masquerade can interfere. Exclude DHCP from NAT so the relay agent information and
giaddrsurvive the path to the server. - It adds bytes. Option 82 adds roughly 32 bytes; verify path MTU on constrained links.
- Not all servers honour it. Windows DHCP requires explicit configuration to use Option 82 for policy; ISC and Kea support it but need matching expressions.
7. Multiple DHCP servers and redundancy
The dhcp-server property accepts a comma-separated list, and the relay forwards every request to every listed server. The manual is unambiguous: it “does not choose one of them”. This is a race, not a coordinated failover — which has consequences.
/ip dhcp-relay set [find name=relay-vlan10] \
dhcp-server=10.10.99.10,10.10.99.11
| Approach | Behaviour | Risk |
|---|---|---|
| Single server | Simple, deterministic | Server outage = DHCP outage for all VLANs |
| Two relays, one server each | Deterministic per VLAN, but no redundancy within a VLAN | Half the VLANs fail on one server outage |
| One relay, two servers | First response wins | Split-brain lease databases if both are authoritative and unsynchronised |
| Two servers with failover peer | Coordinated pools (ISC/Kea failover) | Requires server-side synchronisation; RouterOS server has no native failover peer |
Architect’s guidance: if you list two RouterOS DHCP servers on one relay, they will both answer and you will get duplicate, inconsistent leases unless the pools are split manually. For genuine redundancy, use an external DHCP server pair with failover configured, or run two relays per VLAN pointing at different servers and accept that only one is active at a time by pool design. Do not treat a comma-separated list as automatic high availability.
8. VRF and routing considerations
If the DHCP server is reachable only inside a VRF, dhcp-server-vrf tells the relay which routing table to use. The CLI reference warns that “when it differs from the VRF of interface, the replies of the server, which are sent to local-address, must be brought into this VRF, for example with a dst-nat rule”.
In practice this means the return path is not automatic: the server replies to giaddr, and that packet must be routed back into the VRF where the relay’s local-address lives. Plan a static route or a dst-nat rule, and test with a packet capture on the server-facing interface before commissioning.
/ip dhcp-relay set [find name=relay-vlan10] dhcp-server-vrf=vrf-servers
VRF relay is a place where “it looks configured” and “it works” diverge; verify both directions.
9. NAT and firewall traps that silently break relay
9.1 Masquerade and giaddr
This is the highest-impact failure mode. If masquerade is active on the uplink toward the DHCP server, it rewrites the packet’s source address and can corrupt the giaddr-based server selection. The server then sees the wrong relay address, selects the wrong scope (or none), and clients get no offer.
Add an accept rule before the masquerade rule to exempt relay traffic:
/ip firewall nat add chain=srcnat protocol=udp src-port=67 dst-port=67 \
action=accept comment="Exempt DHCP relay from masquerade"
# ... then the existing masquerade rule
If local-address-as-src-ip=yes is in use (RouterOS 7.17+), the source is local-address but giaddr is still local-address — NAT still must not rewrite it.
9.2 Firewall filtering UDP 67/68
DHCP uses UDP 67 on the server and 68 on the client. Relay traffic is unicast, so it traverses the forward chain (or the input/output chains on the server itself). Ensure the relay router permits:
- Client → relay: UDP 68 → 67 (broadcast on the client interface)
- Relay → server: UDP 67 → 67 (unicast)
- Server → relay: UDP 67 → 67 (unicast to
local-address) - Relay → client: UDP 67 → 68 (unicast or broadcast)
A drop rule on UDP 67 in the forward chain that was written to “block rogue DHCP servers” will also block legitimate relay traffic. Scope such rules to the client-facing interface and exclude the relay’s own source address.
9.3 Bridge firewall and hardware offload
On bridge-heavy designs, DHCP may be intercepted before the IP firewall sees it, and bridge filter rules can drop it separately. If IP firewall counters show nothing but clients still fail, check the bridge filter and any /interface bridge vlan filtering that could be pruning the VLAN before the relay interface receives it.
10. Verification and troubleshooting
Work from the relay outward, not from the client inward. The relay’s own counters tell you which half of the path is broken.
10.1 Check relay state and counters
/ip dhcp-relay print detail
/ip dhcp-relay monitor [find name=relay-vlan10]
The monitor command shows two read-only counters:
requests— number of client requests the relay forwarded to the servers.responses— number of server replies the relay delivered to clients.
The interpretation is unambiguous:
- requests increasing, responses zero → the server is not answering. Check server-side
relay=, the route back tolocal-address, and NAT. - requests zero → the relay is not hearing clients. Check
disabled=no, the interface binding, VLAN pruning, and bridge filters. - requests and responses both increasing but clients still fail → the reply is not reaching the client. Check the client-facing interface, ARP, and any firewall rule on the return path.
10.2 Confirm the relay is enabled
“New relays are created disabled”. The X flag in print output means disabled; I means the configuration is invalid. Both produce silence.
10.3 Capture both directions
/tool sniffer quick interface=ether-uplink ip-protocol=udp port=67,68
Run this on the server-facing interface during one client lease attempt. You should see the unicast forward to the server and the unicast reply back to local-address. If you see the forward but not the reply, the problem is server-side or NAT. If you see neither, the relay is not forwarding.
10.4 Logs
Relay activity and errors appear under the dhcp topic. Enable it temporarily:
/system logging add topics=dhcp action=memory
/log print where topics~"dhcp"
10.5 Symptom-to-cause table
| Symptom | Most likely cause | Fix |
|---|---|---|
| Clients get no IP at all | Relay disabled (X flag) |
/ip dhcp-relay enable [find name=…] |
| Requests sent, no replies | Server relay= mismatch |
Set relay= equal to the relay’s local-address |
| Wrong pool assigned | Masquerade rewriting the relay path | Exempt UDP 67 from srcnat |
| Relay not forwarding | No route to DHCP server | Add a route or fix the default gateway |
| Works on one VLAN, fails on another | Relay bound to bridge, not VLAN interface | One relay per VLAN interface |
| Server sees wrong source IP | Transit source used instead of relay address | local-address-as-src-ip=yes on 7.17+ |
| Option 82 missing at server | add-relay-info=no or NAT interference |
Enable relay info and exempt NAT |
11. Design pitfalls checklist
- One relay per bridge instead of per VLAN. The most common multi-VLAN mistake. The relay serves the bridge interface’s subnet only.
- Relying on
local-address=0.0.0.0. Auto-selection is unpredictable with multiple addresses on the VLAN interface. Set it explicitly. - Forgetting
relay=on the RouterOS server. Requests arrive and are dropped because no server instance claims thatgiaddr. - Leaving the relay disabled. New objects default to disabled; “configured” is not “active”.
- Masquerade on the server path. Rewrites the relay path and can strip Option 82.
- A firewall drop for UDP 67 that also hits relay traffic. Scope it to the client-facing interface and the relay source.
- Treating a comma-separated server list as HA. It is a race with no lease synchronisation.
- Expecting human-readable circuit IDs. RouterOS uses the interface MAC for circuit ID; use
relay-info-remote-idfor readable labels. - Ignoring the VRF return path.
dhcp-server-vrfconfigures the outbound direction; the reply needs explicit handling. - No packet capture during commissioning. Relay failures are silent; a 30-second sniffer run distinguishes “not forwarding” from “not answering” immediately.
12. Frequently asked questions
Can one MikroTik DHCP relay serve multiple VLANs?
No. Relay instances are interface-bound. You need one relay instance per VLAN interface, each with its own local-address, all pointing at the same central server.
What exactly does local-address do?
It is written into the giaddr field of forwarded requests. The DHCP server sends replies to that address and uses it to select the correct pool and server instance. If left at 0.0.0.0, RouterOS picks an interface address automatically.
Why do my clients get an address from the wrong VLAN pool?
Almost always because the relay path is being rewritten — typically by a masquerade rule on the path to the DHCP server. Exempt UDP 67 from srcnat and verify with a capture that giaddr equals the relay’s local-address.
Does the relay load-balance between two DHCP servers?
No. It forwards every request to every listed server, and the first reply wins. There is no coordination or lease synchronisation between RouterOS server instances, so two authoritative servers can produce conflicting leases.
Is Option 82 enabled by default?
No. add-relay-info defaults to no. You must enable it per relay instance.
What is the circuit ID in RouterOS?
The MAC address of the relay interface the request arrived on — not a port name. Use relay-info-remote-id if you need a readable identifier.
Why does the relay show requests but zero responses?
The server is reachable but not answering. Check the server’s relay= property, the return route to local-address, and whether NAT is rewriting the packet. A capture on the server-facing interface will show whether the request leaves and whether any reply returns.
Does DHCP relay work across a VRF boundary?
It can, but the return path is not automatic. dhcp-server-vrf selects the outbound VRF; replies sent to local-address must be brought back into the correct VRF, for example with a dst-nat rule.
Conclusion
MikroTik DHCP relay is a small feature with a narrow contract: bind to the client-facing interface, put a unique and predictable address in giaddr, forward to a server that has a matching pool, and do not let NAT or the firewall silently rewrite or drop the exchange. The design discipline is to treat local-address as the VLAN’s identity — one VLAN, one relay, one giaddr, one server-side relay= match. Get that relationship right, verify it with counters and a capture during commissioning, and a single centralized DHCP server can serve a segmented network cleanly and predictably.
Check our list of MikroTik guides