Skip to content

Repository files navigation

readsb logo

blackoutsecure/readsb

GitHub StarsDocker PullsGitHub ReleaseLicense: GPL v3Made by BlackoutSecure

Unofficial community image for readsb, built with LinuxServer.io style container patterns (s6, hardened defaults, practical runtime options) for RTL-SDR ADS-B workloads. Sponsored and maintained by Blackout Secure.

Important

This repository is not an official LinuxServer.io image release. Want to help make it an officially supported LinuxServer.io Community image? Add your support in linuxserver/discussions/108.

Links: Docker Hub · Balena block · GitHub · Upstream readsb

balena deploy button


Table of Contents


Quick Start

5-minute RTL-SDR receiver setup:

docker run -d \
--name=readsb \
--restart unless-stopped \
-e TZ=Etc/UTC \
-e READSB_DEVICE_TYPE=rtlsdr \
-p 30001:30001 \
-p 30002:30002 \
-p 30003:30003 \
-p 30004:30004 \
-p 30005:30005 \
-p 30104:30104 \
-v readsb-config:/config \
-v readsb-json:/run/readsb \
--device=/dev/bus/usb:/dev/bus/usb \
--security-opt no-new-privileges:true \
blackoutsecure/readsb:latest

Access live JSON output: docker exec readsb cat /run/readsb/aircraft.json | jq .

For compose files, balena, network-only mode, and more examples, see Usage below.


Image Availability

Docker Hub (Recommended):

  • All images published to Docker Hub
  • Simple pull command: docker pull blackoutsecure/readsb:latest
  • Multi-arch support: amd64, arm64
  • No registry prefix needed (defaults to Docker Hub)
# Pull latest
docker pull blackoutsecure/readsb
# Pull specific version
docker pull blackoutsecure/readsb:1.2.3
# Pull architecture-specific (rarely needed)
docker pull blackoutsecure/readsb:latest@amd64

About The readsb Application

readsb is an ADS-B decoder often described upstream as an "ADS-B decoder swiss knife".

It is a detached fork lineage used by many ADS-B receivers and related tooling, with network outputs, JSON/API features, and broad SDR support used for local receivers and large-scale feed/aggregation workflows.

Author and maintenance credits (upstream):

  • Primary upstream maintainer: wiedehopf (Matthias Wirth)
  • Upstream credits/history lineage: antirez (original dump1090), Malcom Robb, mutability (dump1090-mutability / dump1090-fa), Mictronics (readsb fork), and wiedehopf (current fork)
  • Upstream repository and documentation: wiedehopf/readsb

Supported Architectures

This image is published as a multi-arch manifest. Pulling blackoutsecure/readsb:latest retrieves the correct image for your host architecture.

The architectures supported by this image are:

ArchitectureTag
x86-64amd64-latest
arm64arm64v8-latest

Usage

docker-compose (recommended, click here for more info)

---
services:
readsb:
image: blackoutsecure/readsb:latestcontainer_name: readsbenvironment:
- TZ=Etc/UTC
- READSB_DEVICE_TYPE=rtlsdr
- READSB_NET_BI_PORT=30004,30104 # Beast input ports
- READSB_NET_BO_PORT=30005 # Beast output port# - READSB_BIASTEE=true # enable for powered LNAs (e.g. SAWbird+)
- LOG_LEVEL=info # debug | info | warn | error | fatalvolumes:
- /path/to/readsb/config:/config
- /path/to/readsb/json:/run/readsbports:
- 30001:30001# Raw protocol (TCP)
- 30002:30002# Raw protocol input (TCP)
- 30003:30003# SBS protocol (TCP)
- 30004:30004# Beast input (TCP)
- 30005:30005# Beast output (TCP)
- 30104:30104# Beast input (TCP)devices:
- /dev/bus/usb:/dev/bus/usbsecurity_opt:
- no-new-privileges:truetmpfs:
- /tmp
- /run:execrestart: unless-stopped

docker-compose with RTL-SDR over network (e.g., from a remote receiver)

---
services:
readsb:
image: blackoutsecure/readsb:latestcontainer_name: readsbenvironment:
- TZ=Etc/UTC
- READSB_EXTRA_ARGS=--lon <longitude> --lat <latitude>volumes:
- /path/to/readsb/config:/config
- /path/to/readsb/json:/run/readsbports:
- 30001:30001
- 30002:30002
- 30003:30003
- 30004:30004
- 30005:30005
- 30104:30104security_opt:
- no-new-privileges:truetmpfs:
- /tmp
- /run:execrestart: unless-stopped

docker-compose with dump978 sidecar (dual 1090/978 MHz, US only)

---
services:
readsb:
image: blackoutsecure/readsb:latestcontainer_name: readsbenvironment:
- TZ=Etc/UTC
- READSB_DEVICE_TYPE=rtlsdr# - READSB_BIASTEE=true # enable for powered LNAs (e.g. SAWbird+)
- FEED_UAT_INPUT=dump978:30978# - READSB_DEVICE=00001090 # pin 1090 dongle by serialvolumes:
- readsb-config:/config
- readsb-json:/run/readsbdevices:
- /dev/bus/usb:/dev/bus/usbports:
- 30001:30001
- 30002:30002
- 30003:30003
- 30004:30004
- 30005:30005
- 30104:30104security_opt:
- no-new-privileges:truetmpfs:
- /tmp
- /run:execrestart: unless-stoppeddepends_on:
- dump978dump978:
image: blackoutsecure/dump978:latestcontainer_name: dump978environment:
- TZ=Etc/UTC
- DUMP978_SDR=driver=rtlsdr,serial=00000978
- DUMP978_PROFILE=adsbexchangevolumes:
- dump978-config:/config
- dump978-run:/run/dump978-faports:
- 8978:8978
- 30978:30978
- 30979:30979devices:
- /dev/bus/usb:/dev/bus/usbsecurity_opt:
- no-new-privileges:truetmpfs:
- /tmp
- /run:execrestart: unless-stoppedvolumes:
readsb-config:
readsb-json:
dump978-config:
dump978-run:

Note: UAT 978 MHz is US-only (below 18,000 ft). Tag your dongles with rtl_eeprom -d 0 -s 00001090 and rtl_eeprom -d 1 -s 00000978. See blackoutsecure/docker-dump978 for full dump978 documentation.

docker run -d \
--name=readsb \
-e TZ=Etc/UTC \
-e READSB_DEVICE_TYPE=rtlsdr \
-p 30001:30001 \
-p 30002:30002 \
-p 30003:30003 \
-p 30004:30004 \
-p 30005:30005 \
-p 30104:30104 \
-v /path/to/readsb/config:/config \
-v /path/to/readsb/json:/run/readsb \
--device=/dev/bus/usb:/dev/bus/usb \
--security-opt no-new-privileges:true \
--restart unless-stopped \
blackoutsecure/readsb: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

