Back to blog

Build Series  ·  MikroTik RouterOS 7  ·  Real-World Network  ·  October 2026

Build: The
Gaming Center Network

Forty gaming PCs, six console booths, a counter and a lounge full of waiting parents. The customers are the threat model here, the product is low latency, and every PC has to be clean for the next person. We build it on MikroTik from the seats up in 12 tested steps: diskless seats booting over 10G, customers isolated from each other by the switch chip, a game cache that downloads each title once, UPnP only where consoles need it, two WANs with games pinned to one, queuing that protects ping times, and a tournament mode you can switch on with one script.

Build Series RouterOS 7 Gaming Center Diskless Boot Port Isolation LANCache UPnP Queue Tree Recursive Failover

ether1-10 ether11-20 ether21-30 ether31-40 sfp+2 · 10G sfp+3 · 10G ether1-6 ether9-10 ether13-20 ether21 ether22-24 · trunk sfp+1 ⇄ sfp+1 · 10G trunk sfp+1 ⇄ sfp+2 · 10G trunk ether1 ether2 ARENA-Guest Internet WAN1 · fibre 1000/200 · static WAN2 · VDSL 100/20 · other ISP R1 CCR2004-16G-2S+ BOOT01 diskless images CACHE01 game cache · DNS SW-SEATS CRS354-48G-4S+2Q+ SW-FRONT CRS328-24P-4S+ cAP ax ×3 CAPsMAN Guests V50 Seats 1–10 V10 · stage Seats 11–20 V10 Seats 21–30 V10 Seats 31–40 V10 Console booths ×6 V20 Counter POS · card · V40 CCTV ×8 V60 NVR V60 FULL NETWORK all 12 steps built Drag the slider to build the network step by step. Devices with a config are clickable.

Full network · all steps built

Select a device to see its configuration

highlighted = lines added in the selected step · earlier steps shown plain · later steps hidden

This is part 2 of the Build series. The law firm was about keeping trusted staff away from things they didn't need. A gaming center turns that around: the people sitting at the keyboards are strangers, some of them are bored teenagers with a USB stick, and a few of them will try to cheat, sniff or crash the person next to them. At the same time the business sells exactly one thing, a fast and smooth game, so every security control has to cost zero latency.

It's built on MikroTik because that's what most real gaming centers run: a CCR handles 1 Gb/s of NAT with queues on every packet for a few hundred euros, and the CRS3xx switch chip can isolate ports at line rate. The design ideas carry over to any vendor.

Prerequisites

01

The Brief

A fictional but typical center: one hall, 40 PCs, 6 console booths, open 14 hours a day, and an owner who measures the network in complaints per evening.

The arena has 40 gaming PCs in four rows of ten, the first row beside a small stage used for weekend tournaments. There are 6 console booths with a PS5 and a big TV each, a counter with the POS PC and a card terminal, a lounge where parents wait and want Wi-Fi, and 8 cameras. Customers pay per hour through a cloud session-management service that runs a client on every PC. The owner wants three things above all: nobody complains about lag, nobody can mess with the PC next to them, and nobody leaves anything behind on a PC.

RequirementWhere it comes from, and what it forces
R1 Clean seatsCustomers install things, plug in USB sticks and leave logged-in accounts behind. Every seat must be factory-clean after every reboot, so the seats are diskless and boot an image over the network.
R2 Seat isolationCustomers attack each other: ARP poisoning to snoop logins, LAN flooding to lag a rival. Seats must not reach each other at Layer 2, which means isolation in the switch, not on the router.
R3 Latency firstOne customer downloading a 120 GB game must not add a single millisecond to someone else's match. Interactive traffic is queued ahead of bulk on every packet.
R4 Download onceForty seats patching the same game on release day would saturate any line. A LAN game cache fetches each title once and serves it at 10G.
R5 Consoles need open NATPS5 matchmaking and voice chat suffer behind strict NAT. Consoles get UPnP; nothing else does.
R6 Stay online, stay stableTwo ISPs. But a game that switches public IP mid-match drops anyway, so games stay on WAN1 and only bulk downloads use WAN2, until something fails.
R7 TournamentsOn weekends, seats 1–10 must see each other for LAN play, then go back to isolated. That has to be one action for staff, not a config change.
R8 Money & camerasThe card terminal and POS are on their own network, and so are the cameras. Neither is ever reachable from a seat.

02

Four Design Decisions

Most of this is forced by the brief. These four choices aren't, and each one is a trade-off worth stating out loud.

1 · Diskless seats, served at Layer 2

Diskless platforms boot every seat from a golden image on a server over iSCSI; each seat's writes go to an overlay that's thrown away at reboot. That solves R1 completely. The network decision is where the boot server sits. When a full room powers on at opening time, every seat reads several gigabytes at once. If that traffic had to cross the router, the router would become the bottleneck and the opening queue would last ten minutes. So BOOT01 sits in the seats' VLAN, on a 10G port of the seat switch, and the boot storm never leaves the switch chip.

2 · Isolation in the switch chip, not in VLANs

The textbook way to stop clients talking to each other is a VLAN per client or private VLANs. Forty VLANs would mean forty subnets, forty DHCP scopes and a boot server with forty legs. Instead, the CRS354's switch chip does port isolation: each seat port may only forward to the uplink and the two server ports. Seats share one subnet, can't exchange a single frame, and the boot and cache servers are still one hop away at line rate.

3 · Games pinned to WAN1, bulk on WAN2

Load-balancing every connection across two WANs (PCC) sounds efficient, but games and anti-cheat systems dislike a player whose public IP changes between connections, and some platforms flag it. So interactive traffic always uses WAN1, and only the game cache's downloads use WAN2, where a slow line doesn't matter. If either WAN fails, everything moves to the other one.

4 · No FastTrack

FastTrack makes RouterOS much faster by skipping most of the packet path, including the queues. That would defeat R3. A CCR2004 handles 1 Gb/s of NAT with a queue tree and full firewall without FastTrack, so we leave it off and accept the extra CPU load. On smaller routers, this trade-off goes the other way.

03

The Address & VLAN Plan

One /16, one /24 per VLAN, third octet = VLAN ID. Seats get fixed addresses by MAC, so "seat 17" is always .117.

