Build log · MikroTik RB5009 · RouterOS containers
Running the UniFi controller on the router itself
UniFi Network Application + MongoDB as RouterOS containers on the RB5009 — no second always-on box. The companion to the CGNAT build log.
Build log · MikroTik RB5009 · RouterOS containers
UniFi Network Application + MongoDB as RouterOS containers on the RB5009 — no second always-on box. The companion to the CGNAT build log.
This is a companion to the CGNAT build log. That post builds a segmented home LAN on a single MikroTik RB5009. This one runs the UniFi Network Application and its MongoDB on the router itself, as two RouterOS containers — so there is no second always-on box and no second attack surface to manage two UniFi 6 APs.
The controller runs as two containers — MongoDB and the UniFi Network Application — bridged onto the main LAN through veth interfaces. From the LAN they look like ordinary hosts; from the router they are services with hard memory limits. Two constraints are load-bearing and explain almost every odd line below: the RB5009 has 1 GB of RAM, and its Cortex-A72 is ARMv8.0-A, which the current ARM64 MongoDB builds do not run on.
Every numbered section is paste-ready against a RouterOS v7 box that already has the LAN segmentation from the companion post. The italic notes are the rationale — the trade-off being made and why.
This post starts where the segmentation post ends. You should already have:
the bridge with VLAN filtering enabled, the main LAN on VLAN 1
(192.168.88.0/24, router at 192.168.88.1), and the two UniFi 6 APs cabled
to trunk ports. If you do not, build
section 3 of the CGNAT post
first. You also need a dedicated 64 GB USB stick for container
storage and swap — its contents will be destroyed.
Substitute every <PLACEHOLDER> before pasting. Snippets assume the defconf
bridge name bridge, the router at 192.168.88.1, and place the containers
at 192.168.88.2 (Mongo) and 192.168.88.3 (UniFi) on VLAN 1.
| Placeholder | Meaning |
|---|---|
<MONGO_ROOT_PASS> | MongoDB root password (set on first init; keep it). |
<MONGO_UNIFI_PASS> | Password for the unifi Mongo user the application logs in with. |
<TZ> | IANA tz database name for the controller container, e.g. Asia/Manila. |
/dev/usb1 (64 GB USB stick)
├── part1 8 GiB raw → swap (smb-sharing=no, media-sharing=no, swap=yes)
└── part2 ~56 GiB ext4 → /usb1-part2
├── images/ container image layers
├── tmp/ container tmpdir
├── unifi-config/ UniFi /config
├── mongo-data/ Mongo /data/db
├── mongo-config/ Mongo /data/configdb
└── mongo-initdb/ Mongo init scripts (read-only)
The sequence below is destructive to the USB stick. Confirm it is expendable and run from a path that survives bridge and container changes.
USB swap + container storage
bash
1/container/config/set layer-dir="" tmpdir=""
2
3:foreach p in=[/disk/find slot~"usb1-part"] do={/disk/remove $p}
4/disk/add parent=usb1 type=partition partition-size=8589934592
5/disk/add parent=usb1 type=partition
6/disk/format numbers=usb1-part2 file-system=ext4
7
8/disk/set [find slot="usb1-part1"] smb-sharing=no media-sharing=no
9/disk/set [find slot="usb1-part1"] swap=yes
10
11/file/add type=directory name="usb1-part2/images"
12/file/add type=directory name="usb1-part2/tmp"
13/file/add type=directory name="usb1-part2/unifi-config"
14/file/add type=directory name="usb1-part2/mongo-data"
15/file/add type=directory name="usb1-part2/mongo-config"
16/file/add type=directory name="usb1-part2/mongo-initdb"
17
18/container/config/set layer-dir=/usb1-part2/images tmpdir=/usb1-part2/tmpStatic veths make the containers ordinary LAN hosts. With bridge VLAN filtering on, the VLAN-1 untagged list must be updated to include the new veths or they will not pass traffic.
veths on the main LAN
bash
1/interface/veth/add name=veth1-mongo address=192.168.88.2/24 gateway=192.168.88.1 gateway6=""
2/interface/veth/add name=veth2-unifi address=192.168.88.3/24 gateway=192.168.88.1 gateway6=""
3
4/interface/bridge/port/add bridge=bridge interface=veth1-mongo pvid=1
5/interface/bridge/port/add bridge=bridge interface=veth2-unifi pvid=1
6
7# Required when bridge vlan-filtering is enabled.
8/interface/bridge/vlan/set [find vlan-ids=1] \
9 untagged=bridge,ether2,ether3,ether4,ether5,veth1-mongo,veth2-unifiUniFi's LinuxServer image expects an external Mongo. The bootstrap user needs
ownership of unifi, unifi_stat, and unifi_audit, plus broader admin
roles for backup restore — UniFi creates a transient restore DB during import.
Mongo bootstrap user
javascript
1db.getSiblingDB("unifi").createUser({
2 user: "unifi",
3 pwd: "<MONGO_UNIFI_PASS>",
4 roles: [
5 { role: "dbOwner", db: "unifi" },
6 { role: "dbOwner", db: "unifi_stat" },
7 { role: "dbOwner", db: "unifi_audit" },
8 { role: "readWriteAnyDatabase", db: "admin" },
9 { role: "dbAdminAnyDatabase", db: "admin" }
10 ]
11});MongoDB is pinned to arm64v8/mongo:4.4.18. Newer ARM64 builds require
ARMv8.2-A atomics that the RB5009 Cortex-A72 (ARMv8.0-A) does not have; they
crash with SIGILL. The pinned version is EOL, so it stays LAN-only and
never gets a WAN forward.
Mongo + UniFi containers
bash
1/container/envs/add list=mongo-envs key=MONGO_INITDB_ROOT_USERNAME value=root
2/container/envs/add list=mongo-envs key=MONGO_INITDB_ROOT_PASSWORD value=<MONGO_ROOT_PASS>
3
4/container/mounts/add list=mongo-mounts src=/usb1-part2/mongo-data dst=/data/db
5/container/mounts/add list=mongo-mounts src=/usb1-part2/mongo-config dst=/data/configdb
6/container/mounts/add list=mongo-mounts src=/usb1-part2/mongo-initdb dst=/docker-entrypoint-initdb.d read-only=yes
7
8# Mongo 4.4.18 — newer ARM64 builds need ARMv8.2-A atomics that the
9# RB5009 Cortex-A72 (ARMv8.0-A) doesn't have; they exit with SIGILL.
10/container/add remote-image=arm64v8/mongo:4.4.18 interface=veth1-mongo envlist=mongo-envs
11/container/set [find name="mongo:4.4.18"] \
12 mountlists=mongo-mounts hostname=mongo name=mongo start-on-boot=yes logging=yes dns=192.168.88.1 \
13 cmd="mongod --wiredTigerCacheSizeGB 0.25 --bind_ip_all --ipv6" tmpfs="/tmp:64M:fixed"
14
15/container/envs/add list=unifi-envs key=PUID value=1000
16/container/envs/add list=unifi-envs key=PGID value=1000
17/container/envs/add list=unifi-envs key=TZ value=<TZ> # e.g. Asia/Manila — see tzdata(5)
18/container/envs/add list=unifi-envs key=MONGO_HOST value=192.168.88.2
19/container/envs/add list=unifi-envs key=MONGO_PORT value=27017
20/container/envs/add list=unifi-envs key=MONGO_USER value=unifi
21/container/envs/add list=unifi-envs key=MONGO_PASS value=<MONGO_UNIFI_PASS>
22/container/envs/add list=unifi-envs key=MONGO_DBNAME value=unifi
23/container/envs/add list=unifi-envs key=MONGO_AUTHSOURCE value=unifi
24/container/envs/add list=unifi-envs key=MEM_LIMIT value=384
25/container/envs/add list=unifi-envs key=MEM_STARTUP value=256
26
27/container/mounts/add list=unifi-mounts src=/usb1-part2/unifi-config dst=/config
28/container/add remote-image=lscr.io/linuxserver/unifi-network-application:latest interface=veth2-unifi envlist=unifi-envs
29/container/set [find name="unifi-network-application:latest"] \
30 mountlists=unifi-mounts hostname=unifi name=unifi start-on-boot=yes logging=yes \
31 dns=192.168.88.1 tmpfs="/tmp:128M:fixed"
32
33/container/start [find name="mongo"]
34/container/start [find name="unifi"]Container mounts use list= on the mount object and mountlists= on the
container. RouterOS rejects mountlists= during the initial image pull, so
set them with /container/set after the add.
Controller smoke tests
bash
1/disk/print where slot~"usb1-part"
2/log/print where topics~"container"
3
4nc -z -w 2 192.168.88.2 27017 && echo OK
5curl -sk https://192.168.88.3:8443/manage/account/login \
6 -o /dev/null -w "HTTP %{http_code}\n"Then browse to https://192.168.88.3:8443/, finish the setup wizard, and
adopt the APs. If an AP does not appear, point it at the controller with
set-inform http://192.168.88.3:8080/inform from its SSH console (or set the
controller hostname via DHCP option 43 / a DNS unifi record).
4.4.18 receives no security updates. It is
reachable only from the LAN and never port-forwarded; do not expose it.wiredTigerCacheSizeGB 0.25,
MEM_LIMIT 384) plus USB swap keep a backup-restore burst from starving
the routing plane. Lower them further on a busier box rather than removing
them.*AnyDatabase admin roles, not just unifi ownership.unifi-config (UniFi's own settings → backup) and treat the stick
as replaceable.Comments
Comments are powered by GitHub Discussions and require a free GitHub account to post.