Ports

ParameterFunction
-p 30001:30001Raw protocol output (TCP)
-p 30002:30002Raw protocol input (TCP)
-p 30003:30003SBS protocol compatible output (TCP)
-p 30004:30004Beast protocol input (TCP)
-p 30005:30005Beast protocol output (TCP)
-p 30104:30104Beast protocol input (TCP)

Environment Variables

ParameterFunctionRequired
-e TZ=Etc/UTCTimezone (TZ database)Optional
-e READSB_NET=trueEnable TCP networking (Beast, raw, SBS listeners). Default: true. Set to false for decode-only mode.Optional
-e READSB_DEVICE_TYPE=rtlsdrSDR device type (rtlsdr, modesbeast, etc.). Leave empty for network-only mode. Maps to --device-type.Optional
-e READSB_EXTRA_ARGS=Additional readsb arguments appended after all env-var-driven options (e.g. --freq-correction 3).Optional
-e READSB_USER=abcRuntime user (default: abc). USB permissions are fixed automatically during init.Optional
-e PUID=1000User ID for file ownership (LinuxServer.io base image standard)Optional
-e PGID=1000Group ID for file ownership (LinuxServer.io base image standard)Optional
-e READSB_NET_BI_PORT=Beast protocol input port(s), comma-separated (e.g. 30004,30104). Maps to --net-bi-port.Optional
-e READSB_NET_BO_PORT=Beast protocol output port(s), comma-separated (e.g. 30005). Maps to --net-bo-port.Optional
-e READSB_DEVICE=RTL-SDR device index or serial for 1090 MHz (overrides auto-detection)Optional
-e FEED_PROFILES=Comma-separated feed exchanges (e.g. adsbexchange,adsb-fi). Defaults to adsbexchange if unset.Optional
-e FEED_UUID_ADSBEXCHANGE=Per-profile UUID override. Use uppercase profile name with hyphens as underscores (e.g. FEED_UUID_ADSB_FI, FEED_UUID_AIRPLANESLIVE). Stored in /config/feed-uuid-<profile>.Optional
-e FEED_STATS_ENABLED=trueEnable periodic console stats logging and ADSBx RSSI/stats upload. Console stats log aircraft count, positions, and message totals. ADSBx upload activates automatically when adsbexchange is in FEED_PROFILES, using the same UUID as the beast feed.Optional
-e STATS_LOG_INTERVAL=120Console stats logging interval in seconds (default: 120 = every 2 minutes)Optional
-e FEED_UAT_INPUT=UAT 978 MHz source as host:port (e.g. dump978:30978). Requires docker-dump978 sidecar. US only.Optional
-e FEED_LAT=Receiver latitude (e.g. 47.6062). Fallback if --lat not in READSB_EXTRA_ARGS.Optional
-e FEED_LON=Receiver longitude (e.g. -122.3321). Fallback if --lon not in READSB_EXTRA_ARGS.Optional
-e READSB_AUTO_LOCATION=trueAuto-detect latitude/longitude via IP geolocation when FEED_LAT/FEED_LON not set.Optional
-e READSB_AUTOGAIN=trueEnable readsb built-in autogain (--gain auto). Default: true. Set to false to disable.Optional
-e READSB_GAIN=Set a fixed RTL-SDR gain value (e.g. 40.2). Overrides READSB_AUTOGAIN.Optional
-e READSB_BIASTEE=falseEnable bias-T DC voltage on the RTL-SDR coax to power active antennas/LNAs (default: false). Only enable if your antenna requires DC power.Optional
-e LOG_LEVEL=infoMinimum log verbosity: debug, info (default), warn, error, fatal. Set to debug for verbose operational detail, or warn to suppress routine informational messages.Optional

Storage Mounts

ParameterFunctionRequired
-v /configConfiguration and persistent dataRecommended
-v /run/readsbJSON output directoryRecommended

Devices

ParameterFunctionRequired
--device=/dev/bus/usb:/dev/bus/usbRTL-SDR USB device accessRequired (for RTL-SDR local RX)

Volume Details

The container uses two volumes for data persistence and output:

/config — Configuration & Persistence

  • Required: No (container runs without it, but state is lost on restart)
  • Purpose: Stores persistent data including aircraft database cache and application state
  • Contents:
    • aircraft.csv.gz (aircraft identification database)
    • Temporary caches and application state
    • Any custom configuration files
  • Example: -v /path/to/readsb/config:/config or -v readsb-config:/config

/run/readsb — JSON Output

  • Required: No (but needed to access real-time ADS-B data)
  • Purpose: Real-time JSON protocol output consumed by other applications and visualization tools
  • Contents:
    • aircraft.json (current aircraft with positions and altitudes)
    • receiver.json (receiver stats and information)
    • Other protocol outputs
  • Update frequency: Multiple times per second
  • Example: -v /path/to/readsb/json:/run/readsb or -v readsb-json:/run/readsb

Best Practices

  • For persistence: Always use named volumes or host paths for /config to preserve aircraft database between container restarts
  • For JSON access: Mount /run/readsb to a host path to query data from other containers or host processes
  • Alternative (tmpfs): Can use tmpfs mounts for temp directories, but JSON output will be lost when container stops

Volume Mount Examples

Named volumes (recommended for single-host deployments):

volumes:
- config:/config
- json:/run/readsb

Host paths (for direct file access):

volumes:
- /var/lib/readsb/config:/config
- /var/lib/readsb/json:/run/readsb

Accessing JSON data from host:

docker exec readsb cat /run/readsb/aircraft.json | jq '.aircraft | length'

Configuration

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


User / Group Identifiers

By default, this container runs as the LSIO abc user (non-root) for better security isolation. The abc user is created by the LinuxServer.io base image with UID/GID 911 and remapped at container start via PUID/PGID.

Non-root mode (default, recommended):

  • readsb runs as the abc user by default
  • RTL-SDR USB device permissions are automatically fixed during container init — no manual configuration needed
  • Set PUID and PGID only if you need file ownership to match a specific host user

Root mode (fallback):

  • Set READSB_USER=root if needed for other reasons
  • USB permissions are handled automatically regardless of user

Application Setup

The container runs readsb with network support and automatic RTL-SDR device detection by default.

