MikroTik put a container runtime inside a router OS, and the community immediately split into two camps: people running Pi-hole on a hAP ax² and loving it, and people who consider the feature an attack surface bolted onto critical infrastructure. Both camps are right. That’s the interesting part.
The container feature in RouterOS 7 is a real Linux-container implementation — namespaces, cgroups, OCI images pulled straight from Docker Hub. It is also explicitly shipped with a warning from MikroTik that there is no security guarantee of any kind, gated behind a device-mode flag that requires physical access to enable. When a vendor makes you press a physical button before letting you use a feature, believe them about the risk.
This article covers how to wire containers correctly — device-mode, veth, NAT, mounts, registries — which workloads actually make sense on router hardware, and why your internet edge router probably shouldn’t be one of the hosts.
Device-Mode: The Physical Gate
Containers are disabled by default and controlled by device-mode, RouterOS 7’s mechanism for locking down feature classes even against an attacker with full admin credentials:
/system/device-mode/update container=yes# You now have a few minutes to press the physical reset button# (or power-cycle on some devices) to confirm.No button press, no containers. This is the point: if someone steals your admin password, they still can’t remotely turn your router into a host for their malware. Enable it only on devices where you’ve decided containers belong, and leave it off everywhere else. Check the current state:
/system/device-mode/printTwo hardware prerequisites before you go further. You need an ARM, ARM64, or x86 (CHR) device — and on ARM you must pull images matching that architecture. And you need real storage: container layers on the internal NAND of a small router will both fill it instantly and wear it out. Use a USB stick you can afford to lose, or properly, a device with NVMe/SATA (RB5009 with an attached disk, CCR2004+, or CHR). Format it and reference it as disk1 or similar.
Wiring: veth, Bridge, NAT
A container gets its network through a veth pair — one end inside the container, one end in RouterOS. The docs’ standard pattern, which I’d keep: a dedicated bridge and subnet for containers, NATed out.
# veth for the container/interface/veth/add name=veth1 address=172.17.0.2/24 gateway=172.17.0.1
# dedicated bridge with the gateway address/interface/bridge/add name=containers/ip/address/add address=172.17.0.1/24 interface=containers/interface/bridge/port add bridge=containers interface=veth1
# NAT out/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.17.0.0/24To expose a service — say DNS on a Pi-hole container — dst-nat to the veth address:
/ip/firewall/nat/add chain=dstnat dst-address=10.0.10.1 dst-port=53 \ protocol=udp action=dst-nat to-addresses=172.17.0.2Treat the container subnet like any other untrusted segment: forward-chain rules deciding what containers may reach. A container that only needs to talk to one upstream API should be firewalled to exactly that. This is your own firewall discipline from earlier in this series applied inward.
Pulling and Running
Point the runtime at a registry and give it a temp directory on your external disk:
/container/config/set registry-url=https://registry-1.docker.io tmpdir=disk1/tmpEnvironment variables and mounts are named lists, referenced at container creation:
/container/envs/add list=pihole_envs key=TZ value="Asia/Almaty"/container/envs/add list=pihole_envs key=FTLCONF_webserver_api_password value="change-me"
/container/mounts/add list=pihole_mounts src=disk1/volumes/pihole-etc dst=/etc/piholeMounts are the difference between a stateful service and one that resets on every image update — anything the container writes that you care about belongs in a mount on persistent storage, not in the container’s root filesystem.
Create and start:
/container/add remote-image=pihole/pihole:latest interface=veth1 \ root-dir=disk1/images/pihole mounts=pihole_mounts envlist=pihole_envs \ logging=yes start-on-boot=yes
/container/print# wait for status=stopped (extracted), then:/container/start 0Verification is the usual trio — status, logs, and a probe from the router itself:
/container/print detail/log print where topics~"container"/ping 172.17.0.2# and for a DNS container:/resolve server=172.17.0.2 example.comFor interactive debugging, /container/shell 0 drops you into the container — assuming the image ships a shell at all; minimal images often don’t.
Global resource limiting is blunt but present — a soft RAM ceiling for the container subsystem:
/container/config/set ram-high=256MWhen usage crosses the boundary the processes get throttled and pushed into reclaim, which on a small ARM box means “your container gets slow before your router gets unstable.” That’s the correct priority order.
What’s Actually Worth Running
The honest list is short, and everything on it shares one profile: small, mostly idle, and valuable because it sits on the network device.
Pi-hole or a DNS filter. The classic. The router is already every client’s DNS server, so filtering there is architecturally clean. On a device with 1 GB of RAM it runs comfortably.
cloudflared / a tunnel client. An outbound-only tunnel endpoint (Cloudflare Tunnel, Tailscale in userspace) on a site’s router gives you management access to the site without inbound holes. Tiny footprint, huge operational win for remote sites.
Monitoring exporters. A prometheus SNMP exporter or a small agent that the router’s own metrics don’t cover. I’d still rather scrape RouterOS via SNMP or the REST API from outside — but on isolated sites, an exporter-plus-tunnel pair in containers is a legitimate pattern.
Things people run that I’d argue against: databases (flash wear, RAM), Home Assistant on anything smaller than an RB5009 (it fits, barely, and you’ve just fused your home automation to your router’s reboot cycle), and anything internet-facing like a reverse proxy or game server. The moment a container serves the internet, your router inherits that container’s entire vulnerability surface.
Why Your Edge Router Shouldn’t Run Them
Here’s the operational argument, independent of any CVE. Your edge router has one job with a clear failure model: forward packets, enforce policy, stay up. Every container you add breaks that model in three ways.
Blast radius. MikroTik’s own documentation says it plainly: a compromised container is a foothold on the device that terminates your internet connection, holds your firewall config, and speaks to every VLAN. Container escape bugs are rare but not theoretical, and RouterOS containers don’t come with the seccomp/AppArmor hardening layers a mature container platform gives you. The device-mode button exists because MikroTik knows this.
Resource contention. Routing under load wants CPU and RAM headroom for bursts — conntrack growth, a BGP reconvergence, a DoS spike. A container quietly eating 40% of RAM on a hAP ax² removes exactly the headroom you’ll need at the worst moment. ram-high softens this; it doesn’t eliminate it.
Operational coupling. RouterOS upgrades now restart your DNS filter. Container image updates now touch your router. Your change windows for “network” and “services” collapse into one, and your rollback story gets murkier.
The pattern I actually endorse: containers on a dedicated device — an RB5009 or hex-class box behind the edge, or a CHR — where container=yes is enabled, and device-mode left locked on the router that faces the internet. You get the convenience without wagering the edge.
Security Checklist
If you run them anyway — and on interior devices, reasonably — the short list:
# 1. Containers on their own subnet, firewalled like a DMZ/ip/firewall/filter add chain=forward src-address=172.17.0.0/24 \ dst-address=10.0.0.0/8 action=drop comment="containers cannot reach internal"
# 2. Pin image tags — 'latest' is not a version# remote-image=pihole/pihole:2026.02.1 rather than :latest
# 3. Logging on, and watched/container/set 0 logging=yesPlus the non-CLI items: pull only images you’d trust with root on a Linux box you own, keep container storage on removable media so a compromised or corrupted state can be physically destroyed, and record container=yes devices in your inventory — they’re a different risk class and should be patched like servers, not like switches.
Closing Thoughts
Containers on RouterOS are the same story as most powerful features: excellent in the narrow lane they were built for, hazardous when treated as a general-purpose platform. The lane is small network-adjacent services — DNS filtering, tunnel clients, exporters — on devices with real RAM and real storage, behind your edge rather than at it.
The device-mode reset-button ritual is a good forcing function. Every time you walk to a rack to press that button, ask the question it’s designed to make you ask: does this workload really belong on a router, or do I just not feel like provisioning a proper host? On my own network the answer has been “proper host” more often than not — and the containers that did make the cut have been boringly reliable, which is the only kind of reliable that counts.
Next up: RouterOS scripting — the language you’ll need for the automation articles that close out this series, and its considerable collection of footguns.