VLAN · subnetWho lives here · gateway · DHCP
10 SEATS · 10.30.10.0/24Seats 1–40 at .101–.140 (reservations by MAC), BOOT01 data .10, CACHE01 data .20. GW .1 on R1. DHCP on R1 with PXE options; DNS = CACHE01.
20 CONSOLES · 10.30.20.0/24PS5s .11–.16 (reservations). DNS = CACHE01 (PSN updates are cacheable too). UPnP allowed.
30 SRV-MGMT · 10.30.30.0/24BOOT01 and CACHE01 management interfaces (.10, .20), tagged on their 10G ports. Admin only.
40 COUNTER · 10.30.40.0/24POS PC .10, card terminal .11, staff Wi-Fi. Internet + boot admin UI + NVR viewing.
50 GUEST · 10.30.50.0/24Lounge Wi-Fi. Internet only, client isolation, 30 Mb/s shared.
60 CCTV · 10.30.60.0/248 cameras .21–.28, NVR .10. No internet.
99 MGMT · 10.30.99.0/24R1 .1, SW-SEATS .11, SW-FRONT .12, cAPs .21–.23, monitoring box .20.
WAN1 · 203.0.113.10/29Fibre, static, gateway .9. Games, consoles, seats, guests.
WAN2 · DHCPVDSL from a second ISP. Game-cache downloads, plus everything if WAN1 dies.
MAIN HALL FRONT OF HOUSE Stage + seats 1–10V10 · tournament block Seats 11–20V10 Seats 21–30V10 Seats 31–40V10 · seat 40 = stream booth Console booths ×6V20 · PS5 + 55" TV Network closetR1 · switches · BOOT01 · CACHE01 · UPS CounterPOS · card terminal · V40 Lounge & snacksguest Wi-Fi · V50 EntranceCCTV · NVR · V60 AP AP AP Seats 1–10 sit beside the stage so tournament mode is one contiguous port range. Every seat is diskless: nothing a customer does survives a reboot.

04

The Policy Matrix

Rows start the connection, columns receive it. Default deny. Two enforcement points: the switch chip for seat-to-seat, R1's forward chain for everything else.

FROM ↓ · TO → Seats Consoles Boot · cache Counter CCTV · NVR Internet Seats · V10 ✕ isolated ✕ ✓ L2 · 10G ✕ ✕ ✓ queued Consoles · V20 ✕ — ◐ cache only ✕ ✕ ✓ + UPnP Srv mgmt · V30 ✕ ✕ — ✕ ✕ ◐ 443 Counter · V40 ✕ ✕ ◐ boot admin — ◐ NVR 443 ✓ Guest · V50 ✕ ✕ ✕ ✕ ✕ ✓ 30 Mb/s CCTV · V60 ✕ ✕ ✕ ✕ — ✕ never Mgmt · V99 ✓ ✓ ✓ ✓ ✓ ◐ updates allowed limited to the host/port shown denied (default) same zone / n.a. Seat↔seat is blocked in the switch chip (port isolation); everything else is R1's forward chain. During a tournament, seats 1–10 may reach each other.

Two cells explain most of the design. Seats → Boot · cache is "✓ L2" because those servers live in the seat VLAN on override ports, so that traffic never reaches R1 at all. And Seats → Seats is ✕ even though they share a subnet, because the switch chip never forwards one seat port to another.

04b

Requirements Traceability

Every requirement from the brief, the steps that implement it, the config that does it, and the test that proves it.

RequirementImplemented inKey configProven by
R1 Clean seatsstep 2 · step 4BOOT01 in the seats' VLAN on 10G; DHCP next-server + boot file; read-only golden image"Opening rush" and "Dirty seat" drills, section 17
R2 Seat isolationstep 1 · step 12port-isolation forwarding-override on ether1-40; DHCP snooping on both switches"ARP poison" and "Room sweep" drills
R3 Latency firststep 9connection-bytes bulk mark; queue tree priority 1/8 with PCQ and fq-codel; no FastTrack"Lag under load" drill
R4 Download oncestep 2 · step 6CACHE01 as the seats' DNS; DNS and DoT bypass dropped"Release day" drill
R5 Consoles need open NATstep 7UPnP internal on v20-consoles only"Console NAT" drill
R6 Stay onlinestep 8recursive routes; via-wan2 table for the cache; LAN-first routing rule"Fibre cut" drill
R7 Tournamentsstep 10tournament-on/off scripts + scheduler on SW-SEATS"Tournament" drill
R8 Money & camerasstep 3 · step 5camera ports learn=no + static hosts; forward chain keeps V40/V60 away from seats"Money & cameras" drill

The build

Bottom to Top, One Layer at a Time

Seats first, internet last. Each step ends with a checkpoint, and nothing goes up a layer until the current one passes.

Twelve steps: the seat switch, the game servers, the front-of-house switch, the router, the firewall, the cache, WAN1 and consoles, WAN2 and failover, queuing, tournament mode, guest Wi-Fi, and finally hardening. Each step that adds equipment opens with the topology: built parts solid, the step's additions in orange, later steps faded.

Safety first: when you change bridge VLAN settings on a remote MikroTik, use Safe Mode (Ctrl+X) or a console cable. Turning on vlan-filtering with one wrong line cuts off your own management access. See the VLAN post.

05 · Step 1 of 12

Seats & the Seat Switch

Forty seats on one CRS354, one VLAN, and a switch chip that refuses to forward a single frame from one seat to another.

▲ Show step 1 in the interactive topology · see what this step adds, and click any device for its config

The CRS354-48G-4S+2Q+ has 48 gigabit ports for the seats and four 10G SFP+ ports: one up to the router and two for the game servers, with one spare. All seat ports are untagged members of VLAN 10. The uplink is a tagged trunk. Hardware offload stays on, so the switch chip does all of this at line rate, not the switch's CPU.

