The ~7 MB seen via 'du' inside the container is RouterOS block-allocation
rounding (a 3 MB file occupies ~6 MB of blocks), NOT layer duplication —
verified: the published image carries tailscale.combined in exactly one
layer, and the real flash cost is ~3.7 MiB (free-hdd-space delta).
Fix the docs to measure on-flash footprint via free-hdd-space delta, not
du; clarify the overlayfs section is about keeping the image clean (still
valid best practice) and explicitly decouple it from the du number.
The --extra-small baseline omits unixsocketidentity, but without it the
localapi cannot verify a request came over the trusted unix socket, so
PermitRead/PermitWrite are always false and every CLI call (status, up,
set, ...) returns 'access denied' (tailscale/tailscale#17873). Add it to
the opt-in allowlist. Negligible size cost (~3.55 MB unchanged); the CLI
is non-functional without it.
README shrinks to a repo intro with pointers. Separate the three
audiences:
- docs/USAGE.md deploy the prebuilt image on RouterOS + operate it
- docs/DEVELOPMENT.md build, local test, version bump, cut releases
- docs/DESIGN.md size optimizations, feature allowlist, why the
updater and netmap disk-cache are removed, flash-wear
protection, versioning/release architecture, the
overlayfs layer-duplication gotcha, dependency pinning
Scheduled script that recreates the container only when the published
:stable image digest actually changed — no wasteful re-pulls. Compares
the registry manifest digest (anonymous Gitea token + :deserialize for
the token JSON) against a stored digest; recreates + records on change.
Verified end-to-end on RouterOS 7.21.2:
- token URL omits &service= (& is RouterOS's AND operator and breaks url=)
- header digest parsed case-insensitively from the flat http-headers string
- container identified by name; mounts via mountlists (list=, not name=)
- stop/start waits retry the operation (remove/start) rather than polling
a status string, which never matched and forced full timeouts
- no /container get ... status (status is a flag, not a gettable property)
- installed as a named /system/script (NOT /import, which only executes once)
cachenetmap only persists the netmap to disk for cold-start-during-
control-outage; the in-memory map (the common case) is unaffected. Its
cost is a flash write on every netmap delta, which is frequent on active
tailnets — the opposite of this image's flash-conservation goal. Remove
it from the allowlist. Also expand the clientupdate/cachenetmap comments
to document why they're deliberately omitted, and fix the stale volume/
statedir comments that referenced the now-removed on-disk netmap cache.
Creating the tailscale argv[0] symlinks with RUN in the final scratch
stage forced overlayfs to copy-up the whole /usr/local/bin directory,
duplicating the ~3 MB binary into a second layer. RouterOS extracts
overlay layers separately, so the on-disk rootfs measured ~7 MB instead
of ~3.4 MB. Assemble /usr/local/bin in the builder stage and bring it in
with a single COPY layer. Verified on RouterOS 7.21.2: du -sx / now ~3.4 MB.