Key Features

  • JSON Output: ADS-B data is output as JSON to /run/readsb/ and updated frequently
  • RTL-SDR Support: USB devices are auto-detected when passed to the container; permissions are fixed automatically
  • Aircraft Database: Includes tar1090 aircraft database for accurate identification
  • Automatic Gain: readsb's built-in autogain (--gain auto) is enabled by default (READSB_AUTOGAIN=true) — adjusts gain at the IQ sample level every ~0.5s, in-process without restarts. Set READSB_AUTOGAIN=false to disable.
  • Bias-T Power: Optional DC voltage on coax for active antennas with built-in LNAs (see Bias-T Power for Active Antennas)
  • Docker HEALTHCHECK: Built-in health monitoring — marks container unhealthy if aircraft.json stops updating
  • Periodic Stats: Logs aircraft count, positions, and message totals every 2 minutes (configurable via STATS_LOG_INTERVAL). When adsbexchange is in FEED_PROFILES, also uploads RSSI/stats data to ADSBx every 5 seconds using the same UUID as the beast feed. Per-upload confirmations are debug-level.
  • Log Verbosity: Set LOG_LEVEL to control log output: debug, info (default), warn, error, fatal
  • Feed Status URLs: Init logs include verification URLs for each active feed profile
  • RTL-SDR Tools: rtl_test, rtl_eeprom, and rtl_biast available inside the container for diagnostics, dongle tagging, and bias-T control

Extra Arguments (READSB_EXTRA_ARGS)

Most readsb settings have dedicated READSB_* env vars (READSB_NET, READSB_DEVICE_TYPE, READSB_GAIN, READSB_AUTOGAIN, READSB_BIASTEE, READSB_NET_BI_PORT, etc.). Use READSB_EXTRA_ARGS for anything not covered by a dedicated variable — these are appended to the end of the command line after all env-var-driven options.

Examples:

# Frequency correction
-e READSB_EXTRA_ARGS="--freq-correction 10"# Location and range
-e READSB_EXTRA_ARGS="--lat 51.5 --lon -0.1 --max-range 350"# See autogain decisions in the log
-e READSB_EXTRA_ARGS="--gain auto-verbose"# Multiple extra options
-e READSB_EXTRA_ARGS="--freq-correction 3 --max-range 300 --ppm 2"

For all available readsb options, see the readsb documentation.

JSON Output Files

  • aircraft.json - Current aircraft data with positions, callsigns, and altitudes
  • receiver.json - Statistics and receiver information

Supported Modes

  • Read-only filesystem: Supported when JSON and temp directories are mounted to volumes or tmpfs
  • Non-root user: Supported by default — RTL-SDR USB permissions are fixed automatically during init

SDR Device Selection

The container auto-detects RTL-SDR dongles at startup and assigns the 1090 MHz device automatically using the community serial-number convention.

Auto-detection (default):

  • Single dongle: assumed to be 1090 MHz
  • Multiple dongles: serial containing 978 or uat is identified as UAT (informational); first non-UAT dongle is assigned as 1090

Manual override:

Set READSB_DEVICE to a device index or serial number:

environment:
- READSB_DEVICE=0 # by device index
- READSB_DEVICE=00001090 # by serial number

UAT 978 MHz (US only):

readsb only decodes 1090 MHz. For 978 MHz UAT, run blackoutsecure/docker-dump978 as a sidecar container and set FEED_UAT_INPUT=dump978:30978 to merge UAT aircraft into readsb's unified output. See the dump978 sidecar compose example above.

Tagging dongle serials:

rtl_eeprom -d 0 -s 00001090 # tag first dongle as 1090
rtl_eeprom -d 1 -s 00000978 # tag second dongle as UAT

Feed Profiles

FEED_PROFILES controls data forwarding to ADS-B data aggregators directly from the readsb container. It defaults to adsbexchange if unset. No separate feed container is needed — readsb forwards data using --net-connector arguments appended automatically per profile. Set FEED_PROFILES to an empty string to disable all feeding. MLAT positioning requires a separate mlat-client container per exchange (see MLAT table below).

Environment Variables

VariableDefaultDescription
FEED_PROFILESadsbexchangeComma-separated list of exchanges to feed. Options: adsbexchange, adsb-fi, airplaneslive, planewatch, opensky, flyitalyadsb, adsbhub, radarplane. Defaults to adsbexchange if unset. Set to empty string to disable feeding.
FEED_UUID_<PROFILE>(auto-generated)Per-profile UUID. Use uppercase profile name with hyphens as underscores (e.g. FEED_UUID_ADSBEXCHANGE, FEED_UUID_ADSB_FI, FEED_UUID_AIRPLANESLIVE). Auto-generated on first run and stored in /config/feed-uuid-<profile>.
FEED_STATS_ENABLEDtrueEnable periodic console stats logging and ADSBx RSSI/stats upload. ADSBx upload activates automatically when adsbexchange is in FEED_PROFILES, using the same UUID as the beast feed.
STATS_LOG_INTERVAL120Console stats logging interval in seconds. Default is 120 (every 2 minutes). Set to e.g. 60 for 1 min or 300 for 5 min.
FEED_UAT_INPUT(empty)UAT 978 MHz data source as host:port (e.g. dump978:30978). Only applies in the US.
FEED_LAT(empty)Receiver latitude in decimal degrees. Used as fallback if --lat is not in READSB_EXTRA_ARGS.
FEED_LON(empty)Receiver longitude in decimal degrees. Used as fallback if --lon is not in READSB_EXTRA_ARGS.
READSB_AUTO_LOCATIONtrueAuto-detect latitude/longitude via IP geolocation when FEED_LAT/FEED_LON are not set. Set to false to disable.
READSB_NETtrueEnable TCP networking (Beast, raw, SBS listeners). Default: true. Set to false for decode-only mode with no network ports.
READSB_DEVICE_TYPE(empty)SDR device type (rtlsdr, modesbeast, etc.). Leave empty for network-only mode. Maps to --device-type.
READSB_AUTOGAINtrueEnable readsb built-in autogain (--gain auto). Adjusts gain at the IQ sample level every ~0.5s. Set to false to disable (uses max gain unless READSB_GAIN is set).
READSB_GAIN(empty)Set a fixed RTL-SDR gain value (e.g. 40.2). Overrides READSB_AUTOGAIN.
READSB_NET_BI_PORT(none)Beast protocol input port(s), comma-separated (e.g. 30004,30104). Maps to --net-bi-port.
READSB_NET_BO_PORT(none)Beast protocol output port(s), comma-separated (e.g. 30005). Maps to --net-bo-port.
READSB_BIASTEEfalseBias-T DC power for active antennas. See Bias-T Power for Active Antennas.
LOG_LEVELinfoMinimum log verbosity: debug, info, warn, error, fatal. Set to debug for verbose operational detail.

Supported Profiles — Feed Connectors

Profile name--net-connector value
adsbexchangefeed1.adsbexchange.com,30004,beast_reduce_out,feed2.adsbexchange.com,64004
adsb-fifeed.adsb.fi,30004,beast_reduce_out
airplaneslivefeed.airplanes.live,30004,beast_reduce_out
planewatchatc.plane.watch,30004,beast_reduce_out
openskyfeed.opensky-network.org,30005,beast_reduce_out
flyitalyadsbdati.flyitalyadsb.com,4905,beast_reduce_out
adsbhubdata.adsbhub.org,5002,beast_reduce_out
radarplanefeed.radarplane.com,30001,beast_reduce_out

