Docker Image
MOAS-Server is published as a container image on GitHub Container Registry (GHCR):
ghcr.io/m1dst/moas-server
This image packages the native moas-server-cli binary and a Docker entrypoint that maps environment variables to CLI arguments.
What the image includes
Section titled “What the image includes”- Linux runtime image based on Ubuntu 24.04.
moas-server-clibinary at/app/moas-server-cli.- Built-in example configs:
/app/config/sample-config.moas/app/config/sample-network-config.moas
- Default exposed ports:
12059/tcpfor client command and control12059/udpfor client discovery
Supported image tags
Section titled “Supported image tags”The publish workflow pushes multiple tags to GHCR:
latest- Version tags from Git tags, for example
v2.1.0 - Commit SHA tags (
sha-*)
Use version tags in production for reproducible deployments.
Architecture support
Section titled “Architecture support”Published GHCR images are multi-architecture:
linux/amd64linux/arm64
So on ARM64 hosts (for example Raspberry Pi 64-bit or Apple Silicon Macs), docker pull ghcr.io/m1dst/moas-server:<tag> will automatically fetch the ARM64 variant.
Notes:
- Multi-arch images are created by the GitHub publish workflow using Buildx.
- On macOS, run with Docker Desktop. Network-based setups work normally; direct USB serial passthrough is typically not supported in Docker Desktop.
Pull the prebuilt image
Section titled “Pull the prebuilt image”docker pull ghcr.io/m1dst/moas-server:latestOr pin to a release:
docker pull ghcr.io/m1dst/moas-server:v2.0.0Quick start: simulation mode
Section titled “Quick start: simulation mode”Simulation mode is the safest first run because no physical switching occurs.
docker run --rm -it \ -e MOAS_SIM=true \ -e MOAS_LOG_LEVEL=info \ -p 12059:12059/tcp \ -p 12059:12059/udp \ ghcr.io/m1dst/moas-server:latestRun with your own .moas file
Section titled “Run with your own .moas file”Mount your config directory and point MOAS_CONFIG at a file in the mount.
docker run --rm -it \ -v "$PWD/moas:/config:ro" \ -e MOAS_CONFIG=/config/2x6.moas \ -e MOAS_LOG_LEVEL=info \ -p 12059:12059/tcp \ -p 12059:12059/udp \ ghcr.io/m1dst/moas-server:latestRun with USB serial hardware (Linux host)
Section titled “Run with USB serial hardware (Linux host)”Pass through the serial device to the container.
docker run --rm -it \ --device=/dev/ttyUSB0:/dev/ttyUSB0 \ -e MOAS_CONFIG=/app/config/sample-config.moas \ -e MOAS_LOG_LEVEL=info \ -p 12059:12059/tcp \ -p 12059:12059/udp \ ghcr.io/m1dst/moas-server:latestIf your config references a different serial device, update the .moas file accordingly.
Run with network-connected switch
Section titled “Run with network-connected switch”No device passthrough is needed when using TCP transport.
docker run --rm -it \ -v "$PWD/moas:/config:ro" \ -e MOAS_CONFIG=/config/2x6-network.moas \ -e MOAS_LOG_LEVEL=info \ -p 12059:12059/tcp \ -p 12059:12059/udp \ ghcr.io/m1dst/moas-server:latestOr use the bundled template:
docker run --rm -it \ -e MOAS_CONFIG=/app/config/sample-network-config.moas \ -e MOAS_LOG_LEVEL=info \ -p 12059:12059/tcp \ -p 12059:12059/udp \ ghcr.io/m1dst/moas-server:latestExample Switches block for TCP transport:
"Switches" : [ { "ID" : 1, "Transport" : "TCP", "Address" : "192.168.1.50", "TCPPort" : 12058 }]Address and TCPPort are the hardware switch endpoint (commonly 12058), not the MOAS server listen port.
Environment variables
Section titled “Environment variables”The Docker entrypoint maps these variables to CLI arguments:
MOAS_MODEdefaultrunMOAS_CONFIGdefault/app/config/sample-config.moasMOAS_LOG_LEVELdefaultinfoMOAS_SIMdefaultfalse(trueor1enables--sim)MOAS_DURATIONunset by default (if set, adds--duration <value>)
12059/udp: MOAS client discovery12059/tcp: MOAS client command and control12058/tcp: MOAS hardware switch control port when using TCP switch transport. This is outbound from the container to the switch, so you normally do not publish it with-p.12060/udp: optional - N1MM logger (only required whenUseN1MMLogger=true)
Expose only the ports your setup needs.
Passing CLI arguments directly
Section titled “Passing CLI arguments directly”If you pass arguments after the image name, the entrypoint executes moas-server-cli directly and ignores env-var defaults.
docker run --rm -it \ ghcr.io/m1dst/moas-server:latest \ validate /app/config/sample-config.moas info --simChecking logs
Section titled “Checking logs”Run in detached mode:
docker run -d \ --name moas-server \ -v "$PWD/moas:/config:ro" \ -e MOAS_CONFIG=/config/m1dst_tcp.moas \ -e MOAS_LOG_LEVEL=info \ -p 12059:12059/tcp \ -p 12059:12059/udp \ ghcr.io/m1dst/moas-server:latestThen inspect logs:
docker logs moas-serverOr follow logs live:
docker logs -f moas-serverA typical startup log looks like:
__ __ ___ _ ____ ___ ___| \/ | / _ \ / \ / ___| |_ _| |_ _|| |\/| | | | | | / _ \ \___ \ | | | || | | | | |_| | / ___ \ ___) | | | | ||_| |_| \___/ /_/ \_\ |____/ |___| |___|
Master Of Antenna Switching (MOAS)Developed by James Patterson, M1DST and Paul Young, K1XM.Version: v2.0.0
Loading configuration: /config/m1dst_tcp.moasConfiguration loaded.MOAS core running. Press Ctrl+C to stop.Stopped.
Read 3905 lines from configuration fileAntenna 1 has fast transition with 17 but the reverse is not trueAntenna 2 has fast transition with 17 but the reverse is not trueAntenna 3 has fast transition with 17 but the reverse is not trueAntenna 18 conflicts with 2 but the reverse is not trueAntenna 18 has fast transition with 3 but the reverse is not trueAntenna 18 has fast transition with 17 but the reverse is not trueLinux TCP listener started on port 12059Switch 1 port 192.168.10.94:12058 is not connectedSwitch 1 port 192.168.10.94:12058 is connectedSwitch 1 port 192.168.10.94:12058 is operationalPortainer deployment
Section titled “Portainer deployment”If you are using Portainer and want to use a Stack, you can find an example below.
version: "3.8"
services:
moas-server:
# Override IMAGE in Portainer if needed.
image: ${IMAGE:-ghcr.io/m1dst/moas-server:latest}
container_name: moas-server
restart: unless-stopped
environment:
MOAS_MODE: "run"
# Network-switch default template.
MOAS_CONFIG: "/config/2x6-network.moas"
MOAS_LOG_LEVEL: "info"
MOAS_SIM: "false"
# Optional finite run for testing, for example "10".
MOAS_DURATION: ""
ports:
# MOAS client command/control
- "12059:12059/tcp"
# MOAS client discovery
- "12059:12059/udp"
# Optional N1MM logger UDP input (enable only if UseN1MMLogger=true)
# - "12060:12060/udp"
volumes:
# Host path containing your .moas files.
- /opt/moas/config:/config:ro
# Optional serial passthrough (uncomment if using USB serial hardware).
# devices:
# - "/dev/ttyUSB0:/dev/ttyUSB0"
Default behavior of the stack:
- Uses
ghcr.io/m1dst/moas-server:latest(override withIMAGEif needed) - Mounts
/opt/moas/config:/config:ro - Exposes
12059/tcpand12059/udp - Includes optional serial passthrough lines you can uncomment
Troubleshooting
Section titled “Troubleshooting”- Container exits immediately:
- Check
MOAS_MODEandMOAS_CONFIGvalues. - Run
docker logs <container>for startup errors.
- Check
- Clients cannot discover server:
- Ensure
12059/udpis published and allowed through firewall.
- Ensure
- Clients cannot connect:
- Ensure
12059/tcpis published and reachable.
- Ensure
- USB serial hardware not detected:
- Confirm
--devicemapping and permissions on/dev/ttyUSB*.
- Confirm
- Config path errors:
- Verify host volume mount path and container path in
MOAS_CONFIG.
- Verify host volume mount path and container path in
Security and operations notes
Section titled “Security and operations notes”- Prefer pinned tags (
vX.Y.Z) overlatestin production. - Keep
.moasfiles mounted read-only when possible. - Use simulation mode before switching live hardware.
- Limit network exposure to trusted station networks.