LinuxServer.io–style containerized client forwarding Mode S messages for aircraft multilateration
Unofficial community image for mlat-client, built with LinuxServer.io style container patterns (s6, hardened defaults, practical runtime options) for ADS-B multilateration workloads.
Sponsored and maintained by Blackout Secure.
Important This repository is not an official LinuxServer.io image release.
This project packages upstream wiedehopf/mlat-client into an easy-to-run, LinuxServer.io-style container image with practical defaults for Mode S multilateration clients.
Quick links:
- Docker Hub listing: blackoutsecure/mlat-client
- Balena block listing: mlat-client block on Balena Hub
- GitHub repository: blackoutsecure/docker-mlat-client
- Upstream application: wiedehopf/mlat-client
- Quick Start
- Image Availability
- About The mlat-client Application
- Supported Architectures
- Usage
- Parameters
- Configuration
- Application Setup
- Troubleshooting
- Release & Versioning
- Support & Getting Help
- References
5-minute multilateration client setup:
docker run -d \
--name=mlat-client \
--restart unless-stopped \
-e TZ=Etc/UTC \
-e MLAT_CLIENT_INPUT_CONNECT=readsb:30005 \
-e MLAT_CLIENT_SERVER=feed.adsbexchange.com:31090 \
-e MLAT_CLIENT_LAT=51.5 \
-e MLAT_CLIENT_LON=-0.1 \
-e MLAT_CLIENT_ALT=50m \
-e MLAT_CLIENT_USER_ID=myuser \
-e MLAT_CLIENT_RESULTS=beast,connect,readsb:30104 \
-v mlat-client-config:/config \
blackoutsecure/mlat-client:latestFor compose files, balena, and more examples, see Usage below.
Docker Hub (Recommended):
- All images published to Docker Hub
- Simple pull command:
docker pull blackoutsecure/mlat-client:latest - Multi-arch support: amd64, arm64
- No registry prefix needed (defaults to Docker Hub)
# Pull latest
docker pull blackoutsecure/mlat-client:latest
# Pull specific version
docker pull blackoutsecure/mlat-client:0.4.3mlat-client is a Mode S multilateration client that selectively forwards Mode S messages to a server that resolves the transmitter position by multilateration of the same message received by multiple clients.
The corresponding server code is available at wiedehopf/mlat-server.
Author and maintenance credits (upstream):
- Original author: Oliver Jowett (mutability)
- Current upstream maintainer: wiedehopf (Matthias Wirth)
- Upstream repository and documentation: wiedehopf/mlat-client
This image is published as a multi-arch manifest. Pulling blackoutsecure/mlat-client:latest retrieves the correct image for your host architecture.
The architectures supported by this image are:
| Architecture | Tag |
|---|---|
| x86-64 | amd64-latest |
| arm64 | arm64v8-latest |
docker-compose (recommended, click here for more info)
---
services:
mlat-client:
image: blackoutsecure/mlat-client:latest
container_name: mlat-adsbx
environment:
- TZ=Etc/UTC
- MLAT_CLIENT_INPUT_CONNECT=readsb:30005
- MLAT_CLIENT_SERVER=feed.adsbexchange.com:31090
- MLAT_CLIENT_AUTO_LOCATION=true
- MLAT_CLIENT_USER_ID=myuser
- MLAT_CLIENT_RESULTS=beast,connect,readsb:30104
- LOG_LEVEL=info # debug | info | warn | error | fatal
volumes:
- mlat-config:/config
tmpfs:
- /tmp
- /run:exec
restart: unless-stopped
volumes:
mlat-config:---
services:
readsb:
image: blackoutsecure/readsb:latest
container_name: readsb
environment:
- TZ=Etc/UTC
- READSB_ARGS=--net --device-type rtlsdr --net-bi-port 30004,30104
- READSB_NET_BO_PORT=30005
- READSB_AUTO_LOCATION=true
- READSB_AUTOGAIN=true
# - READSB_BIASTEE=true # enable for powered LNAs (e.g. SAWbird+)
- FEED_PROFILES=adsbexchange
- LOG_LEVEL=info
volumes:
- readsb-config:/config
- readsb-json:/run/readsb
devices:
- /dev/bus/usb:/dev/bus/usb
ports:
- 30001:30001
- 30002:30002
- 30003:30003
- 30004:30004
- 30005:30005
- 30104:30104
privileged: true
tmpfs:
- /tmp
- /run:exec
restart: unless-stopped
mlat-client:
image: blackoutsecure/mlat-client:latest
container_name: mlat-adsbx
depends_on:
- readsb
environment:
- TZ=Etc/UTC
- MLAT_CLIENT_INPUT_CONNECT=readsb:30005
- MLAT_CLIENT_SERVER=feed.adsbexchange.com:31090
- MLAT_CLIENT_AUTO_LOCATION=true
- MLAT_CLIENT_USER_ID=myuser
- MLAT_CLIENT_RESULTS=beast,connect,readsb:30104
- MLAT_CLIENT_UUID_FILE=/readsb-config/feed-uuid-adsbexchange
- LOG_LEVEL=info
volumes:
- mlat-config:/config
- readsb-config:/readsb-config:ro
tmpfs:
- /tmp
- /run:exec
restart: unless-stopped
volumes:
readsb-config:
readsb-json:
mlat-config:Note: When both services are in the same compose file, Docker Compose creates a shared network automatically — no extra network configuration needed. If you run mlat-client from a separate compose file, create an external network so both containers can communicate:
docker network create adsbThen add to each compose file:
networks: adsb: external: trueAnd add
networks: [adsb]to each service.
docker-cli (click here for more info)
docker run -d \
--name=mlat-client \
-e TZ=Etc/UTC \
-e MLAT_CLIENT_INPUT_CONNECT=readsb:30005 \
-e MLAT_CLIENT_SERVER=feed.adsbexchange.com:31090 \
-e MLAT_CLIENT_LAT=51.5 \
-e MLAT_CLIENT_LON=-0.1 \
-e MLAT_CLIENT_ALT=50m \
-e MLAT_CLIENT_USER_ID=myuser \
-e MLAT_CLIENT_RESULTS="beast,connect,readsb:30104" \
-v /path/to/mlat-client/config:/config \
--restart unless-stopped \
blackoutsecure/mlat-client:latestThis image can be deployed to Balena-powered IoT devices using the included docker-compose.yml file (which contains the required Balena labels):
balena push <your-app-slug>For deployment via the web interface, use the deploy button in this repository. See Balena documentation for details.
| Parameter | Description | Example |
|---|---|---|
-e MLAT_CLIENT_LAT= |
Receiver latitude in decimal degrees (auto-detected if empty and MLAT_CLIENT_AUTO_LOCATION=true) |
51.5 |
-e MLAT_CLIENT_LON= |
Receiver longitude in decimal degrees (auto-detected if empty and MLAT_CLIENT_AUTO_LOCATION=true) |
-0.1 |
-e MLAT_CLIENT_ALT= |
Receiver altitude with unit (m or ft); auto-detected from terrain elevation when MLAT_CLIENT_AUTO_ALT=true and alt is not set |
50m |
-e MLAT_CLIENT_USER_ID= |
User identifier for the MLAT server | myuser |
| Parameter | Default | Description |
|---|---|---|
-e TZ=Etc/UTC |
Etc/UTC |
Timezone (TZ database) |
-e MLAT_CLIENT_INPUT_CONNECT= |
readsb:30005 |
Beast data source (host:port) |
-e MLAT_CLIENT_SERVER= |
feed.adsbexchange.com:31090 |
Multilateration server (host:port) |
-e MLAT_CLIENT_RESULTS= |
(none) | Results output destination(s) |
-e MLAT_CLIENT_UUID= |
(none) | UUID sent to the server |
-e MLAT_CLIENT_UUID_FILE= |
/config/feed-uuid-adsbexchange |
Path to UUID file |
-e MLAT_CLIENT_STATS_JSON= |
(none) | Path for stats JSON output |
-e MLAT_CLIENT_PRIVACY= |
false |
Hide receiver on coverage maps |
-e MLAT_CLIENT_NO_UDP= |
false |
Disable UDP transport |
-e MLAT_CLIENT_AUTO_LOCATION= |
true |
Auto-detect lat/lon via IP geolocation when not explicitly set |
-e MLAT_CLIENT_AUTO_ALT= |
true |
Auto-detect altitude via terrain elevation when lat/lon are available but alt is not set |
-e MLAT_CLIENT_LOG_TIMESTAMPS= |
true |
Print timestamps in logs |
-e MLAT_CLIENT_ARGS= |
(none) | Raw arguments (overrides individual env vars) |
-e MLAT_CLIENT_USER= |
root |
Runtime user for the container |
-e PUID=911 |
911 |
User ID for non-root operation |
-e PGID=911 |
911 |
Group ID for non-root operation |
| Volume | Description | Required |
|---|---|---|
-v /config |
Configuration and persistent data | Recommended |
Environment variables are set using -e flags in docker run or the environment: section in docker-compose.
You can configure the mlat-client in two ways:
-
Individual environment variables (recommended): Set
MLAT_CLIENT_LAT,MLAT_CLIENT_LON, etc. The container service script assembles the command-line arguments. -
Raw arguments: Set
MLAT_CLIENT_ARGSto the full argument string. This overrides the individual environment variables.
When MLAT_CLIENT_AUTO_LOCATION is true (the default) and MLAT_CLIENT_LAT/MLAT_CLIENT_LON are not set, the container will automatically detect your approximate latitude and longitude using IP-based geolocation via ip-api.com.
- Latitude and longitude are resolved from your container's public IP address via ip-api.com
- Explicit
MLAT_CLIENT_LAT/MLAT_CLIENT_LONvalues always take priority - Set
MLAT_CLIENT_AUTO_LOCATION=falseto disable this feature entirely
Note: IP-based geolocation is approximate (typically city-level accuracy). For best MLAT results, set your exact coordinates manually.
Example — auto-detect location:
environment:
- MLAT_CLIENT_AUTO_LOCATION=true # default, can be omitted
# MLAT_CLIENT_LAT, MLAT_CLIENT_LON, MLAT_CLIENT_ALT left unset
- MLAT_CLIENT_USER_ID=myuser
- MLAT_CLIENT_SERVER=feed.adsbexchange.com:31090
- MLAT_CLIENT_INPUT_CONNECT=readsb:30005
- MLAT_CLIENT_RESULTS=beast,connect,readsb:30104When MLAT_CLIENT_AUTO_ALT is true (the default) and MLAT_CLIENT_ALT is not set, the container will automatically detect your ground elevation using the Open-Meteo Elevation API. This works with both auto-detected and explicitly provided coordinates.
- Altitude is resolved from lat/lon using the Open-Meteo Elevation API (ground elevation in metres); falls back to
0mon failure - Explicit
MLAT_CLIENT_ALTalways takes priority - Set
MLAT_CLIENT_AUTO_ALT=falseto disable this feature
Note on altitude: The auto-detected altitude represents ground elevation at the detected coordinates, not your antenna height above sea level. For accurate MLAT, your altitude should include the height of your antenna above ground. For example, if ground elevation is
50mand your antenna is on a10mrooftop mast, setMLAT_CLIENT_ALT=60m. When relying on auto-detection, consider adding your antenna height manually for better multilateration accuracy.
Example — explicit lat/lon with auto-detected altitude:
environment:
- MLAT_CLIENT_LAT=51.5
- MLAT_CLIENT_LON=-0.1
# MLAT_CLIENT_ALT left unset — auto-detected from terrain elevation
- MLAT_CLIENT_AUTO_ALT=true # default, can be omitted
- MLAT_CLIENT_USER_ID=myuser
- MLAT_CLIENT_SERVER=feed.adsbexchange.com:31090
- MLAT_CLIENT_INPUT_CONNECT=readsb:30005
- MLAT_CLIENT_RESULTS=beast,connect,readsb:30104The MLAT_CLIENT_RESULTS variable accepts output specifications in the format:
format,type,address:port
Common examples:
| Results Value | Description |
|---|---|
beast,connect,readsb:30104 |
Beast format, connect to readsb on port 30104 |
beast,listen,30105 |
Beast format, listen on port 30105 |
basestation,listen,31003 |
SBS/BaseStation format, listen on port 31003 |
beast,connect,localhost:30104 |
Beast format, connect to localhost |
- Anything that produces Beast-format output with a 12MHz clock:
- readsb, dump1090-mutability, dump1090-fa
- an actual Mode-S Beast
- airspy_adsb in Beast output mode
- Radarcape in 12MHz mode
- Radarcape in GPS mode
By default, this container runs as root.
Root mode (default):
- No
PUIDorPGIDneeded
Non-root mode (advanced):
- Set
MLAT_CLIENT_USERto your username - Provide matching
PUIDandPGIDvalues - Defaults to
911:911if omitted
The container runs mlat-client, connecting to a Beast-format data source and forwarding Mode S messages to a multilateration server.
- Multilateration: Positions resolved by correlating timestamps from multiple receivers
- Network-only: No hardware required — connects to readsb/dump1090 via network
- Multiple Output Formats: Beast, SBS/BaseStation output support
- Automatic Reconnection: Handles connection interruptions gracefully
- Privacy Mode: Option to hide receiver location on coverage maps
Common environment variable combinations:
# Standard ADSBexchange feed
-e MLAT_CLIENT_INPUT_CONNECT=readsb:30005
-e MLAT_CLIENT_SERVER=feed.adsbexchange.com:31090
-e MLAT_CLIENT_RESULTS=beast,connect,readsb:30104
# Custom MLAT server with privacy
-e MLAT_CLIENT_SERVER=mlat.myserver.com:31090
-e MLAT_CLIENT_PRIVACY=true
# With UUID file for persistent identity (default: /config/feed-uuid-adsbexchange)
-e MLAT_CLIENT_UUID_FILE=/config/feed-uuid-adsbexchange
# With statistics output
-e MLAT_CLIENT_STATS_JSON=/config/mlat-client/stats.jsonFor all available options, see the mlat-client documentation.
Check logs:
docker logs mlat-client
docker logs mlat-client --tail 50 -f # Follow last 50 linesCommon causes:
- Missing required arguments: Ensure
MLAT_CLIENT_LAT,MLAT_CLIENT_LON,MLAT_CLIENT_ALT, andMLAT_CLIENT_USER_IDare set (or leaveLAT/LON/ALTempty withMLAT_CLIENT_AUTO_LOCATION=trueto auto-detect via IP) - Invalid altitude format: Use suffix
m(metres) orft(feet), e.g.50mor160ft - Configuration error: Check
MLAT_CLIENT_ARGSsyntax - Auto-location failed: If IP geolocation fails (no internet, API down), set coordinates manually
Verify the Beast data source is reachable:
docker exec mlat-client nc -zv readsb 30005If connection fails:
- Check that readsb/dump1090 is running and exposing Beast output on port 30005
- Verify both containers are on the same Docker network
- Check for firewall rules blocking the connection
Verify server connectivity:
docker exec mlat-client nc -zv feed.adsbexchange.com 31090If connection fails:
- Check internet connectivity from the container
- Verify the server address and port
- The MLAT server may be temporarily unavailable
If the client connects but no positions are resolved:
- Ensure your location (
LAT/LON/ALT) is accurate - MLAT requires multiple receivers seeing the same aircraft — results depend on receiver density in your area
- Check that your Beast data source is providing valid timestamps
- Review logs for synchronization status
- Check upstream mlat-client documentation
- Review container logs:
docker logs -f mlat-client - Open an issue on GitHub
This project uses semantic versioning:
- Releases published on GitHub Releases
- Multi-arch images (amd64, arm64v8) built automatically
- Docker Hub tags: version-specific,
latest, and architecture-specific
Update to latest:
docker pull blackoutsecure/mlat-client:latest
docker-compose up -d # if using composeCheck image version:
docker inspect -f '{{ index .Config.Labels "build_version" }}' blackoutsecure/mlat-client:latest- Questions: GitHub Issues
- Bug Reports: Include Docker version, container logs, and reproduction steps
- Upstream Documentation: mlat-client on GitHub
- Community: LinuxServer.io Discord
Get help:
docker logs mlat-client # View container logs
docker exec -it mlat-client /bin/bash # Access container shell
docker inspect blackoutsecure/mlat-client # Check image detailsSponsored and maintained by Blackout Secure
Upstream project: wiedehopf/mlat-client Container patterns: LinuxServer.io
| Resource | Link |
|---|---|
| Docker Hub | blackoutsecure/mlat-client |
| Balena Hub | mlat-client block |
| GitHub Issues | Report bugs or request features |
| GitHub Releases | Download releases |
| Resource | Link |
|---|---|
| mlat-client | wiedehopf/mlat-client |
| mlat-server | wiedehopf/mlat-server |
| readsb | wiedehopf/readsb |
| LinuxServer.io | linuxserver.io |
This project is licensed under the GNU General Public License v3.0 or later - see the LICENSE file for details.
The mlat-client application itself is also licensed under GPL-3.0-or-later. For more information, see the mlat-client repository.
Made with ❤️ by Blackout Secure