Supported Profiles — MLAT Servers

MLAT (multilateration) is optional — your ADS-B feed and stats work without it. MLAT uses timing data from multiple receivers to locate aircraft that don't broadcast GPS positions. If you want to enable it, run a separate mlat-client container per exchange.

mlat-client Environment Variables

VariableDefaultDescription
MLAT_CLIENT_INPUT_CONNECTreadsb:30005Beast data source (host:port)
MLAT_CLIENT_SERVERfeed.adsbexchange.com:31090Multilateration server (host:port) — see table below
MLAT_CLIENT_LAT(auto-detected)Receiver latitude in decimal degrees. Auto-detected via IP geolocation if empty and MLAT_CLIENT_AUTO_LOCATION=true.
MLAT_CLIENT_LON(auto-detected)Receiver longitude in decimal degrees. Auto-detected via IP geolocation if empty and MLAT_CLIENT_AUTO_LOCATION=true.
MLAT_CLIENT_ALT(auto-detected)Receiver altitude with unit (m or ft). Auto-detected from terrain elevation when lat/lon are auto-detected.
MLAT_CLIENT_USER_ID(required)User identifier / feeder name for the MLAT server
MLAT_CLIENT_RESULTS(none)Results output destination(s), e.g. beast,connect,readsb:30104
MLAT_CLIENT_AUTO_LOCATIONtrueAuto-detect lat/lon via ip-api.com and altitude via Open-Meteo Elevation API when MLAT_CLIENT_LAT/MLAT_CLIENT_LON are not set. Set to false to disable.
MLAT_CLIENT_PRIVACYfalseHide receiver on coverage maps
MLAT_CLIENT_UUID(none)UUID sent to the server
MLAT_CLIENT_UUID_FILE(none)Path to UUID file for persistent identity
MLAT_CLIENT_NO_UDPfalseDisable UDP transport
MLAT_CLIENT_ARGS(none)Raw arguments (overrides individual env vars)

Auto-location: When MLAT_CLIENT_AUTO_LOCATION is true (the default) and MLAT_CLIENT_LAT/MLAT_CLIENT_LON are not set, the mlat-client container automatically detects your approximate location via IP geolocation. Altitude is resolved from terrain elevation at the detected coordinates. Explicit values always take priority. IP-based geolocation is approximate (city-level) — for best MLAT results, set your exact coordinates manually.

Profile nameMLAT server endpoint
adsbexchangefeed.adsbexchange.com:31090
adsb-fifeed.adsb.fi:31090
airplaneslivefeed.airplanes.live:31090
planewatchmlat.plane.watch:31090
flyitalyadsbdati.flyitalyadsb.com:30100
radarplanemlat.radarplane.com:40900

Feeder name: Most aggregators (e.g. ADSBExchange) derive your display name from the --user / MLAT_CLIENT_USER_ID value sent by your mlat-client container. If no mlat-client is connected, the exchange typically auto-generates a random name. To set a custom name, configure MLAT_CLIENT_USER_ID in your mlat-client sidecar (see examples below).

Quick Start — Single Exchange

services:
readsb:
image: blackoutsecure/readsb:latestcontainer_name: readsbenvironment:
- TZ=Etc/UTC
- READSB_DEVICE_TYPE=rtlsdr# - READSB_BIASTEE=true # enable for powered LNAs (e.g. SAWbird+)
- FEED_PROFILES=adsbexchange
- FEED_LAT=51.5074
- FEED_LON=-0.1278volumes:
- readsb-config:/config
- readsb-run:/run/readsbdevices:
- /dev/bus/usb:/dev/bus/usbrestart: unless-stoppedmlat-client:
image: blackoutsecure/mlat-client:latestcontainer_name: mlat-adsbxenvironment:
- MLAT_CLIENT_INPUT_CONNECT=readsb:30005
- MLAT_CLIENT_SERVER=feed.adsbexchange.com:31090
- MLAT_CLIENT_LAT=51.5074 # omit to auto-detect via IP geolocation
- MLAT_CLIENT_LON=-0.1278 # omit to auto-detect via IP geolocation
- MLAT_CLIENT_ALT=50m # omit to auto-detect from terrain elevation# - MLAT_CLIENT_AUTO_LOCATION=true # default — auto-detects lat/lon/alt when not set
- MLAT_CLIENT_USER_ID=myfeeder-london
- MLAT_CLIENT_RESULTS=beast,connect,readsb:30104depends_on: [readsb]restart: unless-stoppedvolumes:
readsb-config:
readsb-run:

Quick Start — Single Exchange (Auto-Location)

Minimal setup — lat/lon/alt are auto-detected from your IP address:

services:
readsb:
image: blackoutsecure/readsb:latestcontainer_name: readsbenvironment:
- TZ=Etc/UTC
- READSB_DEVICE_TYPE=rtlsdr
- FEED_PROFILES=adsbexchangevolumes:
- readsb-config:/config
- readsb-run:/run/readsbdevices:
- /dev/bus/usb:/dev/bus/usbrestart: unless-stoppedmlat-client:
image: blackoutsecure/mlat-client:latestcontainer_name: mlat-adsbxenvironment:
- MLAT_CLIENT_INPUT_CONNECT=readsb:30005
- MLAT_CLIENT_SERVER=feed.adsbexchange.com:31090
- MLAT_CLIENT_USER_ID=myfeeder-london
- MLAT_CLIENT_RESULTS=beast,connect,readsb:30104# MLAT_CLIENT_LAT, MLAT_CLIENT_LON, MLAT_CLIENT_ALT auto-detecteddepends_on: [readsb]restart: unless-stoppedvolumes:
readsb-config:
readsb-run:

Quick Start — Multi-Exchange

