Change Guard and Config Health¶
RouterOS applies every command immediately and independently. There is no transaction, no rollback, and no cross-object validation — so it will accept a change that severs the very path you are managing the device over. MikroTik's own documented advice for enabling VLAN filtering is to have a serial console ready.
Three layers address that. This page covers how they behave and how to configure them; the README has the short version.
Change Guard — device-side auto-revert¶
Before applying a protected change:
- The device saves a restore point locally.
- A scheduler is armed to reapply it after a timeout.
- The change is applied.
- Reachability is proven on a brand-new connection — not the one that made the change.
- Confirmed → the scheduler is disarmed and the restore point deleted. Unreachable → nothing is done, and the device restores itself.
The restore point is saved before the scheduler is armed, so a rollback returns a configuration containing neither the scheduler nor the change. It is self-cleaning, and a revert loop is impossible.
Everything runs over the RouterOS API, so it works on devices with no SSH credentials configured.
Covered changes¶
Every write that can take a device off the network runs under Change Guard. Some are also simulated first, so a change predicted to cut management is stopped before it is applied; the rest are protected by auto-revert alone.
| Area | Operations | Predicted? |
|---|---|---|
| Firewall filter | add, edit, enable/disable, move, delete | Yes, in rule order (see Firewall rules) |
| Firewall address lists | add, edit, enable/disable, remove | Yes |
| Bridge | VLAN filtering toggle | Yes |
| Bridge | move ports between bridges, create a bridge with the move, delete an empty bridge | Yes |
| Ports | PVID and tagged/untagged membership | Yes |
| Ports | enable/disable | Yes |
| Ports | MTU, FEC, flow control, speed, PoE | Flagged when the port carries management |
| Bridge VLANs | add, update, delete, copy from another switch | Yes |
| Addressing | IP address add, remove | Yes |
| Routing | static route add, remove | Yes |
| Routing | OSPF, BGP, routing tables, route filters | No |
| Bonding | create, edit, delete | Flagged when a member or the bond carries management |
| Services | management service enable/disable | Yes |
| NAT | add, edit, move, delete | No |
| WireGuard | interface enable/disable, delete | Yes; the tunnel the manager arrives through is read-only, below |
| WireGuard | interface edit, peers | Flagged when the tunnel carries management; the manager's own tunnel is read-only, below |
| Wireless | interface edit, enable/disable, delete; security profile edit, delete | Enable/disable and delete predicted; edits flagged when the interface carries management |
| Guest Wi-Fi | hotspot setup, server enable/disable, remove | Setup flagged when the chosen interface carries management |
"Flagged" means the change can't be simulated, but because it touches something the manager's connection runs through, auto-revert becomes mandatory for it (see When protection is required).
The manager's WireGuard tunnel¶
When the manager reaches a device through WireGuard, that tunnel is left alone from here (#205). It's the WireGuard interface that either holds the address the manager connects to, or carries the device's route back to the manager. IPv4 and IPv6 both count, so an IPv6-only tunnel is found too, and so is a device added by hostname (the address the connection actually reached is the one looked for).
- The WireGuard page marks it Protected, says why, and shows the interface and every peer on it read-only: no switching off, editing or deleting, and no adding peers. The peer carrying the manager's address is marked Manager.
- The API refuses any change to that interface or its peers, including moving another peer onto it, with the reason.
Change it on the device itself (WinBox or the terminal), or move management off the tunnel first. Other WireGuard interfaces work as before, under Change Guard.
Where the device doesn't track connections, the manager can't see its own address there, so the peer carrying it can't be singled out (Maybe manager on every peer). The tunnel is protected either way.
Creating a bond also moves the member ports' VLAN membership onto the bond, and deleting it gives the ports their VLANs back. Before 0.24.44 a port tagged on a VLAN became a bond that wasn't, and that VLAN stopped crossing the link.
Each guarded change takes a few seconds longer than an unguarded one: the device saves a restore point first, and the manager proves it can still connect before disarming.
One change at a time per device¶
While a protected change is being applied or verified, other writes to the same device
are refused with HTTP 409 and code: "device_busy". A revert restores a backup taken
before its change, so a write that slipped in between would be undone with it. Reads,
diagnostics and the manager's own records (location, monitoring) are not affected.
What you see when it fires¶
Verified end to end on a CRS running RouterOS 7.23.3 by deliberately deleting a switch's own management address:
T+0 restore point saved, revert scheduler armed, change applied
T+0s device drops off the network
T+40s manager gives up after 4 fresh connection attempts and reports back
T+120s device restores its own backup and reboots
T+3m device is back, change undone, scheduler and restore point gone
The request returns HTTP 200 with guard.auto_reverting: true and the message
"Contact with the device was lost while applying this change. It is restoring itself and
should come back shortly." — deliberately not an error.
This matters. The most dangerous changes sever the very connection carrying them, so the API call times out even though the change succeeded on the device. Reachability, not the exception, decides the outcome. A change that throws while the device is still reachable is a genuine failure, and the guard is disarmed immediately so nothing reboots for nothing.
The exception is a change that fails partway: a bond half built, a VLAN entry removed but not re-added, a restore that stopped in the middle. The device is then in neither the old state nor the new one, so the revert is left armed and puts it back (#281). A step the device clearly rejected before anything was written is an ordinary failure; a lost reply counts as possibly applied.
The manager also won't keep a change once the revert is within 20 seconds of firing, or past it: cancelling it then would race the revert. It leaves the revert to run and says so. To cancel a revert it disables the scheduler, reads it back disabled, and only then removes it, so a lost reply can't be mistaken for a revert that has already started.
Guard history¶
Every protected change is recorded per device:
| Status | Meaning |
|---|---|
committed |
Change applied, device confirmed reachable, guard disarmed and proven gone |
reverted |
Contact lost; the device was left to restore itself |
failed |
Change rejected by the device; nothing applied, guard disarmed |
uncertain |
Change applied and the device is reachable, but the revert scheduler could not be confirmed removed, or was already gone, so the revert may already have run. The device may restore (or have restored) its previous configuration; the UI gives the time and says which. |
Disarming is only counted once the manager has re-read the device's schedulers and seen
the revert gone. A failed read is a failure, not an empty list: before 0.24.36 a timed-out
read could record committed while the revert stayed armed and later undid the change.
While a revert may still fire, the device stays locked against other protected changes.
Settings¶
Settings → General → Change Guard has all three (admins only; before 0.48.0 they could only be set through the API).
| Setting | Default | Description |
|---|---|---|
change_guard_enabled |
true |
Master switch. When off, routine guarded changes apply with no safety net, and changes that need protection (see below) are refused. |
change_guard_mode |
binary |
See below. |
change_guard_timeout_sec |
120 |
How long the device waits before rescuing itself. |
Choosing a mode:
| Mode | What the revert does | Reboots? | Login needs |
|---|---|---|---|
binary (default) |
Restores a /system backup taken just before the change: the whole configuration, exactly |
Yes | the policy permission (the full group has it) |
script |
Puts back only the menus the change could touch, exactly | No | the write group is enough |
binary puts everything back, whatever the change did, at the cost of a reboot when it fires.
script avoids the reboot (#258). Before the change it reads the menus that kind of change
can touch and writes a restore for exactly those, kept on the device as /system script
entries the revert scheduler runs:
- rows the change edited are set back to their old values, rows it deleted are added back, and rows it created are removed (matched by RouterOS's internal ids, which survive edits);
- firewall filter and NAT are rebuilt in their old order, and only if every rule could be added back;
- the scheduler removes itself first, so a revert runs once.
It covers bridge and port changes (VLANs, PVID, moving ports between bridges, VLAN filtering, bonds), port settings, IP addresses, static routes, services, firewall filter, NAT and address lists. Any other change (bulk commands, firmware, routing protocols, WireGuard, wireless, the hotspot) is protected with a binary restore point instead, even in script mode, and the guard record says so. RouterOS 6 devices always use binary.
Before 0.48.0 script mode saved a full /export and imported it on revert. /import stops at
its first error, and a full export starts by adding objects that already exist, so the revert
stopped on its first lines and restored nothing. If you had set change_guard_mode to script,
your changes were not protected until this release.
Choosing a timeout: it must exceed the time the manager needs to verify reachability — roughly 40 seconds across four connection attempts — with margin for a slow device. Too short and a healthy device reverts a good change; too long and an outage lasts longer than it needs to.
Lockout prediction¶
Rather than blocklisting operations someone once decided were dangerous, the platform reads live device state, resolves how the manager actually reaches the device, simulates the proposed change against that model, and reports any invariant that flips from satisfied to violated.
Two details make the result precise rather than merely cautious:
- The manager's own address, as the device sees it, is read from the device's connection tracking — rather than being treated as unknowable behind NAT. When other hosts (a monitoring tool, WinBox) are connected to the API port too, the manager picks out its own session by its source port; if it can't, it says so and works from the default gateway instead of guessing.
- Only what's needed is read from the device: the connections to its API port, and the routes whose prefix covers the manager's address (the device picks the longest of those, and they always include the default route). A router carrying a full BGP table used to be asked for every route and every tracked connection, which could take minutes; the same is true of the WireGuard page's tunnel check (#313).
- The ingress port is taken from the bridge forwarding table, so it is the port the traffic actually arrives on, not a guess from topology.
What the model follows:
- the address, the interface holding it, and a VLAN interface's parent port, so
disabling
ether1undervlan10is caught; - the bridge and its VLAN table, including a VLAN interface reached through an access port whose PVID is that VLAN (tagged at the CPU, untagged on the wire);
- a bond holding the address, under its VLAN interface, or as the ingress port;
- the port holding the address, or the parent port of its VLAN interface, being moved into a bridge, where it would stop receiving management (#281);
- the route back to an off-subnet manager: the one the device would actually pick (longest prefix, then distance), so a default route elsewhere doesn't count, and a blackhole route counts as no way back;
- the API service and the input firewall chain.
A failed read during the check is never taken as "nothing there": the check fails, and the change then requires auto-revert. VLAN edits are simulated with the same planning code the device write uses, so the prediction and the write can't disagree about which rows change. (Before 0.24.49 a port listed as both tagged and untagged for a VLAN was simulated as both; the write leaves it untagged.)
The resulting warning names the mechanism:
This change is predicted to cut management access to 2GT-NW-100G. Management arrives untagged on
sfp28-1(PVID 1) — the gateway's MAC is learned there — but VLAN 1 has no bridge VLAN entry listingbridge1as an untagged member.
Firewall rules¶
The input chain is evaluated the way RouterOS does it, first match wins, for the connection the manager actually opens: a new TCP connection from its address to the API port, arriving on the interface that holds the management address. Address lists, interface lists and jumps into custom chains are followed. Anything the check can't evaluate (tcp-flags, rate limits, marks, time) counts as "maybe", and a rule that maybe matches makes the answer "can't tell" rather than a guess.
So enabling a staged drop rule with the toggle, moving a drop above the rule that accepts the manager, deleting that accept rule, or removing or disabling the manager's entry in an accepted address list is caught before it is applied. RouterOS's default configuration is understood correctly: it lets LAN traffic reach the end of the chain, so appending a drop-all to it is predicted as a lockout.
Only the IPv4 filter table is checked. Rules in the raw table are not, and a manager connecting over IPv6 isn't affected by the IPv4 filter at all. If the device has connection tracking switched off (RouterOS turns it on only once firewall rules exist), the manager's address isn't known, so rules scoped to a source address count as "can't tell".
Overriding a verdict¶
Deliberately awkward. The API requires confirm_lockout: true in the request body, and
the UI requires typing the device name. The change then runs under Change Guard regardless,
so an override is a decision to rely on auto-revert — not to bypass protection.
When protection is required¶
Auto-revert is mandatory, not best effort, for a change that:
- you confirmed past a lockout warning,
- the prediction flagged as a warning, or
- could not be analysed (the device's state couldn't be read), or
- depends on something the check couldn't evaluate, including a check that failed to run, or a port change on a bridge where the port management arrives on isn't known.
A backup restore, and moving ports into a bridge after confirming that an address on them will stop working, always need it. A restore still runs when Change Guard is turned off in Settings, and says it ran without auto-revert.
A failed read of any part of the device's state counts as "could not be analysed". Before 0.24.44 a failed read was treated as an empty table, which could produce a confident "safe".
If the device can't arm auto-revert for one of these (the API user can't save a backup or
add a scheduler, the flash is full, or Change Guard is turned off), the change is refused
before anything is applied, with HTTP 422 and code: "guard_required". Until 0.24.36 it
ran unprotected and only said so afterwards. Bulk commands with Change Guard ticked follow
the same rule: a device that can't arm it is marked failed, which counts toward
halt-on-failure.
The lockout dialog checks this before you confirm, so it never promises protection the device can't give:
POST /api/devices/:id/change-guard/check -> { "ready": true, "mode": "binary", "reason": null }
The check does what a real guard does, with throwaway names: it saves a restore point and adds a scheduler, then removes both.
A pre-existing violation is reported separately from one your change would cause. That distinction matters: if an invariant is already broken, that check cannot detect a new break, and the verdict says so rather than implying a clean result. A pre-existing warning that your change would make critical counts as caused by the change.
Capability probe¶
POST /api/devices/:id/change-guard/probe
Reports what safety mechanisms a device actually supports, tested on the device rather than assumed. Non-destructive — it creates and removes a harmless scheduler and leaves nothing behind.
It also establishes whether /system history can revert a change without a reboot.
Measured on RouterOS 7.23 over the binary API:
| Command | Result |
|---|---|
/system/history/print |
works |
/system/history/undo |
no such command |
/undo numbers=<id> |
unknown parameter |
/undo |
works — reverts the newest action, no reboot |
Undo is therefore real but untargeted, and it cannot replace auto-revert: a dead-man switch has to fire once the manager has already lost contact, and issuing an undo requires the contact that was just lost. Binary restore remains the default.
Reviewing a change before applying it¶
Most edit forms have a Review changes button next to Save (#255). It's optional: Save works exactly as before, and the review only appears when you ask for it.
Review changes runs the form through the same code as Save, against the live device, but nothing is sent: every write is recorded instead, and nothing is saved in the manager either. A window then shows:
- Changes: each item that would be added, changed or removed. A change lists each setting with its current value and the new one. A removal shows the item being removed. Settings the device already has are listed as "Already set" rather than as changes.
- Commands: the same writes as RouterOS terminal commands, with a Copy button. Items are selected by name, or by their internal id where the menu has no names, never by comment.
- Change Guard's prediction: a warning or a predicted lockout appears at the top, the same verdict Save would give you, without the change being refused.
Passphrases, keys and passwords are shown as (hidden), both as values and in the commands. The window's apply button, labelled like the form's own (Add VLAN, Save Rule), applies through the normal Save, with Change Guard as usual. Close it to go back to the form.
The review is available on: - VLANs: add and edit. - Ports: the port editor (link settings and VLAN role). - IP addresses and routes. - Routing: OSPF instances and areas, BGP connections, route filters, routing tables. - Firewall: filter and NAT rules, and address lists. - Bandwidth queues. - DHCP: servers, pools and static leases. - DNS: settings and static records. - NTP and WireGuard: NTP settings, and WireGuard interfaces and peers. - Wireless: interface settings on one AP.
Bulk commands and templates already show their commands before they run, and so do Config Health fixes.
A preview is recorded in the audit log as PREVIEW followed by the request, so it isn't
mistaken for a change.
Config Health¶
A scheduled, read-only audit for configurations RouterOS accepts without complaint and then quietly fails to honour. Each finding explains what it does to the network, how to fix it, how long it has been present, and links the relevant MikroTik documentation.
Findings appear at the top of the device's Config tab and in the dashboard's Things to handle.
One-click fixes¶
Findings with one clearly right fix have a Fix it button (#239). It shows the exact change first, then runs it under Change Guard, and the audit runs again afterwards so the finding clears:
| Finding | Fix |
|---|---|
| Spanning tree off on a bridge | /interface/bridge set <bridge> protocol-mode=rstp |
| Bridge running classic STP | /interface/bridge set <bridge> protocol-mode=rstp |
| VLAN interface whose VLAN the bridge is not tagged in | Adds the bridge to that VLAN's tagged ports, creating the entry if there is none. A VLAN that's part of a multi-VLAN entry is left for you to change on the device |
The others have no button, and say why: most have more than one reasonable fix (move the address or take the port out of the bridge? lower the MTU or raise the L2 MTU?), and device-mode can only be changed at the device. Their advice is unchanged.
What it checks¶
| Finding | Consequence |
|---|---|
| IP address on a bridge slave port | The address is served by the bridge, or not at all |
| VLAN interface on a bridge slave port | The interface receives nothing; management may be unreachable |
| VLAN interface added as a bridge port | Forwarding loop; STP ports flap |
| Bond slave that is also a bridge port | Undefined forwarding path; the bond silently loses members |
| VLAN interface whose VLAN the bridge is not tagged in | The interface is up and carries nothing |
| Management surviving only on a dynamic VLAN entry | Works today; disappears the moment a port admits only tagged frames |
| Port in no VLAN on a filtering bridge | Links up, then every frame is dropped |
| Multi-VLAN bridge entry with untagged ports | Ambiguous; RouterOS warns and applies it anyway |
PVID next to frame-type=admit-only-vlan-tagged |
The PVID is stored but never applied |
| Several bridges competing for hardware offload | One silently falls back to CPU forwarding |
| Routing between VLANs on the CPU of a switch chip that can do it in hardware | Inter-VLAN traffic runs at a fraction of the ports' speed; see Routing between VLANs in hardware |
| MTU above L2MTU | Large frames dropped without error |
| Duplicate address on two interfaces | Only one can answer; which is not stated by the config |
| Ingress port would reject the frames management arrives in | admit-only-vlan-tagged on a port carrying untagged management drops it at ingress, while every VLAN row still lists the port |
| Spanning tree off on a bridge with several active ports | Nothing stops a loop; the second cable someone plugs in becomes a broadcast storm |
| Bridge running classic STP rather than RSTP | Convergence takes tens of seconds where RSTP takes under one |
| Spanning tree off with switches on two or more ports (critical) | One stray cable between those switches is a loop nothing here will break |
| Spanning-tree root chosen by lowest MAC (default priority on every bridge) | Add or replace a switch and the root can move to it; the tree reconverges around a box nobody chose |
| An access point is the spanning-tree root over other switches | Its reboot or link flap reconverges the whole tree |
| Device-mode blocks the scheduler | Change Guard can't arm its automatic undo, so changes that could cut off management are refused |
| Device-mode blocks bandwidth-test, sniffer, hotspot or fetch | The bandwidth test, packet capture, Guest WiFi wizard or adopting through this device fail with "not allowed by device-mode" |
| RouterOS has flagged the device | RouterOS found configuration it considers suspicious; device-mode can't change until it's cleared |
Changing device-mode needs someone at the device: RouterOS asks for a button press or power-cycle to confirm.
Settings¶
| Setting | Default | Description |
|---|---|---|
config_health_enabled |
true |
Runs the standing audit |
config_health_interval_min |
60 |
Cadence per device — one read-only snapshot over the API |
Both are under Settings → General → Config Health. Before 0.24.37 the scheduler never read them, so the audit always ran hourly whatever they said.
A note on false positives¶
Rules are calibrated against real hardware rather than documentation alone. For example,
RouterOS 7 relocates an address configured on a bridge slave port onto the bridge itself
(actual-interface), so that stock configuration works — and is reported as informational
rather than critical. Similarly, a port that belongs to no VLAN is only reported when it
is actually active, because an unplugged port carrying no configuration is not a fault.