The default RouterOS log is a 1000-line ring buffer in RAM. During normal operation that holds a day of history. During the incident you actually care about — a brute-force run, a flapping interface, a DHCP storm — it holds about ninety seconds, and it evaporates entirely on reboot. Which means the log is empty precisely when someone asks “what happened?”
I have sat in more than one post-incident review where the answer to “who changed the firewall rule?” was a shrug, because the change was logged to memory on a router that rebooted twice since. That is not a logging problem, it is a decision you made by accepting defaults.
The fix is structural: logs leave the router, in real time, to a collector that outlives it. RouterOS makes this genuinely easy — the /system logging model is just topics routed to actions — and then gives you two classic ways to ruin it: logging every firewall drop on a WAN interface (drowning), and logging to flash on a device with soldered NAND (killing the hardware). This article avoids both.
The Model: Topics Route to Actions
Every log entry in RouterOS carries one or more topics (system, firewall, account, dhcp, ospf, error, critical, …). A logging rule matches topics and sends entries to an action: memory, disk, echo, email, or remote. Inspect the defaults:
/system logging print# 0 topics=info action=memory# 1 topics=error action=memory# 2 topics=warning action=memory# 3 topics=critical action=echo
/system logging action print# memory: 1000 lines in RAM# disk, echo, remote: preconfigured but mostly unusedTwo rules of thumb before touching anything. Comma-separated topics in one rule are AND (“firewall AND debug”), so to catch multiple topics you add multiple rules. And a ! prefix negates: topics=ospf,!debug logs OSPF but not its debug firehose.
Remote Syslog: The One Non-Negotiable
Configure a remote action pointing at your collector — rsyslog, Graylog, Loki via syslog ingestion, a Vector pipeline, whatever you already run — then route the important topics to it:
# The action: where logs go/system logging action add name=syslog target=remote \ remote=10.0.20.10 remote-port=514 \ src-address=10.0.20.1 syslog-facility=local0
# The rules: what goes there/system logging add topics=info action=syslog/system logging add topics=warning action=syslog/system logging add topics=error action=syslog/system logging add topics=critical action=syslog/system logging add topics=account action=syslog/system logging add topics=firewall action=syslogsrc-address matters on multi-homed routers — pin it so the collector can attribute entries to the right device instead of whatever egress interface won the route lookup.
Classic RouterOS syslog is UDP, RFC 3164-style. Recent 7.x releases grew up considerably here: remote-protocol=tcp (and TLS with check-certificate=yes), syslog-time-format=iso8601 for sane timestamps, remote-log-format=cef for SIEMs that speak Common Event Format, and a vrf property so logging can live in a management VRF. If your fleet runs a current 7.x, prefer at least:
/system logging action set syslog syslog-time-format=iso8601UDP syslog on an unreliable path silently loses exactly the entries generated during network trouble — the ones you wanted. If the collector is across a WAN, use TCP or ship via a local collector per site.
Keep the default memory rules alongside remote. On-box /log print is still your fastest tool during live debugging, and bumping the buffer is free on anything with RAM to spare:
/system logging action set memory memory-lines=10000Disk Target: Know Your Storage First
target=disk writes rotating files onboard:
/system logging action add name=disklog target=disk \ disk-file-name=log disk-lines-per-file=2000 disk-file-count=10Here is the catch: on most RouterBOARD hardware, “disk” is the same small soldered NAND/flash that holds RouterOS itself, with a finite erase-cycle budget and no wear-leveling heroics. Continuous logging to it is a slow-motion hardware failure — I have seen hAPs bricked by years of chatty disk logging. Policy:
- Flash-only devices (hAP, hEX, CRS, most RBs): disk logging for burst debugging only, then turn it off. Remote syslog is the archive.
- Devices with real storage (CCRs with NVMe, or a USB SSD, CHR on a datastore): disk logging to that storage is fine — set
disk-file-name=usb1/logor similar. - Never treat on-box files as your audit trail. They die with the router, and an attacker with admin can delete them.
Firewall Logging Without Drowning
Add log=yes log-prefix= to any firewall rule and matches hit the firewall topic. The mistake is deciding “log all drops” on a WAN interface: internet background radiation runs constant scans, and you will bury real events under thousands of identical lines while burning CPU on a busy box.
Log decisions, not noise. The pattern that works:
# 1. Drops of NEW connections to the router itself - always log, low volume,# high signal (this is your brute-force and scan detector)/ip firewall filter add chain=input connection-state=new action=drop \ in-interface-list=WAN log=yes log-prefix="DROP-INPUT: " \ place-before=[find where chain=input && action=drop && !log]
# 2. Forward drops: log only what should never happen internally,# e.g. lateral movement from guest VLAN toward management/ip firewall filter add chain=forward in-interface=vlan40-guest \ dst-address=10.0.20.0/24 action=drop log=yes log-prefix="GUEST-TO-MGMT: "
# 3. The catch-all forward drop: do NOT log it on internet-facing boxes.# If you must sample it temporarily, add limit=/ip firewall filter add chain=forward action=drop \ log=yes log-prefix="DROP-FWD: " limit=5,10:packetThree details carry the weight here. connection-state=new means one log line per flow instead of per packet. log-prefix is what makes logs greppable and lets the collector route/alert on classes of events — prefix everything. And limit=5,10:packet on a logging rule caps it at a sustained 5 lines/second with a burst of 10 — the closest RouterOS gets to log rate-limiting, and good enough to make even a catch-all safe during a flood.
Verify what you built:
/log print follow where topics~"firewall"# Then from an external host, poke a closed port and watch DROP-INPUT appearWhat to Log for Security Audits
If you need to answer auditors — or just yourself at 3 AM — two topics are mandatory and cheap:
account — every login and logout, with source address and method:
/log print where topics~"account"# user admin logged in from 10.0.20.5 via winbox# login failure for user admin from 203.0.113.7 via sshRepeated login failure lines from one address are your brute-force alarm; alert on them at the collector (three failures from one source in five minutes is a sane threshold).
system — configuration changes, attributed:
/log print where topics~"system" and message~"changed"# filter rule added by admin# item changed by netops-bRouterOS logs that a user changed something and what kind of thing, but not a full before/after diff. Close that gap with scheduled /export snapshots diffed at the collector (the next article in this series covers the export pipeline) — syslog tells you who and when, the export diff tells you exactly what.
If logins go through RADIUS (covered two articles from now), the collector can correlate router account entries with RADIUS accounting for a complete trail.
NetFlow: /ip traffic-flow
Logs record events; NetFlow records conversations. When the question is “what was that host actually talking to at 02:14,” flow data answers it and logs cannot. RouterOS calls it traffic-flow and supports NetFlow v1/v5/v9 and IPFIX:
/ip traffic-flow set enabled=yes interfaces=all \ active-flow-timeout=1m inactive-flow-timeout=15s
/ip traffic-flow target add dst-address=10.0.20.10 port=2055 version=ipfixUse IPFIX (or v9) — v5 cannot represent IPv6. The one setting worth changing from default is active-flow-timeout: the default 30m means a long-lived transfer only exports after half an hour, which makes near-real-time dashboards useless. One minute is the conventional choice; your collector merges the records.
On the collector side, anything from nfdump/nfsen to ElastiFlow to Akvorado works. Mind the cache on busy routers — cache-entries defaults to 4k flows, which a busy edge exceeds easily; raise it and watch the router’s memory.
The payoff is correlation. A DROP-INPUT burst in syslog tells you someone scanned you. The flow record tells you the same source successfully moved 40 MB through an allowed rule an hour earlier. Neither source alone tells the story.
Verification
The logging pipeline is infrastructure; test it like infrastructure:
# End-to-end test entry - watch it arrive at the collector/log info message="syslog-pipeline-test $(date)"# (from script: /log info "test"; interactively just:):log info "syslog-pipeline-test"
# Confirm rules and actions/system logging print/system logging action print detail where name=syslog
# Confirm flows are exporting/ip traffic-flow print# and on the collector: tcpdump -ni any udp port 2055Then do the honest test: fail a login on purpose, confirm the account entry reaches the collector, and confirm whatever alert you configured actually fires.
Closing Thoughts
The order of operations, if you do nothing else: remote syslog for info/warning/error/critical/account/firewall, prefixes and connection-state=new on every logging firewall rule, disk target off on flash devices, IPFIX export to a collector. That is maybe twenty minutes per router, and it converts “no idea what happened” into a queryable timeline.
Logs answer what happened; the previous article’s metrics answer how it’s trending; the export snapshots in the next article answer what exactly changed. You want all three, off-box, before the incident — after it, the only thing you can configure is regret.