services:
readsb:
image: blackoutsecure/readsb:latestcontainer_name: readsbenvironment:
- TZ=Etc/UTC
- READSB_DEVICE_TYPE=rtlsdr# - READSB_BIASTEE=true # enable for powered LNAs (e.g. SAWbird+)
- FEED_PROFILES=adsbexchange,adsb-fi,airplaneslive
- FEED_LAT=51.5074
- FEED_LON=-0.1278volumes:
- readsb-config:/config
- readsb-run:/run/readsbdevices:
- /dev/bus/usb:/dev/bus/usbrestart: unless-stoppedmlat-adsbx:
image: blackoutsecure/mlat-client:latestcontainer_name: mlat-adsbxenvironment:
- MLAT_CLIENT_INPUT_CONNECT=readsb:30005
- MLAT_CLIENT_SERVER=feed.adsbexchange.com:31090
- MLAT_CLIENT_LAT=51.5074 # omit to auto-detect via IP geolocation
- MLAT_CLIENT_LON=-0.1278 # omit to auto-detect via IP geolocation
- MLAT_CLIENT_ALT=50m # omit to auto-detect from terrain elevation
- MLAT_CLIENT_USER_ID=myfeeder-london
- MLAT_CLIENT_RESULTS=beast,connect,readsb:30104depends_on: [readsb]restart: unless-stoppedmlat-adsb-fi:
image: blackoutsecure/mlat-client:latestcontainer_name: mlat-adsb-fienvironment:
- MLAT_CLIENT_INPUT_CONNECT=readsb:30005
- MLAT_CLIENT_SERVER=feed.adsb.fi:31090
- MLAT_CLIENT_LAT=51.5074 # omit to auto-detect
- MLAT_CLIENT_LON=-0.1278 # omit to auto-detect
- MLAT_CLIENT_ALT=50m # omit to auto-detect
- MLAT_CLIENT_USER_ID=myfeeder-london
- MLAT_CLIENT_RESULTS=beast,connect,readsb:30104depends_on: [readsb]restart: unless-stoppedmlat-airplaneslive:
image: blackoutsecure/mlat-client:latestcontainer_name: mlat-airplanesliveenvironment:
- MLAT_CLIENT_INPUT_CONNECT=readsb:30005
- MLAT_CLIENT_SERVER=feed.airplanes.live:31090
- MLAT_CLIENT_LAT=51.5074 # omit to auto-detect
- MLAT_CLIENT_LON=-0.1278 # omit to auto-detect
- MLAT_CLIENT_ALT=50m # omit to auto-detect
- MLAT_CLIENT_USER_ID=myfeeder-london
- MLAT_CLIENT_RESULTS=beast,connect,readsb:30104depends_on: [readsb]restart: unless-stoppedvolumes:
readsb-config:
readsb-run:

Services Requiring Separate Containers

Note

TO DO — Sidecar container images for the services listed below are planned but not yet available. Compose examples and documentation will be added as each image is published.

Some aggregators use proprietary protocols or require authentication that cannot be handled via a simple --net-connector feed profile. These require running a dedicated sidecar container that reads Beast data from readsb:30005.

ServiceContainerAuthStatusNotes
FlightAwareflightaware/piawarefeeder-id🔜 PlannedConnects to readsb:30005 for Beast input. Handles auth, feeding, and MLAT independently.
FlightRadar24fr24feedsharing-key🔜 PlannedProprietary binary. Connects to readsb:30005 in AVR/Beast mode.
Radarboxrbfeedersharing-key🔜 PlannedProprietary binary. Connects to readsb:30005 in Beast mode.
Planefinderpfclientshare-code🔜 PlannedProprietary binary. Connects to readsb:30005 in Beast mode.

Notes

  • MLAT: MLAT (multilateration) positioning is entirely optional — your ADS-B feed works without it. MLAT requires a separate mlat-client container per exchange. The readsb container handles Beast data forwarding only. If you see "MLAT: Not Found" on your feeder status page, that is normal when no mlat-client container is running. See the MLAT server table above for the correct server endpoint per exchange if you want to set it up. The mlat-client container supports automatic location detection (MLAT_CLIENT_AUTO_LOCATION=true, enabled by default) — lat/lon are resolved from your IP address and altitude from terrain elevation, so you can omit MLAT_CLIENT_LAT/MLAT_CLIENT_LON/MLAT_CLIENT_ALT for a quick start. For best MLAT accuracy, set your exact coordinates manually.

  • UUID: Each feed profile gets its own UUID, stored in /config/feed-uuid-<profile> (e.g. /config/feed-uuid-adsbexchange). UUIDs are auto-generated on first run and persisted across container restarts. They are also injected into the s6 container environment (e.g. FEED_UUID_ADSBEXCHANGE) for downstream services. To override, set FEED_UUID_<PROFILE> env vars. To view: docker exec readsb cat /config/feed-uuid-adsbexchange

  • Checking feed status:

    Feed verification URLs are also logged during container startup for each active profile.

Note: IP-based geolocation is approximate (typically city-level accuracy). Elevation is ground-level at the detected coordinates. For best MLAT results, set your exact coordinates manually.

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.


Gain Control

RTL-SDR dongles have a tunable Low-Noise Amplifier (LNA) in the R820T/R820T2 tuner with 29 discrete gain steps from 0.0 to 49.6 dB. Gain directly affects reception quality:

  • Too much gain → the ADC (analog-to-digital converter) overloads, strong nearby signals clip, and you lose both close and distant aircraft
  • Too little gain → weak signals from distant aircraft are buried in the noise floor and never decoded
  • Optimal gain → the noise floor is just above the ADC's quantization noise, maximizing the dynamic range available for ADS-B signals

Gain Modes Comparison

ModeREADSB_AUTOGAINREADSB_GAINHow it worksWhen to use
Autogain (default)true(empty)readsb's built-in algorithm monitors IQ noise floor statistics every ~0.5s and adjusts the R820T tuner gain in-process. Converges quickly on startup, makes fine adjustments as conditions change.Recommended for all users. Best results with minimal effort.
Fixed gaintrue or falsee.g. 40.2A specific gain value from the R820T table is locked in. readsb will not adjust it. READSB_GAIN takes priority over READSB_AUTOGAIN.When you've tested your RF environment and know the optimal value, or when autogain isn't converging well (rare).
Disabled (max gain)false(empty)No --gain flag is passed to readsb. readsb defaults to maximum gain (equivalent to --gain 58 / hardware AGC). The R820T runs at its highest amplification.Not recommended. Overloads most receivers. Only useful for extremely remote locations with zero nearby RF sources.

How Autogain Works

readsb's built-in autogain (--gain auto) is fundamentally different from older shell-script-based approaches:

  1. Operates at the IQ sample level — analyzes raw magnitude data from every USB transfer (~0.5s cycles), not decoded message statistics
  2. Monitors noise floor — tracks noiseLowSamples, noiseHighSamples, and loudEvents per buffer
  3. Adjusts in-process — changes gain via rtlsdr_set_tuner_gain() without restarting readsb
  4. Fast startup convergence — uses a gain sweep multiplier on first run, settling within seconds
  5. Loud signal detection — immediately lowers gain when clipping events are detected

The algorithm's thresholds can be tuned via READSB_EXTRA_ARGS (but there should be no need to change them):

--gain auto-verbose,<lowestGain>,<noiseLowThreshold>,<noiseHighThreshold>,<loudThreshold>

Defaults: --gain auto-verbose,0,34,36,243 (thresholds are 0–256 scale)

Recommended Configuration

For most users (default — no changes needed):

environment:
- READSB_DEVICE_TYPE=rtlsdr# READSB_AUTOGAIN defaults to true — autogain is active

To see autogain decisions in the log:

environment:
- READSB_EXTRA_ARGS=--gain auto-verbose

To set a specific fixed gain:

environment:
- READSB_GAIN=40.2

To disable autogain (max gain — not recommended):

environment:
- READSB_AUTOGAIN=false

