identityapproved's blog

Turning a Meraki MX64 into OpenWrt, against its will

2026-03-24

Turning a Meraki MX64 into OpenWrt, against its will

I bought a Meraki MX64 for the same reason many people do: it was cheap, overbuilt, and looked like a piece of serious network gear that deserved a second life.

What I did not buy was a clean, modern, well-documented OpenWrt experience.

This post is about taking a used MX64, getting OpenWrt onto it, fighting through serial weirdness, outdated repo artifacts, naming mismatches, missing files, ambiguous documentation, bootloader replacements, USB boot confusion, and finally ending up with a working OpenWrt box that now sits as the hardened inner boundary of my network, with a separate Debian-based Stora NAS behind it.

It worked in the end.

It was not elegant.

Why even bother ¯_(ツ)_/¯

The Meraki MX64 is one of those devices that feels like it should be easy to repurpose.

It has:

The problem is that Meraki hardware is not built for you. It is built for Meraki’s own stack, their assumptions, their boot flow, their firmware, their management world. The box is happy only when it is doing exactly what Cisco Meraki intended.

The moment you try to make it yours, it becomes a puzzle box.

The real starting point: this is not a “flash image and done” device

This is the first misunderstanding I had.

I assumed the process would look something like:

  1. get OpenWrt image
  2. TFTP or USB boot it
  3. sysupgrade
  4. done

That is not what this box wants.

The MX64 install path is more like:

  1. get serial access
  2. get into the right recovery/diagnostic environment
  3. inspect the exact hardware state
  4. determine if boot flash is locked
  5. determine if it is A0 or not
  6. replace stock U-Boot with a custom one
  7. boot the right temporary image
  8. only then install the actual OpenWrt system (compiled by hands)

So before you even get to OpenWrt, you are really doing bootloader archaeology.

Serial access: the first fight

The board exposes a 4-pin serial header.

Eventually the correct mapping was:

In theory, that sounds easy.

In practice, it was one of those stupid sessions where you start doubting everything:

At first, nothing showed up. Then later it showed up. Then it disappeared. Then I got partial boot logs. Then it worked only when the wires were physically held a certain way. Then the serial adapter would reconnect and disappear again.

Classic garbage-tier Dupont cable reality.

Eventually I got clean output at 115200 8N1, which was the first real sign that this project was not totally cursed.

First useful success: stock Meraki boot logs

Once serial finally worked, the MX64 booted into stock Meraki firmware and printed enough to confirm important details:

That part matters because it tells you the device is not bricked, not dead, and not silently broken in some strange hardware way.

It also tells you the “problem” is now software and procedure, which is much better than dead silicon.

The second misunderstanding: the <Meraki> prompt is not a shell

At one point, the box booted into a prompt that looked promising:

<Meraki>

Naturally, I tried things like:

Every one of them came back as some variation of “unrecognized command.”

That was another little trap. Seeing a prompt does not mean you have a usable shell. It just means Meraki is willing to display a prompt-shaped object on your screen.

The actual useful environment came later, in the diagnostic BusyBox shell.

Diagnostic mode: finally something useful

By holding reset during boot/after boot/while boot etc., I eventually got into the BusyBox-based diagnostic environment.

This was the first moment the process stopped feeling fake and started feeling actionable.

From there I could check the three critical values:

For my unit, the important facts were:

That determined the install branch.

This matters because the install path is not the same across all MX64-like cases. If you skip those checks, you are gambling.

The third misunderstanding: “the repo name must match the model”

This one wasted a stupid amount of time.

There are clayface repos involved in the older MX64 bootloader process. And because the naming is similar, it is very easy to land in the wrong place and then spend hours staring at files that look almost right.

That happened to me.

At different points I was bouncing between:

The worst part is that the names are close enough that your brain keeps telling you, “this is probably the one.”

It is often not the one.

Replacing U-Boot: the point of no return

The actual first major irreversible step was replacing the stock bootloader.

Because my boot flash was locked, that was not just a simple write. It involved:

That part is unnerving because once you cross that line, you are no longer “just experimenting”. You are now actively reauthoring how the box boots.

And this kernel module parameter (funniest and most honest names), make me nervous:

insmod mtd-rw.ko i_want_a_brick=1

OpenWrt installer: “Are you sure?”
You: “yes”
Module: “Not enough. Say the words.”
You: i_want_a_brick=1

The good news is that once the write completed cleanly, that was one of the biggest milestones of the whole process.

The bad news is that it did not make the rest easy.

It just unlocked a new category of confusion.

U-Boot prompt chaos and serial noise

After replacing the bootloader, the box dropped into a custom U-Boot prompt.

Which would have been nice, except the serial line was flaky enough that U-Boot frequently interpreted garbage as input.

So what I got was:

This created another layer of doubt because now I had to figure out whether I had a bad image, a bad bootloader, or just garbage coming over serial at the wrong time.

It turned out to be mostly the last one.

Serial noise is enough to ruin what would otherwise be a clean autoboot.

The fourth misunderstanding: old boot artifacts vs actual OpenWrt install images

This is the part that cost me hours.

There are older clayface repo images used for bootloader-related steps, with names like:

And then there are the current OpenWrt install-stage images, with names like:

The documentation does not make this distinction feel as sharp as it really is when you are sleep-deprived and juggling USBs, serial, static IPs, and multiple repos.

So I spent a lot of time asking some variation of:

That last one was especially bad.

The missing initramfs rabbit hole

The sysupgrade image was easy enough to get.

The actual initramfs image I needed for the install stage was not.

This is where the process became deeply annoying.

The documentation referred to:

openwrt-bcm53xx-generic-meraki_mx64-initramfs.bin

But there was no neat obvious “here is that exact file” path that matched what I expected. The public sources, repo contents, and wiki wording did not line up cleanly enough to make this feel straightforward.

At some point, I realized I had spent far too long trying to confirm whether a prebuilt file existed in the shape I wanted.

After few minutes of forum scrolling.
I stopped searching.

I realize that I just should compile it myself.

That was the correct move.

Compiling the actual initramfs myself

This ended up being one of the most useful decisions in the whole process, because it replaced ambiguity with control.

In OpenWrt menuconfig, the important choices were:

Then build.

After the build finished, I finally had the exact artifacts I wanted:

That was the moment the entire process became sane again.

The lesson here is simple:

If the naming, download location, and documentation feel inconsistent enough that you are no longer sure what you are holding, build the damn file yourself.

This was also the moment I understood Gentoo users a little better. (๑>؂•̀๑)

The time spent compiling was far less painful than the time spent second-guessing artifact names.

USB boot: finally real progress

With the freshly built bcm53xx initramfs on the USB and the custom U-Boot already in place, the MX64 finally did what it was supposed to do:

This stage still had some confusion because not all initramfs environments are equal. In my case, I got a booted OpenWrt initramfs, but it did not expose USB storage the way I initially expected for the final stage.

That meant I could not just mount the USB and sysupgrade from there.

So the solution became:

That worked.

And when the SSH session got killed during sysupgrade, that was one of the few moments in this process where “connection closed” was actually a good sign. ◟(‘ﮧ’ ◟ )

Reboot: OpenWrt is real now

After sysupgrade and reboot, OpenWrt came back up from flash.

Not initramfs.
Not recovery.
Not Meraki.
Not U-Boot shell.
Actual installed OpenWrt.

That is the point where the project stops being a flashing exercise and becomes a real network design problem.

Now the box had:

That was the first time the MX64 felt like a tool instead of a hostage situation.

Turning it into the hardened inner boundary

I wanted the MX64 to become the more controlled, more hardened part of my network.

So instead of replacing my main router outright, I used a cleaner layered design:

That gave me a private interior segment that I could manage separately.

I moved the MX64 LAN to:

192.168.50.0/24

with the router at:

192.168.50.1

That way the MX64 became the gatekeeper for devices behind it, rather than just another random switch-shaped object on the old flat network.

Then came the Stora

This should have been easy.

It was not.

The Stora is a Debian-based NAS-like box I had previously configured with a hardcoded IP:

And… I successfully forgot about last one and all turned out to nightmare. ༎ຶ‿༎ຶ

The fifth misunderstanding: “same cable, new subnet, should be fine”

I plugged the Stora behind the MX64 and expected that, after changing the IP, I would be able to SSH into it normally.

Instead:

At first this looked like a network config problem.

It was not.

It was a firewall problem on the Stora.

The subtle trap: static IP changed, firewall did not

I changed the Stora’s Debian network config from:

to:

That part was correct.

But the firewall rules on disk still allowed only:

So from the Stora’s point of view, the new subnet was hostile.

The machine had moved, but the trust boundary had not.

That is why it looked alive from ARP but dead to ping and SSH.

Recovering the Stora from USB

Since the Stora boot Debian from USB and I could mount its installed root filesystem offline, the recovery path was straightforward:

  1. mount the installed root
  2. inspect /etc/network/interfaces
  3. inspect firewall files
  4. fix the allowed subnet
  5. unmount
  6. boot normally again

What I found was exactly the problem:

After updating those to 192.168.50.0/24, the Stora finally became reachable behind the MX64.

That was the last real blocker.

♪┏(・o・)┛♪

The final structure

At the end of all of this, the network looked the way I wanted:

That is a much cleaner design than what I started with.

It gives me:

Backups and artifacts: what was worth keeping

One thing this process made obvious is that you do not want to lose the small pile of files that finally made everything work.

The useful things worth backing up were:

The giant build tree was not worth preserving as a full backup. The outputs matter. The compiler sludge does not.

Lessons learned ᝰ.ᐟ

1. Documentation can be technically correct and still operationally confusing

The MX64 documentation is not pure fiction. It does contain the right ingredients. But when multiple generations of artifacts, repos, file names, and install flows coexist, it is very easy to end up “technically following” the process while still holding the wrong file in your hand.

2. If you cannot confidently identify the right binary, compile it yourself

This was the biggest breakthrough. Once I built the exact initramfs I needed, the ambiguity mostly disappeared.

3. Bootloader stage and install stage are different

This sounds obvious after the fact. It was not obvious while staring at similarly named files across multiple sources.

4. Serial problems create fake software problems

A noisy or flaky UART setup can make a healthy bootloader look broken.

5. Static IP changes are not enough if firewall policy still trusts only the old subnet

This bit me on the Stora. Hard. Hope I’ll never forget to check firewall rules before applying smth.

6. Layered routing is better than trying to make everything flat

Using the MX64 as an inner boundary behind the main router gave me exactly the security/control separation I wanted without having to redesign the entire network from scratch.

Was it worth it?

Yes.

Not because the process was fun.

It was worth it because now the MX64 is no longer a dead corporate brick. It is a useful part of the network:

And the Stora, instead of being some dusty hardcoded Debian island, now lives behind that boundary where it belongs.

Final advice if you want to DYI

If you are about to do the same thing, here is the short version:

And above all:

When you finally get it working, back everything up immediately, because you will not want to rediscover all of this from scratch six months later.
I really hope I will not have to… •𐃷•

← Back to Home