RouterOS 7 Container Test
Introduction
A simple test of the container functionality in RouterOS 7.
We build a custom go binary, build a container for it, export that container, import that container on the router, and run it!
I’m not a Docker guy at all. This is my first steps into actually using it.
Lab Setup
- Mikrotik RB5009UPr+S+
- USB SanDisk 3.2Gen1 32GB
- Firmware versions tested: 7.19.2, 7.19.3, 7.20beta5 and 7.20beta6
- Raspberry pi 4 8GB
- 64GB MicroSD
Test Code
Companion GitHub repo for this blog
A tiny webserver in Go, so we can compile it to a static binary.
package main
import (
"fmt"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, World!")
}
func main() {
http.HandleFunc("/", helloHandler)
fmt.Println("Server starting on port 8080")
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Println("Error starting server:", err)
}
}
The Dockerfile.
# Use Debian Bookworm for arm64 as base image
FROM --platform=linux/arm64 debian:bookworm-slim
# Set working directory
WORKDIR /usr/local/bin
# Copy your prebuilt binary into the image
COPY hello /usr/local/bin/hello
# Make sure the binary is executable
RUN chmod +x /usr/local/bin/hello
# Expose the port the binary listens on
EXPOSE 8080/tcp
# Run the binary
CMD ["/usr/local/bin/hello"]
A docker compose file as well.
version: "3.9"
services:
test_service:
build:
context: .
dockerfile: Dockerfile
platform: linux/arm64
image: my-arm64-helloworld
ports:
- "8080:8080"
restart: unless-stopped
deploy:
resources:
limits:
cpus: "0.5"
memory: "256M"
And a Makefile, because I like having tooling to make things simple and repeatable.
APP_NAME := hello
GO_FILE := hello.go
BINARY := $(APP_NAME)
ARCH := arm64
OS := linux
.PHONY: all build clean docker-build up down logs
all: build
## Build Go binary for Linux ARM64
build:
GOARCH=$(ARCH) GOOS=$(OS) CGO_ENABLED=0 go build -o $(BINARY) $(GO_FILE)
## Remove outputs
clean:
rm -f $(BINARY) my-arm64-helloworld.tar
## Build Docker image
docker-build:
docker buildx build --platform linux/arm64 -t my-arm64-helloworld .
## Export Docker image to tar
docker-save:
docker save my-arm64-helloworld -o my-arm64-helloworld.tar
## Start container
up:
docker-compose up
## Stop and remove container
down:
docker-compose down
## View container logs
logs:
docker-compose logs -f
Building the Go Code
The Makefile provides a target for this, so we run make build and we have a fresh shiny hello binary to try in our container.
Building and Exporting the Docker Container
You can do this either directly with docker buildx or thru docker-compose. I opted to use the first, as then used the docker save command to get a tar image of the container to try on the router.
Testing the Container!
Copy the tar to your router, under the usb1 filesystem. I used Winbox to drag and drop the file after downloading it from my pi4.
This largely follows the pihole example in the Mikrotik documentation on Containers
Create everything you need, and import the container.
/interface bridge add name=dockers
/ip address add address=172.17.0.1/24 interface=dockers network=172.17.0.0
/interface veth add address=172.17.0.2/24 dhcp=no gateway=172.17.0.1 name=veth1
/interface bridge port add bridge=dockers interface=veth1
/container/add file=usb1/my-arm64-helloworld.tar interface=veth1 \
name=helloworld cmd=/usr/local/bin/hello \
root-dir=usb1/container/helloworld workdir=/usr/local/bin
/container/start 0
Since it is running in docker, the docker subnet either needs to be reachable in your network, or else you need to set up a port forward. I opted for a port forward.
/ip firewall nat
add action=dst-nat chain=dstnat dst-port=8080 protocol=tcp to-addresses=172.17.0.2 to-ports=8080
Overall and Next Steps
This was a success. I have a webserver on an alternate port that I can make requests to prove functionality.
$ curl -i http://192.0.2.0:8080/
HTTP/1.1 200 OK
Date: Wed, 16 Jul 2025 14:42:59 GMT
Content-Length: 14
Content-Type: text/plain; charset=utf-8
Hello, World!
Not only does it work, but its extremely fast. Without TLS, I’m getting responses back in sub-5ms across the LAN.
This small webserver is using 8 megabytes of memory. Cloudflared container uses about 60 megabytes idle.
Next steps will be to do a similar test, but maybe serve up some text files from a mount, to ensure that I’ve got a solid understanding of how that part of the functionality works.
