Skip to content

Latest commit

 

History

History
563 lines (425 loc) · 20.8 KB

File metadata and controls

563 lines (425 loc) · 20.8 KB

mlat-client logo

docker-mlat-client

LinuxServer.io–style containerized client forwarding Mode S messages for aircraft multilateration

GitHub Stars Docker Pulls GitHub Release Release CI Publish CI License: GPL v3

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.

Overview

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:

Table of Contents

Quick Start

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:latest

For compose files, balena, and more examples, see Usage below.

Image Availability

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.3

About The mlat-client Application

mlat-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):

Supported Architectures

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

Usage

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:

docker-compose with readsb (full ADS-B + MLAT stack)

---
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 adsb

Then add to each compose file:

networks:
  adsb:
    external: true

And add networks: [adsb] to each service.

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:latest

Balena Deployment

This 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.

Parameters

Environment Variables (Required)

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

Environment Variables (Optional)

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

Storage Mounts

Volume Description Required
-v /config Configuration and persistent data Recommended

Configuration

Environment variables are set using -e flags in docker run or the environment: section in docker-compose.

Individual Environment Variables vs MLAT_CLIENT_ARGS

You can configure the mlat-client in two ways:

  1. Individual environment variables (recommended): Set MLAT_CLIENT_LAT, MLAT_CLIENT_LON, etc. The container service script assembles the command-line arguments.

  2. Raw arguments: Set MLAT_CLIENT_ARGS to the full argument string. This overrides the individual environment variables.

Automatic Location Detection

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_LON values always take priority
  • Set MLAT_CLIENT_AUTO_LOCATION=false to 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:30104

Automatic Altitude Detection

When 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 0m on failure
  • Explicit MLAT_CLIENT_ALT always takes priority
  • Set MLAT_CLIENT_AUTO_ALT=false to 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 50m and your antenna is on a 10m rooftop mast, set MLAT_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:30104

Results Output Format

The 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

Supported Receivers

  • 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

User / Group Identifiers

By default, this container runs as root.

Root mode (default):

  • No PUID or PGID needed

Non-root mode (advanced):

  • Set MLAT_CLIENT_USER to your username
  • Provide matching PUID and PGID values
  • Defaults to 911:911 if omitted

Application Setup

The container runs mlat-client, connecting to a Beast-format data source and forwarding Mode S messages to a multilateration server.

Key Features

  • 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

Customizing Configuration

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.json

For all available options, see the mlat-client documentation.

Troubleshooting

Container won't start or exits immediately

Check logs:

docker logs mlat-client
docker logs mlat-client --tail 50 -f  # Follow last 50 lines

Common causes:

  • Missing required arguments: Ensure MLAT_CLIENT_LAT, MLAT_CLIENT_LON, MLAT_CLIENT_ALT, and MLAT_CLIENT_USER_ID are set (or leave LAT/LON/ALT empty with MLAT_CLIENT_AUTO_LOCATION=true to auto-detect via IP)
  • Invalid altitude format: Use suffix m (metres) or ft (feet), e.g. 50m or 160ft
  • Configuration error: Check MLAT_CLIENT_ARGS syntax
  • Auto-location failed: If IP geolocation fails (no internet, API down), set coordinates manually

Cannot connect to Beast source

Verify the Beast data source is reachable:

docker exec mlat-client nc -zv readsb 30005

If 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

Cannot connect to MLAT server

Verify server connectivity:

docker exec mlat-client nc -zv feed.adsbexchange.com 31090

If connection fails:

  • Check internet connectivity from the container
  • Verify the server address and port
  • The MLAT server may be temporarily unavailable

No MLAT results

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

Getting help

Release & Versioning

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 compose

Check image version:

docker inspect -f '{{ index .Config.Labels "build_version" }}' blackoutsecure/mlat-client:latest

Support & Getting Help

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 details

Sponsor & Credits

Sponsored and maintained by Blackout Secure

Upstream project: wiedehopf/mlat-client Container patterns: LinuxServer.io

References

Project Resources

Resource Link
Docker Hub blackoutsecure/mlat-client
Balena Hub mlat-client block
GitHub Issues Report bugs or request features
GitHub Releases Download releases

Upstream & Related

Resource Link
mlat-client wiedehopf/mlat-client
mlat-server wiedehopf/mlat-server
readsb wiedehopf/readsb
LinuxServer.io linuxserver.io

Technical Resources

License

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