SW-SEATS — bridge, VLANs, seat ports21 lines · this step's additions · click to expand
/system identity set name=SW-SEATS
/interface bridge add name=bridge vlan-filtering=no
# seats: untagged VLAN 10 only
:for i from=1 to=40 do={
  /interface bridge port add bridge=bridge interface=("ether" . $i) pvid=10 \
      frame-types=admit-only-untagged-and-priority-tagged
}
/interface bridge port
add bridge=bridge interface=sfp-sfpplus1 frame-types=admit-only-vlan-tagged comment="uplink R1"
add bridge=bridge interface=sfp-sfpplus2 pvid=10 comment="BOOT01 (step 2)"
add bridge=bridge interface=sfp-sfpplus3 pvid=10 comment="CACHE01 (step 2)"
/interface bridge vlan
add bridge=bridge vlan-ids=10 tagged=sfp-sfpplus1
add bridge=bridge vlan-ids=30 tagged=sfp-sfpplus1,sfp-sfpplus2,sfp-sfpplus3
add bridge=bridge vlan-ids=99 tagged=bridge,sfp-sfpplus1
/interface vlan add name=v99-mgmt interface=bridge vlan-id=99
/ip address add address=10.30.99.11/24 interface=v99-mgmt
/ip route add gateway=10.30.99.1                 # R1 arrives in step 4
/interface ethernet disable ether41,ether42,ether43,ether44,ether45,ether46,ether47,ether48
# last, from the console port or in Safe Mode:
/interface bridge set bridge vlan-filtering=yes

Now the isolation. On CRS3xx switches, each port can be given an explicit list of ports it's allowed to forward to. A seat port may forward only to the uplink and the two server ports:

SW-SEATS — port isolation in the switch chip
:for i from=1 to=40 do={
  /interface ethernet switch port-isolation add port=("ether" . $i) \
      forwarding-override=sfp-sfpplus1,sfp-sfpplus2,sfp-sfpplus3
}
Why not just firewall seat-to-seat on the router?

Two seats in the same subnet never send their traffic to the router. Seat 12 ARPs for seat 13, gets its MAC and sends frames straight across the switch. A router firewall rule can't see that traffic, and that's exactly where ARP poisoning and LAN floods happen. The block has to be in the switch. Port isolation does it in hardware, which also means it can't be bypassed by changing an IP address. The port isolation menu has moved between RouterOS releases, so check /interface ethernet switch port-isolation print on yours, and confirm the bridge ports still show the H (hardware-offloaded) flag afterwards.

Checkpoint: test this before building the next step

06 · Step 2 of 12

The Game Servers: Boot & Cache

Two boxes on 10G in the seats' own VLAN. One serves every seat's operating system, the other serves every game download after the first.

▲ Show step 2 in the interactive topology · see what this step adds, and click any device for its config

BOOT01 runs the diskless platform: an NVMe-backed golden Windows image plus a writeback overlay per seat. Its 10G port is untagged in VLAN 10 (address 10.30.10.10) and tagged in VLAN 30 for management (10.30.30.10). CACHE01 runs LANCache: a caching proxy plus a DNS server that answers the game launchers' CDN hostnames with its own address. It's set up the same way: 10.30.10.20 in VLAN 10, 10.30.30.20 in VLAN 30.

ServerWhat matters for the network
BOOT01Reads, not writes, dominate: at opening time 40 seats each read a few GB within about two minutes. One 10G port on the same switch handles that at line rate. NVMe is the real limit, not the network.
CACHE0116 TB of disk is enough for the 40–60 titles a center actually runs. Its DNS forwards everything it doesn't cache to a filtering resolver upstream (step 6).
BothSpare 10G port sfp-sfpplus4 is the upgrade path: LACP the boot server when the room grows, or add a second cache.
Checkpoint: test this before building the next step

07 · Step 3 of 12

Front of House: Booths, Counter, Cameras, APs

A second, smaller switch for everything that isn't a seat. Different devices, different VLANs, same rules.

▲ Show step 3 in the interactive topology · see what this step adds, and click any device for its config

SW-FRONT — CRS328-24P-4S+ (PoE)24 lines · this step's additions · click to expand
/system identity set name=SW-FRONT
/interface bridge add name=bridge vlan-filtering=no
/interface bridge port
add bridge=bridge interface=sfp-sfpplus1 frame-types=admit-only-vlan-tagged comment="uplink R1"
add bridge=bridge interface=ether1 pvid=20 comment="booth 1"   # … ether2-6: booths 2-6, same
add bridge=bridge interface=ether9 pvid=40 comment="POS PC"
add bridge=bridge interface=ether10 pvid=40 comment="card terminal"
add bridge=bridge interface=ether13 pvid=60 comment="cam 1"     # … ether14-20: cams 2-8
add bridge=bridge interface=ether21 pvid=60 comment="NVR"
add bridge=bridge interface=ether22 pvid=99 comment="cAP 1 (trunk)"
add bridge=bridge interface=ether23 pvid=99 comment="cAP 2 (trunk)"
add bridge=bridge interface=ether24 pvid=99 comment="cAP 3 (trunk)"
/interface bridge vlan
add bridge=bridge vlan-ids=20,40,60 tagged=sfp-sfpplus1
add bridge=bridge vlan-ids=40,50 tagged=sfp-sfpplus1,ether22,ether23,ether24
add bridge=bridge vlan-ids=99 tagged=bridge,sfp-sfpplus1
/interface vlan add name=v99-mgmt interface=bridge vlan-id=99
/ip address add address=10.30.99.12/24 interface=v99-mgmt
/ip route add gateway=10.30.99.1
# cameras: no learning, only their own MAC may use the port
/interface bridge port set [find interface=ether13] learn=no
/interface bridge host add bridge=bridge interface=ether13 mac-address=AC:CC:8E:00:00:21
# … repeat per camera; then:
/interface bridge set bridge vlan-filtering=yes

RouterOS has no "sticky MAC". The equivalent for the outdoor cameras is to turn off MAC learning on the port and add the camera's MAC as a static host. A laptop plugged into a camera cable then gets nowhere (see port security). The three cAPs sit on ether22–24 as trunks (management untagged, staff and guest SSIDs tagged) and get their SSIDs in step 11.

Checkpoint: test this before building the next step

08 · Step 4 of 12

The Router: Gateways, DHCP & Network Boot

R1 joins both switches, owns every gateway, and tells each seat where to boot from.

▲ Show step 4 in the interactive topology · see what this step adds, and click any device for its config