Choosing a Fixed Gain Value

If you prefer manual control, use the strong signal percentage as your guide. You can check this inside the container:

docker exec readsb sh -c 'jq ".total.local | {accepted: (.accepted | add), strong: .strong_signals, signal: .signal, noise: .noise}" /run/readsb/stats.json'

Target: ~5% strong signals (messages with RSSI > −3 dBFS). Guidelines from the upstream adsb-wiki:

Strong signal %MeaningAction
> 10%ADC is overloading — you're clipping strong signals and losing distant ones tooDecrease gain
5–10%Slightly high but acceptable, especially near airportsMay be optimal for your location
1–5%Sweet spot — good balance of range and close-in receptionIdeal for most locations
< 1%Gain may be too low — you could be missing weak distant signalsIncrease gain (or this is normal for rural areas with an LNA)
< 0.1%Very low — likely too conservative unless you have an external LNAIncrease gain

Another approach: Aim for the weakest received signal (visible in graphs1090 or tar1090 stats) to be between −30 and −34 dBFS. This ensures you're using the full dynamic range without clipping.

RTL-SDR Gain Table

The R820T/R820T2 tuner supports these discrete gain values (in dB):

 0.0 0.9 1.4 2.7 3.7 7.7 8.7 12.5 14.4 15.7
16.6 19.7 20.7 22.9 25.4 28.0 29.7 32.8 33.8 36.4
37.2 38.6 40.2 42.1 43.4 43.9 44.5 48.0 49.6

Most receivers settle between 25–44 dB depending on antenna, LNA, cable loss, and local RF environment. Common sweet spots:

SetupTypical optimal gain
Bare RTL-SDR + short coax + outdoor antenna38–49.6
RTL-SDR + LNA (e.g. SAWbird+) + outdoor antenna25–38
RTL-SDR + LNA + long coax run (> 10m)33–44
Indoor antenna / window mount42–49.6
Near a major airport (< 10 km)20–33

Tips

  • Start with autogain — it handles 95% of setups correctly with zero configuration
  • Wait at least 5 minutes before evaluating gain performance — stats need time to accumulate
  • After moving your antenna, let autogain re-converge (it will adjust automatically)
  • USB power matters more than gain — voltage drop from long USB cables or shared hubs causes more reception problems than suboptimal gain. See USB topology warnings
  • An external LNA changes everything — if you add a SAWbird+ or similar, you'll likely need lower gain than before (the LNA adds 15–20 dB of external amplification)

Bias-T Power for Active Antennas

Some ADS-B setups use an inline LNA (Low Noise Amplifier) and/or SAW filter module between the antenna and the RTL-SDR dongle. These modules amplify and filter the signal right at the antenna before cable loss degrades it — significantly improving range and aircraft count. They need DC power delivered through the coaxial cable, which is called bias-T (bias tee).

A popular example is the Nooelec SAWbird+ ADS-B — a dual-channel cascaded ultra-low noise amplifier and SAW filter module with dedicated 1090 MHz (ADS-B) and 978 MHz (UAT) channels. It plugs inline between your antenna and dongle. It supports two power options: bias-T (3–5V DC via the SMA coax) or external power via a micro-USB port. You only need one — not both.

Typical Setup with SAWbird+

Option A — Bias-T powered (cleanest setup, no extra cables):

Antenna ──► SAWbird+ ADS-B (1090 MHz channel) ──► RTL-SDR dongle (serial 1090)
└─ bias-T powers SAWbird+ via coax

Option B — USB powered (bias-T not needed):

Antenna ──► SAWbird+ ADS-B (1090 MHz channel) ──► RTL-SDR dongle (serial 1090)
└─ micro-USB power cable

If using Option A (bias-T), set READSB_BIASTEE=true — without it the LNA has no power and actually adds insertion loss (makes reception worse than no filter at all). If using Option B (USB power), leave READSB_BIASTEE=false — the SAWbird+ is already powered and bias-T is unnecessary.

When to Enable

Enable READSB_BIASTEE=true if you use:

  • Nooelec SAWbird+ ADS-B powered via bias-T (no USB power connected)
  • Nooelec SAWbird+ NOAA or other SAWbird+ variants powered via bias-T
  • RTL-SDR Blog ADS-B LNA (inline filtered LNA)
  • Any inline LNA/filter that says "requires bias-T" or "requires 3.3–5V DC via coax"
  • Active antennas with a built-in LNA that has no separate power input

When NOT to Enable

Do not enable bias-T if:

  • Your antenna is passive (simple whip, dipole, or ground plane with no active electronics)
  • Your LNA/filter has its own separate USB or DC power supply (some models have a micro-USB port)
  • You have a standalone bias-T injector powered externally
  • You're unsure — leave it off and check your device documentation

Warning: Sending DC voltage to a device not designed for it won't damage most modern RTL-SDR dongles (they have short-circuit protection), but it's best practice to only enable when needed.

Note

Externally powered LNA? If your LNA has its own power source (e.g., micro-USB or barrel jack), you must set READSB_BIASTEE=false (the default). Enabling bias-T when the LNA is already externally powered provides no benefit, and in some configurations may cause unexpected behavior. The LNA will function normally using its own power supply — bias-T is only needed when the LNA has no other power source.

Caution

Only enable READSB_BIASTEE=true if your antenna or inline LNA requires DC power via coax. Bias-T is disabled by default. If you are unsure whether your setup needs it, leave it off and consult your antenna/LNA documentation.

Configuration

environment:
- READSB_BIASTEE=true

The container runs rtl_biast -b 1 before starting readsb. If a specific dongle serial is configured (READSB_DEVICE or auto-detected SDR_1090_SERIAL), it targets that dongle.

Verifying Bias-T

# Check bias-T status in logs
docker logs readsb 2>&1| grep 'bias-T'# Manually test bias-T inside the container
docker exec readsb rtl_biast -b 1 # enable
docker exec readsb rtl_biast -b 0 # disable

When bias-T is active, you should see a noticeable improvement in aircraft count and range compared to running the SAWbird+ unpowered.

Compatible Dongles (Bias-T Capable)

RTL-SDR DongleBias-TVoltage / Current
RTL-SDR Blog V3Yes4.5V, ~180mA max
RTL-SDR Blog V4Yes4.5V, ~180mA max
Nooelec NESDR SMArt v5Yes4.5V
Nooelec NESDR SMArTee XTRYes (always on)4.5V
Generic RTL2832U donglesUsually no

Compatible LNA/Filter Modules

ModuleFrequencyPower
Nooelec SAWbird+ ADS-B1090 MHz + 978 MHz (dual)Bias-T (3–5V via coax) or micro-USB
Nooelec SAWbird+ NOAA137 MHzBias-T (3–5V via coax) or micro-USB
RTL-SDR Blog ADS-B LNA1090 MHzBias-T (3.3–5V via coax)
RTL-SDR Blog Wideband LNAWidebandBias-T or micro-USB

