# mikrotik-tailscale
[
](https://woodpecker.lumpiasty.xyz/repos/3)
A minimal Tailscale Docker image built for MikroTik routers running
[Container](https://help.mikrotik.com/docs/display/ROS/Container). Fits in
16 MB internal flash. Built from source with only router-relevant features
included.
## Supported architectures
| Docker platform | RouterOS arch | Example devices |
|---|---|---|
| `linux/amd64` | x86 / CHR | x86 installs, Cloud Hosted Router |
| `linux/arm64` | arm64 | RB5009, CCR2004/2116/2216, hAP ax³, L009, Chateau |
| `linux/arm/v7` | arm (ARMv7) | hAP ac², RB3011, RB4011, RB1100AHx4 |
A single Dockerfile builds all three. The Go binary is **cross-compiled** (the
builder stage runs natively on the host for speed), while the busybox stage and
final image are built for the target platform (via `buildx` + QEMU/binfmt for
non-native targets).
**ARMv5 is not supported** (hEX Refresh / hAP ax S, EN7562CT CPU — RouterOS
calls these `arm32v5`). ARMv5 has no Alpine/musl base image, so it cannot use
this image's musl + `scratch` design; it would require a glibc (Debian) base
and produce a substantially larger image (~50 MB+ vs ~4 MB). If you need it,
that's a separate build, not just a `--platform` change.
## Image size
On-disk footprint once extracted (this is what matters — RouterOS stores the
**extracted** rootfs on disk via overlayfs, not the compressed layers):
| Component | On-disk size |
|---|---|
| tailscale.combined (UPX-compressed) | ~3.84 MB |
| custom static busybox (UPX, ~100 applets) | ~229 kB |
| CA certificates | ~218 kB |
| **Total extracted rootfs** | **~4.1 MB** |
(The compressed image / transfer tarball is ~4.3 MB.)
The binary is built with Tailscale's `--extra-small` feature tag set as the
baseline. Features are opted in explicitly — any new feature Tailscale adds
in a future release stays omitted until deliberately added to the Dockerfile.
### Size optimizations applied
- **Feature allowlist** (`--extra-small` baseline + ~10 opt-ins) keeps the
binary minimal and forward-safe against new Tailscale features.
- **`-gcflags=all=-l`** disables function inlining across all packages,
shrinking the compressed binary by ~600 kB. Inlining is a performance
optimization only; disabling it does not affect correctness. The CPU cost
is negligible for an I/O-bound router daemon.
- **`-ldflags="-s -w"`** strips the symbol table and DWARF debug info.
- **`-trimpath`** removes local filesystem paths from the binary.
- **UPX `--lzma --best`** compresses the Tailscale binary (~14 MB → ~3.8 MB).
- **Custom static busybox** — instead of the official `busybox:musl` image
(all ~404 applets, ~1.24 MB), a static busybox is built from source with
only ~100 curated applets (~420 kB), then UPX-compressed to ~229 kB on
disk. The applet set is defined in
[`busybox-applets.config`](busybox-applets.config).
**busybox UPX requires care.** UPX normally breaks busybox's standalone
applet dispatch: the ash shell re-execs `/proc/self/exe` to run built-in
applets, and UPX breaks that path so typed commands fail
([upx#248](https://github.com/upx/upx/issues/248), closed as "invalid").
We work around it by building **without** the standalone/nofork features
and providing an explicit `/bin/