R1 — CCR2004-16G-2S+ · trunks and VLAN interfaces29 lines · this step's additions · click to expand
/system identity set name=R1
/interface bridge add name=br-lan vlan-filtering=yes
/interface bridge port
add bridge=br-lan interface=sfp-sfpplus1 frame-types=admit-only-vlan-tagged comment="SW-SEATS"
add bridge=br-lan interface=sfp-sfpplus2 frame-types=admit-only-vlan-tagged comment="SW-FRONT"
/interface bridge vlan
add bridge=br-lan vlan-ids=10,30 tagged=br-lan,sfp-sfpplus1
add bridge=br-lan vlan-ids=20,40,50,60 tagged=br-lan,sfp-sfpplus2
add bridge=br-lan vlan-ids=99 tagged=br-lan,sfp-sfpplus1,sfp-sfpplus2
/interface vlan
add name=v10-seats interface=br-lan vlan-id=10
add name=v20-consoles interface=br-lan vlan-id=20
add name=v30-srv interface=br-lan vlan-id=30
add name=v40-counter interface=br-lan vlan-id=40
add name=v50-guest interface=br-lan vlan-id=50
add name=v60-cctv interface=br-lan vlan-id=60
add name=v99-mgmt interface=br-lan vlan-id=99
/ip address
add address=10.30.10.1/24 interface=v10-seats
add address=10.30.20.1/24 interface=v20-consoles
add address=10.30.30.1/24 interface=v30-srv
add address=10.30.40.1/24 interface=v40-counter
add address=10.30.50.1/24 interface=v50-guest
add address=10.30.60.1/24 interface=v60-cctv
add address=10.30.99.1/24 interface=v99-mgmt
/interface list add name=LAN
/interface list member
add list=LAN interface=v10-seats
# … the same for every v* interface
R1 — DHCP with PXE options for the seats
/ip pool
add name=p-seats ranges=10.30.10.150-10.30.10.199   # spares; real seats use reservations
add name=p-consoles ranges=10.30.20.50-10.30.20.99
add name=p-counter ranges=10.30.40.50-10.30.40.99
add name=p-guest ranges=10.30.50.20-10.30.50.250
/ip dhcp-server
add name=d-seats interface=v10-seats address-pool=p-seats lease-time=12h
add name=d-consoles interface=v20-consoles address-pool=p-consoles lease-time=12h
add name=d-counter interface=v40-counter address-pool=p-counter lease-time=1d
add name=d-guest interface=v50-guest address-pool=p-guest lease-time=1h
/ip dhcp-server network
add address=10.30.10.0/24 gateway=10.30.10.1 dns-server=10.30.10.20 \
    next-server=10.30.10.10 boot-file-name=ipxe.efi comment="seats: PXE to BOOT01"
add address=10.30.20.0/24 gateway=10.30.20.1 dns-server=10.30.10.20
add address=10.30.40.0/24 gateway=10.30.40.1 dns-server=10.30.40.1
add address=10.30.50.0/24 gateway=10.30.50.1 dns-server=10.30.50.1
/ip dhcp-server lease
add server=d-seats mac-address=58:11:22:00:00:01 address=10.30.10.101 comment="Seat 1"
# … one reservation per seat; the diskless platform maps the same MAC to the seat number
/ip dns set allow-remote-requests=yes servers=1.1.1.2,1.0.0.2

Here's what happens when a seat powers on:

Seat 17no disk · UEFI PXE R1DHCP server BOOT01image server DHCPDISCOVER (PXE client) OFFER 10.30.10.117 · next-server 10.30.10.10 · ipxe.efi TFTP: fetch ipxe.efi iPXE script: your seat = 17 iSCSI login → golden image + seat-17 overlay writes go to the overlay, wiped on reboot Windows up · session client starts BOOTED · CLEAN IMAGENO DISK · POWERED OFF Every reboot is a factory reset: the golden image is read-only, and the per-seat overlay is thrown away.
UEFI, legacy BIOS, and who answers PXE

ipxe.efi only works for UEFI firmware. Every gaming PC from the last decade boots UEFI, so one boot file is enough. In a mixed room you'd hand out different files depending on DHCP option 93 (client architecture), which RouterOS 7 can do with /ip dhcp-server matcher. Some diskless platforms prefer to answer the PXE part themselves as a proxyDHCP server. In that case, remove next-server and boot-file-name here, and let R1 hand out only the addresses.

Checkpoint: test this before building the next step

09 · Step 5 of 12

Firewall: The Matrix as Rules

Input chain protects the router, forward chain is the matrix. Established/related first, explicit accepts, logged drop last.

SW-SEATS → R1 L2 first, then L3 Seat 1210.30.10.112 Seat 1310.30.10.113 ether12 ↛ ether13 → SMB / ARP poison ✕✕ port isolation (switch chip) Seat 1210.30.10.112 Counter POS10.30.40.10 v10 → v40: no rule → tcp/445 ✕✕ R1 forward: no rule, logged Guest phone10.30.50.31 Seat 1210.30.10.112 v50 → v10: no rule → any ✕✕ R1 forward: default drop Seat 1210.30.10.112 BOOT0110.30.10.10 ether12 → sfp+2 ✓ → iSCSI 3260 ✓ L2 · sfp+2 is allowed Seats can reach the boot and cache servers at line rate and the internet, but never each other, the till, or the guests.
R1 — address lists and filter rules29 lines · this step's additions · click to expand
/ip firewall address-list
add list=CACHE address=10.30.10.20
add list=BOOT address=10.30.10.10
add list=POS address=10.30.40.10
add list=NVR address=10.30.60.10
/ip firewall filter
# --- the router itself
add chain=input action=accept connection-state=established,related,untracked
add chain=input action=drop connection-state=invalid
add chain=input action=accept protocol=icmp
add chain=input action=accept in-interface=v99-mgmt comment="manage only from MGMT"
add chain=input action=accept in-interface-list=LAN protocol=udp dst-port=53,67
add chain=input action=accept in-interface-list=LAN protocol=tcp dst-port=53
add chain=input action=drop log=yes log-prefix=IN-DROP
# --- through the router
add chain=forward action=accept connection-state=established,related,untracked
add chain=forward action=drop connection-state=invalid
add chain=forward action=accept connection-nat-state=dstnat comment="UPnP mappings (step 7)"
add chain=forward action=accept in-interface=v10-seats out-interface-list=WAN comment="seats -> internet"
add chain=forward action=accept in-interface=v20-consoles dst-address-list=CACHE
add chain=forward action=accept in-interface=v20-consoles out-interface-list=WAN
add chain=forward action=accept in-interface=v40-counter out-interface-list=WAN
add chain=forward action=accept src-address-list=POS dst-address-list=BOOT protocol=tcp dst-port=443 \
    comment="staff console of the diskless platform (wake/restart seats)"