Advanced Tuning

Gain — When to Intervene Manually

readsb's built-in autogain handles most situations. See Gain Control for a full guide on autogain vs. fixed gain vs. disabled, recommended values by setup type, and how to measure strong signal percentage.

Quick diagnostics:

# Check your strong signal percentage and noise floor
docker exec readsb sh -c 'jq ".total.local | {accepted: (.accepted | add), strong: .strong_signals, signal: .signal, noise: .noise}" /run/readsb/stats.json'# Test the dongle hardware
docker exec readsb rtl_test -t
docker exec readsb rtl_test -s 2400000 -t

Force a specific gain for testing:

environment:
- READSB_GAIN=38.6

Frequency Correction (PPM)

RTL-SDR dongles have a crystal oscillator that's typically off by a few PPM (parts per million). Most R820T2-based dongles are within ±1 PPM and don't need correction. If you're seeing fewer aircraft than expected:

# Test PPM offset (let it run for 1+ minute, read the cumulative value)
docker exec readsb rtl_test -p

Apply correction:

environment:
- READSB_EXTRA_ARGS=--ppm 3
- READSB_DEVICE_TYPE=rtlsdr

Network Tuning

These defaults are applied automatically when feed profiles are active, but can be overridden in READSB_EXTRA_ARGS:

ParameterDefaultDescription
--net-bi-port(none)Beast protocol input port(s). Set via READSB_NET_BI_PORT env var or --net-bi-port in READSB_EXTRA_ARGS.
--net-bo-port(none)Beast protocol output port(s). Set via READSB_NET_BO_PORT env var or --net-bo-port in READSB_EXTRA_ARGS.
--net-beast-reduce-interval0.5Seconds between reduced beast output updates. Lower = more data, more bandwidth.
--net-heartbeat60Seconds between TCP keepalive heartbeats.
--net-ro-size1280Raw output buffer size in bytes.
--net-ro-interval0.2Raw output flush interval in seconds.
--json-location-accuracy2JSON position decimal places (1=~11km, 2=~1.1km).
--range-outline-hours24Hours of range data to retain for outline calculation.
--max-range450Maximum detection range in nautical miles.
--write-json-every1JSON output write interval in seconds. Increase to 5 to reduce CPU/IO.

Stats & Upload Tuning

VariableDefaultDescription
FEED_STATS_ENABLEDtrueMaster switch for console stats logging and ADSBx stats upload.
STATS_LOG_INTERVAL120Console stats interval in seconds. Set 60 for 1-min updates, 300 for 5-min.
ADSBX_UPLOAD_INTERVAL5ADSBx stats upload interval in seconds (matches official ADSBx feeder rate). Only active when adsbexchange is in FEED_PROFILES.
LOG_LEVELinfoMinimum log verbosity: debug, info, warn, error, fatal. Per-upload confirmations are debug-level. Set to debug to see them, or warn to suppress routine messages.

Location Accuracy

For most users, IP geolocation (default) provides city-level accuracy which is sufficient for basic reception. For MLAT and precise range calculations:

environment:
- FEED_LAT=39.6195 # exact latitude
- FEED_LON=-86.1552 # exact longitude
- READSB_AUTO_LOCATION=false # disable IP geolocation

JSON Output Performance

If CPU or disk I/O is a concern (e.g., on a Raspberry Pi Zero):

environment:
- READSB_EXTRA_ARGS=--write-json-every 5
- READSB_DEVICE_TYPE=rtlsdr

This reduces JSON writes from every second to every 5 seconds.


Troubleshooting

Log Format

All log output uses syslog format so you can identify the source and severity of each line in docker logs, and it integrates natively with log aggregators (Loki, Datadog, Fluentd, etc.):

<timestamp> <service>[<priority>]: <message>
2026-04-02T11:38:20Z init-readsb-config[info]: adsbexchange UUID: 12345678-1234-1234-1234-123456789abc
2026-04-02T11:38:20Z init-readsb-config[info]: adsb-fi UUID: 87654321-4321-4321-4321-cba987654321
2026-04-02T11:38:20Z init-readsb-config[info]: Active feed profiles: adsbexchange,adsb-fi
2026-04-02T11:38:20Z init-readsb-config[info]: Verify adsbexchange feed: https://adsbexchange.com/myip/
2026-04-02T11:38:20Z init-readsb-config[info]: Verify adsb-fi feed: https://adsb.fi/status
2026-04-02T11:38:20Z init-readsb-config[info]: fixed USB permissions: /dev/bus/usb/001/004
2026-04-02T11:38:20Z init-readsb-config[info]: receiver location: 51.5074, -0.1278
2026-04-02T11:38:21Z svc-readsb[info]: readsb container startup configuration
2026-04-02T11:38:21Z svc-readsb[info]: Using readsb built-in autogain (--gain auto)
2026-04-02T11:38:21Z svc-readsb[decoder]: *8daa4b32584385ef2a7603346e29;
2026-04-02T11:38:21Z svc-readsb[decoder]: hex: aa4b32 CRC: 000000 fixed bits: 0 decode: ok
2026-04-02T11:38:21Z svc-readsb[decoder]: RSSI: -22.0 dBFS reduce_forward: 1
2026-04-02T11:38:22Z svc-feed-stats[info]: ADSBx stats upload enabled (UUID: a1b2c3d4-e5f6-7890-abcd-ef1234567890)
2026-04-02T11:38:22Z svc-feed-stats[info]: ADSBx stats URL: https://www.adsbexchange.com/api/feeders/?feed=a1b2c3d4-e5f6-7890-abcd-ef1234567890
2026-04-02T11:38:22Z svc-feed-stats[info]: Starting stats service (upload=5s, console=120s, JSON_DIR=/run/readsb)
2026-04-02T11:40:22Z svc-feed-stats[info]: stats: 42 aircraft tracked (38 with position), 128456 messages total
2026-04-02T11:38:52Z svc-feed-stats[warn]: /run/readsb/aircraft.json not updated in 45s.
ServiceDescription
init-readsb-configOne-shot init: per-profile UUID generation and persistence (disk + s6 env), feed profile setup, ADSBx stats URL, USB permissions, geolocation
svc-readsbMain readsb decoder service (startup config + decoder output)
svc-feed-statsPeriodic console stats (every 2 min by default) + ADSBx RSSI/stats upload when adsbexchange profile is active. Both use the same UUID. Per-upload confirmations are debug-level.
PriorityMeaning
debugVerbose operational detail (e.g. per-upload ADSBx confirmations). Hidden at default info level. Set LOG_LEVEL=debug to see.
infoNormal operational messages
warnNon-fatal issues that may need attention
errorErrors that affect functionality
fatalCritical errors causing service exit
decoderRaw readsb decoder output (ADS-B message decoding)

Filter logs by service:

docker logs readsb 2>&1| grep 'svc-readsb\['
docker logs readsb 2>&1| grep 'svc-feed-stats\['
docker logs readsb 2>&1| grep 'init-readsb-config\['

Filter logs by priority:

docker logs readsb 2>&1| grep '\[debug\]:'# verbose upload confirmations (requires LOG_LEVEL=debug)
docker logs readsb 2>&1| grep '\[warn\]:'
docker logs readsb 2>&1| grep '\[error\]:\|\[fatal\]:'
docker logs readsb 2>&1| grep '\[decoder\]:'

Log verbosity control:

Set LOG_LEVEL to control which messages appear. Default is info.

environment:
- LOG_LEVEL=info # default — stats summaries, startup info, warnings, errors
- LOG_LEVEL=debug # all of the above + per-upload ADSBx confirmations
- LOG_LEVEL=warn # warnings and errors only (quietest)

Container won't start or exits immediately

Check logs:

docker logs readsb
docker logs readsb --tail 50 -f # Follow last 50 lines

Common causes:

  • USB device not found: Verify RTL-SDR dongle is connected and restart container
  • Permission denied on /dev/bus/usb: Container may need elevated privileges or device permissions
  • Configuration error: Check READSB_EXTRA_ARGS syntax against Extra Arguments

ADSBx — linking your account

This container feeds ADSBx via two pathways that share a single UUID from /config/feed-uuid-adsbexchange:

  1. Beast feed (--net-connector to port 30004) — primary ADS-B data connection (persistent TCP)
  2. Stats upload (svc-feed-stats) — RSSI/stats data (periodic HTTP POST every 5s, matching official ADSBx feeder)

Both pathways use the same UUID so ADSBx sees one feeder. The stats URL is logged at startup:

https://www.adsbexchange.com/api/feeders/?feed=YOUR-UUID-HERE

To link your account: Visit https://adsbexchange.com/myip/ and click the pre-selected Feed UID at the top of the "Link your receiver" section.

No aircraft data appearing

Verify connectivity:

docker exec readsb cat /run/readsb/aircraft.json | jq '.aircraft | length'

If count is 0:

  • Check RTL-SDR device: docker exec readsb rtl_test -t
  • Verify antenna is connected and positioned properly
  • If using a powered antenna, ensure READSB_BIASTEE=true is set
  • Try a manual gain: set READSB_GAIN=35 or use --gain auto-verbose in READSB_EXTRA_ARGS to watch gain decisions
  • Look for RF interference: try a different location or antenna orientation

JSON output not updating

Check if readsb is running:

docker exec readsb ps aux | grep readsb

Verify write permissions:

docker exec readsb ls -la /run/readsb/

JSON directory should be writable by the container user.

High CPU usage or memory growth

Profile the container:

docker stats readsb --no-stream

Troubleshooting steps:

  • Reduce JSON output frequency: Add --write-json-every 5 (updates every 5 seconds instead of 1)
  • Check for decode storms: Monitor aircraft count with watch 'docker exec readsb jq .aircraft.length /run/readsb/aircraft.json'
  • Restart container: docker restart readsb

Device permission errors (non-root mode)

RTL-SDR USB device permissions are fixed automatically during container init. If you still see permission errors:

# Verify RTL-SDR devices are visible inside the container
docker exec readsb lsusb | grep -i realtek
# Check device permissions were applied
docker exec readsb ls -la /dev/bus/usb/*/*

If devices are not visible, ensure the devices: mapping is correct in your compose file. On some hosts, you may need to restart the container after plugging in the dongle.

Port conflicts

If ports are already in use, map to different host ports:

docker run ... \
-p 30011:30001 \
-p 30012:30002 \
-p 30013:30003 \
...

Then update client connections to use the new ports.

Getting help


Health Monitoring

The container includes built-in health monitoring at multiple levels:

Docker HEALTHCHECK

The image includes a HEALTHCHECK that verifies aircraft.json exists and was updated within the last 60 seconds. Docker marks the container as unhealthy after 3 consecutive failures. This check is profile-agnostic — the same healthcheck applies regardless of which feed profiles are active, since all profiles share the same underlying readsb decoder and aircraft.json output.

SettingValue
Interval30s
Timeout5s
Start period60s
Retries3
# Check container health status
docker inspect --format='{{.State.Health.Status}}' readsb
# View health check history
docker inspect --format='{{json .State.Health}}' readsb | jq .

s6 Service Supervision

All long-running services are supervised by s6-overlay and automatically restarted on crash:

# Check individual service status (equivalent of systemctl status)
docker exec readsb s6-svstat /run/service/svc-readsb
docker exec readsb s6-svstat /run/service/svc-feed-stats

Periodic Console Stats

svc-feed-stats logs a summary every 2 minutes by default (configurable via STATS_LOG_INTERVAL). Stats logging works with all feed profiles — it is not limited to adsbexchange:

svc-feed-stats[info]: stats: 42 aircraft tracked (38 with position), 128456 messages total

To change the interval, set STATS_LOG_INTERVAL to the desired number of seconds (e.g. 60 for every minute, 300 for every 5 minutes). To disable stats logging entirely, set FEED_STATS_ENABLED=false.

Quick Status Commands

# Container health
docker inspect --format='{{.State.Health.Status}}' readsb
# Aircraft count
docker exec readsb cat /run/readsb/aircraft.json | jq '.aircraft | length'# Feed UUID (per-profile)
docker exec readsb cat /config/feed-uuid-adsbexchange
# List all profile UUIDs
docker exec readsb ls /config/feed-uuid-*# Service uptime (all services)
docker exec readsb s6-svstat /run/service/svc-readsb
docker exec readsb s6-svstat /run/service/svc-feed-stats
# RTL-SDR dongle test
docker exec readsb rtl_test -t
# Tag a dongle serial
docker exec readsb rtl_eeprom -d 0 -s 00001090
# Check bias-T status
docker exec readsb rtl_biast -b 1 # enable
docker exec readsb rtl_biast -b 0 # disable

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/readsb:latest
docker-compose up -d # if using compose

Check image version:

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

Upstream Monitoring Workflow

This repo includes .github/workflows/upstream-monitor.yml to track wiedehopf/readsb on the dev branch. On drift, it opens a PR that updates .github/upstream/readsb-dev.json.

  • Schedule: every 6 hours
  • Manual run: Actions -> Monitor Upstream readsb -> Run workflow
  • Optional force run: set force=true when running manually to open a PR even when metadata is unchanged

This workflow never publishes images and never pushes directly to main.


Resources


License

GPL-3.0-or-later — see LICENSE. The upstream readsb project is also GPL-3.0-or-later.

Sponsored and maintained by Blackout Secure.

About

LinuxServer.io containerized of readsb high‑performance ADS‑B decoder RTL‑SDR aircraft signal

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Used by

Contributors

Languages