add chain=forward action=accept src-address-list=POS dst-address-list=NVR protocol=tcp dst-port=443
add chain=forward action=accept in-interface=v50-guest out-interface-list=WAN
add chain=forward action=accept in-interface=v30-srv out-interface-list=WAN protocol=tcp dst-port=443
add chain=forward action=accept in-interface=v99-mgmt
add chain=forward action=drop log=yes log-prefix=FWD-DROP

The WAN interface list is created in step 7, so the out-interface-list=WAN rules match nothing until then. Staff wake and restart seats from the diskless platform's own console, which runs on BOOT01 inside VLAN 10 where Wake-on-LAN broadcasts work. That avoids routing WoL broadcasts across VLANs (for that, see Wake-on-LAN on RouterOS).

Checkpoint: test this before building the next step

10 · Step 6 of 12

The Game Cache & DNS

The cheapest bandwidth upgrade in the building: download each game once.

The seats' DNS server is CACHE01 (set in step 4). When Steam asks for its CDN hostname, the cache answers with its own address. The launcher then downloads from the cache at LAN speed. If the cache doesn't have the file yet, it fetches it once from the real CDN, stores it, and passes it on.

1 · DNS: steam CDN → 10.30.10.20 2 · miss: fetch once 3 · store 80 GB on disk 4 · hit: served at ~1 GB/s Seat 4first download Seat 31same game, later CACHE0110.30.10.20 · 16 TB Steam CDNvia WAN2 The cache answers DNS for the launchers' CDN names with its own address. The first seat pays for the download once; every later seat gets it at LAN speed.

Every other DNS name is forwarded upstream to Cloudflare's malware-blocking resolver (1.1.1.2), or the family resolver (1.1.1.3) if the center admits under-16s on its own. To stop seats from bypassing the cache, and the filtering with it, with a hard-coded resolver, the forward chain drops DNS and DNS-over-TLS from seats. A seat's queries to CACHE01 never cross R1 (same VLAN), so the drop only hits bypass attempts:

R1 — seats use the cache's DNS, nothing else
/ip firewall filter
add chain=forward action=drop in-interface=v10-seats protocol=udp dst-port=53,853 \
    place-before=[find comment="seats -> internet"] comment="no DNS bypass"
add chain=forward action=drop in-interface=v10-seats protocol=tcp dst-port=53,853 \
    place-before=[find comment="seats -> internet"]
Why not just redirect DNS with dst-nat?

The usual trick, dst-nat any port-53 traffic to the resolver you want, breaks here. The cache is in the same subnet as the seats, so its reply goes straight back to the seat at Layer 2, from an address the seat never asked. The seat drops it. That's the classic hairpin-NAT problem, and the drop rule avoids it entirely. DNS-over-HTTPS can't be blocked by port, so the seat image itself disables DoH in browsers and launchers by policy. That's one more advantage of a single golden image.

What the cache can't do

LANCache works because the big launchers (Steam, Battle.net, Epic, Riot, PlayStation updates) still fetch game content over plain HTTP from predictable hostnames. Anything delivered only over HTTPS passes through uncached, because the cache can't read inside TLS and we're not going to break TLS on customers' machines. In practice the big downloads are cached and the long tail isn't.

Checkpoint: test this before building the next step

11 · Step 7 of 12

WAN1, NAT & Open NAT for Consoles

The fibre goes in, everything is masqueraded behind one public IP, and the console VLAN alone gets UPnP.

▲ Show step 7 in the interactive topology · see what this step adds, and click any device for its config

R1 — WAN1, masquerade, UPnP on the console VLAN only
/interface list add name=WAN
/interface list member add list=WAN interface=ether1
/ip address add address=203.0.113.10/29 interface=ether1 comment="WAN1 fibre"
/ip route add gateway=203.0.113.9 comment="temporary, replaced in step 8"
/ip firewall nat add chain=srcnat action=masquerade out-interface-list=WAN

/ip upnp set enabled=yes show-dummy-rule=no
/ip upnp interfaces
add interface=v20-consoles type=internal
add interface=ether1 type=external

A PS5 behind ordinary NAT reports "NAT Type 3": it can make outgoing connections but can't receive them, so matchmaking and party voice sometimes fail. UPnP lets the console ask R1 for a temporary inbound mapping:

PS5 · booth 310.30.20.13 R1UPnP on v20 only Game peersomewhere online SSDP: who is the gateway? AddPortMapping UDP 3074 → 10.30.20.13 dynamic dst-nat rule created UDP to 203.0.113.10:3074 forwarded to booth 3 console leaves: mapping expires NAT TYPE 2 (OPEN)NAT TYPE 3 (STRICT) UPnP is allowed only on the console VLAN. A seat full of customer software never gets to open inbound ports.
One public IP, forty strangers: the shared-IP ban

Every seat leaves through 203.0.113.10. If one customer cheats or gets reported in an online game, some anti-cheat systems and game services ban or rate-limit the IP address, and then the whole center is banned for the evening. Talk to the ISP before opening: a /29 gives you a few spare public addresses. You can source-NAT the console VLAN and the stream booth to their own address, so a ban on the seats' address doesn't take out the consoles. Some game publishers also run registration programs for internet cafés, which put the center's IP range on an allow-list.

Checkpoint: test this before building the next step

12 · Step 8 of 12

WAN2: Failover & Where Downloads Go

The second ISP does two jobs: it carries the cache's downloads every day, and it carries everything on the day the fibre dies.

▲ Show step 8 in the interactive topology · see what this step adds, and click any device for its config

RouterOS detects a dead upstream with recursive routes. Each default route points at a public "probe" address (1.1.1.1 for WAN1, 9.9.9.9 for WAN2) that is itself only reachable through that WAN. check-gateway=ping pings the probe; when it stops answering, the default route that depends on it goes inactive and the next one takes over:

LAN10.30.0.0/16 R1recursive routes WAN1 · fibreether1 · distance 1 WAN2 · VDSLether2 · distance 2 Internetprobe 1.1.1.1 WAN1 healthy: 1.1.1.1 answers via WAN1, so the recursive default route stays active ✕WAN1 upstream dead: 1.1.1.1 stops answering, the recursive route goes inactive, distance 2 via WAN2 takes over The link light on ether1 stays green. Only a probe past the ISP's first hop notices the fault.
R1 — WAN2, recursive failover, and a routing table for the cache
/interface pppoe-client add name=pppoe-wan2 interface=ether2 user=arena@isp2 password=******** \
    add-default-route=no use-peer-dns=no disabled=no
/interface list member add list=WAN interface=pppoe-wan2
/ip route remove [find comment="temporary, replaced in step 8"]
/ip route
add dst-address=1.1.1.1/32 gateway=203.0.113.9 scope=10 comment="probe via WAN1"
add dst-address=9.9.9.9/32 gateway=pppoe-wan2 scope=10 comment="probe via WAN2"
add dst-address=0.0.0.0/0 gateway=1.1.1.1 target-scope=11 check-gateway=ping distance=1 comment="main: WAN1"
add dst-address=0.0.0.0/0 gateway=9.9.9.9 target-scope=11 check-gateway=ping distance=2 comment="main: WAN2"
# a second table where the preference is reversed
/routing table add name=via-wan2 fib
/ip route
add dst-address=0.0.0.0/0 gateway=9.9.9.9 target-scope=11 check-gateway=ping distance=1 routing-table=via-wan2
add dst-address=0.0.0.0/0 gateway=1.1.1.1 target-scope=11 check-gateway=ping distance=2 routing-table=via-wan2
/routing rule
add dst-address=10.30.0.0/16 action=lookup-only-in-table table=main comment="LAN stays LAN"
add src-address=10.30.10.20/32 action=lookup table=via-wan2 comment="cache downloads ride WAN2"
The rule order that keeps the cache on the LAN

The via-wan2 table only has default routes, no connected networks. Without the first rule, when CACHE01 answers a console in VLAN 20, that reply would match the cache's source-address rule, find the default route in via-wan2, and leave for the internet. The dst-address=10.30.0.0/16 → main rule has to come first. The same class of bug shows up in every policy-routing setup that forgets its own LAN. Also: 1.1.1.1 and 9.9.9.9 are now pinned to one WAN each, so never use either as a DNS server. That's why the resolvers are 1.1.1.2 and 1.0.0.2.

What failover can't save

When WAN1 dies, every game in progress drops: the public IP changed, and no routing trick can move a live UDP session to a new address. Connections that were open on WAN1 also keep their old NAT mapping until they time out, which can take minutes. A /tool netwatch on 1.1.1.1 that runs /ip firewall connection remove [find reply-dst-address~"203.0.113.10"] when it goes down makes clients reconnect immediately. Failover keeps the room online; it doesn't keep the match alive.

Checkpoint: test this before building the next step

13 · Step 9 of 12

Latency First: The Queue Tree

Never let a download sit in front of a game packet. Separate the two classes, prioritise the small one, then share each class fairly per seat.

R1 queue tree · WAN1 download and upload game · voice · web interactive · priority 1 · PCQ per seat downloads past 5 MB bulk · priority 8 · PCQ per seat HTB950M / 190M WAN1 1000/200 Game packets jump the queue even while seat 31 downloads 100 GB. PCQ then splits each class evenly per seat, so one heavy user can't starve the room.

Telling "game" from "download" by port is hopeless: games use hundreds of ports and launchers use plain HTTPS. What reliably separates them is size. A TCP connection that has moved more than 5 MB is a download. Everything else (game UDP, voice chat, browsing, the session client) is interactive. The router marks connections once, marks packets by direction, and the queue tree does the rest:

R1 — mangle and queue tree (WAN1)
/ip firewall mangle
add chain=forward action=mark-connection new-connection-mark=bulk protocol=tcp \
    connection-bytes=5000000-0 connection-mark=no-mark passthrough=yes comment="past 5 MB = a download"
add chain=forward action=mark-packet in-interface=ether1 connection-mark=bulk new-packet-mark=dl-bulk passthrough=no
add chain=forward action=mark-packet in-interface=ether1 new-packet-mark=dl-int passthrough=no
add chain=forward action=mark-packet out-interface=ether1 connection-mark=bulk new-packet-mark=ul-bulk passthrough=no
add chain=forward action=mark-packet out-interface=ether1 new-packet-mark=ul-int passthrough=no
/queue type
add name=pcq-down kind=pcq pcq-classifier=dst-address pcq-rate=0
add name=fq kind=fq-codel
/queue tree
add name=DOWN parent=global max-limit=950M
add name=down-int parent=DOWN packet-mark=dl-int priority=1 max-limit=950M queue=pcq-down
add name=down-bulk parent=DOWN packet-mark=dl-bulk priority=8 max-limit=950M queue=pcq-down
add name=UP parent=ether1 max-limit=190M
add name=up-int parent=UP packet-mark=ul-int priority=1 max-limit=190M queue=fq
add name=up-bulk parent=UP packet-mark=ul-bulk priority=8 max-limit=190M queue=fq
Three things that silently disable this

1. FastTrack. Fast-tracked packets skip the queue tree. That's why decision 4 left it off. 2. Shaping at 100%. If max-limit equals the line rate, the queue forms in the ISP's equipment instead of here, and priorities never apply. Shape 5% below. 3. Per-seat fairness on upload. A queue on ether1 sees packets after masquerade, when every seat has the same source address, so a PCQ keyed on src-address would put the whole room into one bucket. Upload uses fq-codel (fair per flow) instead. Download is fine: the destination address was translated back to the seat before the queue sees it. Priority only works on child queues that have their own max-limit, which is why each one repeats it.

Checkpoint: test this before building the next step

14 · Step 10 of 12

Tournament Mode

Saturday evening: seats 1–10 need to see each other for LAN play. Sunday morning they must not. One script each way.

SW-SEATS one script flips it Seat 3normal day Seat 710.30.10.107 ether3 ↛ ether7 → LAN game ✕✕ isolated: ether3 ↛ ether7 Seat 3tournament ON Seat 710.30.10.107 ether3 → ether7 ✓ → LAN game ✓ override now lists ether1-10 Seat 3tournament ON Seat 2310.30.10.123 ether3 ↛ ether23 → anything ✕✕ seat 23 isn't in the bracket Tournament mode adds the bracket's own ports to each bracket port's override list. Nothing else in the network changes.

No VLAN changes, no new subnet, no DHCP changes. The bracket's seats stay in VLAN 10 with their usual addresses, boot server and cache. The only change is the isolation list on their ten switch ports. These scripts live on SW-SEATS, where the isolation is:

SW-SEATS — tournament on/off, scheduled
/system script
add name=tournament-on source={
  :local bracket "sfp-sfpplus1,sfp-sfpplus2,sfp-sfpplus3"
  :for i from=1 to=10 do={ :set bracket ($bracket . ",ether" . $i) }
  :for i from=1 to=10 do={
    /interface ethernet switch port-isolation set [find port=("ether" . $i)] forwarding-override=$bracket
  }
  :log warning "TOURNAMENT ON: seats 1-10 can reach each other"
}
add name=tournament-off source={
  :for i from=1 to=10 do={
    /interface ethernet switch port-isolation set [find port=("ether" . $i)] \
        forwarding-override=sfp-sfpplus1,sfp-sfpplus2,sfp-sfpplus3
  }
  :log warning "TOURNAMENT OFF: seats 1-10 isolated again"
}
/system scheduler
add name=t-on start-date=2026-11-07 start-time=17:00:00 interval=7d on-event=tournament-on
add name=t-off start-date=2026-11-07 start-time=23:30:00 interval=7d on-event=tournament-off

For irregular events, the manager runs /system script run tournament-on from the management laptop. The scheduled t-off means a forgotten "off" never leaves the bracket open past Saturday night. Writing to the log at warning level means the state change shows up on the monitoring box (step 12). For more on RouterOS scripting, see scripting.

Checkpoint: test this before building the next step

15 · Step 11 of 12

Guest & Staff Wi-Fi

The parents in the lounge get fast-enough Wi-Fi that can't see anything else. The staff get a hidden SSID onto the counter VLAN.

▲ Show step 11 in the interactive topology · see what this step adds, and click any device for its config

R1 — CAPsMAN for three cAP ax (RouterOS 7 wifi)17 lines · this step's additions · click to expand
/interface wifi security
add name=sec-guest authentication-types=wpa2-psk,wpa3-psk passphrase="rotate-me-monthly"
add name=sec-staff authentication-types=wpa2-psk,wpa3-psk passphrase="********"
/interface wifi datapath
add name=dp-guest bridge=bridge vlan-id=50 client-isolation=yes
add name=dp-staff bridge=bridge vlan-id=40
/interface wifi configuration
add name=cfg-guest ssid=ARENA-Guest security=sec-guest datapath=dp-guest country=Greece
add name=cfg-staff ssid=ARENA-Staff security=sec-staff datapath=dp-staff country=Greece hide-ssid=yes
/interface wifi capsman set enabled=yes interfaces=v99-mgmt
/interface wifi provisioning
add action=create-dynamic-enabled supported-bands=5ghz-ax master-configuration=cfg-guest slave-configurations=cfg-staff
add action=create-dynamic-enabled supported-bands=2ghz-ax master-configuration=cfg-guest slave-configurations=cfg-staff

# 30 Mb/s for the whole lounge, split evenly per device
/queue simple add name=guest-wifi target=10.30.50.0/24 max-limit=30M/30M \
    queue=pcq-upload-default/pcq-download-default

The bridge=bridge in the datapath refers to each cAP's own local bridge: the APs switch traffic locally onto VLANs 40 and 50 of their trunk, so nothing hairpins through R1. The AP-side bridge setup is the same as in the CAPsMAN post and the hotel lab. Hiding the staff SSID is not a security control. It only stops customers' phones from listing it. The password is the control.

Checkpoint: test this before building the next step

16 · Step 12 of 12

Hardening, Snooping & Backups

Close the management plane, stop rogue DHCP on the switches, and make sure a dead router costs 20 minutes, not a weekend.

R1 + both switches — management plane
/interface list add name=MGMT
/interface list member add list=MGMT interface=v99-mgmt
/ip service disable telnet,ftp,www,api,api-ssl
/ip service set ssh address=10.30.99.0/24
/ip service set winbox address=10.30.99.0/24
/tool mac-server set allowed-interface-list=MGMT
/tool mac-server mac-winbox set allowed-interface-list=MGMT
/ip neighbor discovery-settings set discover-interface-list=MGMT
/user add name=netadmin group=full password=********
/user disable admin
/system ntp client set enabled=yes servers=time.cloudflare.com
/system logging action add name=remote target=remote remote=10.30.99.20
/system logging add topics=warning action=remote
/system logging add topics=firewall action=remote
/system scheduler add name=nightly-export interval=1d start-time=04:00:00 \
    on-event="/export show-sensitive file=nightly"
SW-SEATS + SW-FRONT — DHCP snooping
/interface bridge set bridge dhcp-snooping=yes
/interface bridge port set [find interface=sfp-sfpplus1] trusted=yes

DHCP snooping on the switches drops DHCP offers from anything except the uplink, so a customer's laptop running a DHCP server can't take over the room. The nightly export is collected by the monitoring box over SFTP and kept off-device. Store it encrypted, because show-sensitive includes the PPPoE and Wi-Fi passwords (see backups and hardening).

Checkpoint: test this before building the next step

17

Verify, Then Break It Before Opening Night

Every drill maps to a requirement. Run them with the room full of test seats, not with one laptop.

Drill (requirement)What must happen
Opening rush (R1)Power on all 40 seats at once. All reach the login screen within ~3 minutes; R1's uplink to SW-SEATS stays near idle.
Dirty seat (R1)Install a program and save a file on seat 9, reboot: both are gone.
ARP poison (R2)Run an ARP-spoofing tool on seat 12 against seat 13 and the gateway. Seat 13 never sees it; the gateway's ARP entry for seat 13 doesn't change.
Room sweep (R2)nmap -sn 10.30.10.0/24 from a seat finds only .1, .10 and .20.
Release day (R4)Ten seats install the same 60 GB update: WAN traffic ≈ 60 GB once, the rest is cache hits.
Lag under load (R3)One seat saturates a download; ten seats play an online match. No in-game ping rise above ~2 ms.
Console NAT (R5)Every booth tests NAT Type 2; a party chat across two booths works.
Fibre cut (R6)Pull the fibre upstream of the ONT: the room keeps browsing via WAN2 in ~10–20 s. Matches in progress drop (expected).
Tournament (R7)Scheduled on/off at the set times; the log shows both; seats outside the bracket are never reachable.
Money & cameras (R8)From every VLAN except counter and management, 10.30.40.10 and 10.30.60.0/24 are unreachable.

18

The Attacker's View

In a gaming center the attacker pays by the hour and sits next to the victim. Here's what's left for them to try.

19

Lab It in CHR / GNS3

No Packet Tracer for RouterOS, but nearly all of this runs on free CHR images.

This postIn the lab
R1 CCR2004A CHR with three interfaces (two trunks, two WANs). Everything in steps 4–10 except the radios works the same, including recursive failover. Use two upstream CHRs as "ISPs".
SW-SEATS / SW-FRONTCHR bridges with VLAN filtering. No switch chip, so port isolation doesn't exist. Emulate it with bridge horizon=1 on the seat ports (ports with the same horizon value don't forward to each other) and leave the server and uplink ports without a horizon.
SeatsSmall Linux VMs (or VPCS) in VLAN 10. Diskless boot can be tested with iPXE + a tiny iSCSI target on a Linux VM.
Wi-FiSkip it, or tag a VM's NIC into VLAN 50 to play guest.

20

Bill of Materials

Indicative EU street prices, late 2026, excluding VAT and the gaming PCs themselves.

ItemIndicative (EUR, ex VAT)
R1 · CCR2004-16G-2S+450 – 550
SW-SEATS · CRS354-48G-4S+2Q+450 – 550
SW-FRONT · CRS328-24P-4S+ (PoE)420 – 520
3× cAP ax300 – 380
4× 10G DAC cables60 – 100
BOOT01 · NVMe server + 10G NIC2,500 – 4,500, plus the diskless platform's per-seat licence
CACHE01 · 16 TB + 10G NIC1,200 – 2,500
UPS for the closet600 – 900
Network + servers total≈ 5,900 – 10,000, plus structured cabling (~50 runs)
The PoE you don't need, the UPS you do

Gaming PCs don't need PoE, so the seat switch is a plain CRS354. The cameras and APs do, which is why SW-FRONT is the PoE model. The UPS is not optional, though: a 2-second power dip reboots 40 diskless seats at once, which is a full opening-time boot storm in the middle of a tournament. Put R1, both switches and both servers on it.

Appendix

The Same Design on Cisco

Same brief and the same VLANs on Catalyst and a Cat8000-class router. The Cisco tabs on the config blocks above show the key steps side by side; this appendix covers what changes underneath.

RoleThis post → Cisco
SW-SEATS (48 + 10G)CRS354-48G-4S+2Q+ → Catalyst 9300-48T + C9300-NM-8X
SW-FRONT (PoE)CRS328-24P-4S+ → Catalyst 9200L-24P-4G
R1CCR2004-16G-2S+ → C8300-1N1S-6T (NAT + NBAR2 + shaping at 1 Gb/s)
Wi-Fi3× cAP ax + CAPsMAN → 3× C9115AXI with EWC
Indicative total (network only)≈ 1,700 – 2,100 → ≈ 15,000 – 20,000 EUR ex VAT
FeatureThis post → Cisco
Port isolationforwarding-override lists → switchport protected
Camera port locklearn=no + static host → port-security mac-address sticky
PXE optionsnext-server / boot-file-name → next-server / bootfile in the DHCP pool
UPnP for consolesUPnP → not available: 1:1 static NAT per booth from a public /28
Recursive failovercheck-gateway recursive routes → IP SLA + track
Cache on WAN2routing table + rule → PBR with verify-availability
Latency-first queuingconnection-bytes marks + queue tree → NBAR2 categories + LLQ under a shaper
Tournament modescript + scheduler → EEM applets with cron timers
DHCP snoopingbridge dhcp-snooping → ip dhcp snooping (and DAI)
What actually changes in the design

Classification by application instead of by size. RouterOS calls any TCP connection past 5 MB "bulk"; Cisco NBAR2 recognises the game and download applications themselves. That's more precise, but it depends on NBAR2 knowing the titles your center runs, and the download direction has to be shaped on the LAN side, because the WAN policy only sees upload. No UPnP means open NAT for consoles needs public addresses. Protected ports aren't override lists: an unprotected port reaches every protected one, so tournament mode also moves the bracket to its own VLAN.

Cost is the real difference. Roughly ten times the network budget for the same result. Cisco makes sense here when the center is part of a chain that already runs Cisco and manages it centrally.

Takeaways

  1. Diskless seats make R1 (clean PCs) a solved problem, as long as the boot server sits in the seats' Layer 2 domain so the boot storm never reaches the router.
  2. Customer-to-customer protection belongs in the switch chip. Port isolation blocks seat-to-seat at line rate, in hardware, with one subnet and no VLAN sprawl.
  3. A LAN cache plus DNS is the biggest bandwidth upgrade per euro, and blocking DNS bypass keeps it (and its filtering) in the path.
  4. Pin games to one WAN. Use the second for bulk and for failure. Recursive routes catch the upstream faults that a link light misses, and the LAN-first routing rule prevents the classic policy-routing leak.
  5. Queue by size, not by port. Connections past 5 MB are downloads; everything else is interactive. Shape below line rate, keep FastTrack off, and use fq-codel where NAT hides the seats.
  6. Tournament mode is a port list, not a redesign. One scheduled script flips the bracket's isolation and logs that it did.
  7. The threat sits at the keyboard. UPnP only for consoles, snooping on every switch, BIOS locks, and a reboot at the end of every session.

This is part 2 of the Build series. Part 1: the law firm. Next: the coffee shop.

Opening or fixing a gaming center?

Lag complaints, seats that get "infected", a slow line on patch day: these are network problems with known fixes. I design and build networks like this one on MikroTik and test them under a full room before opening night.

Book a Discovery Call →