Deploy "ultrafeeder"

The "ultrafeeder" container is the heart of our "adsb" application. It receives 1090MHz ADS-B ES signals from your SDR, and demodulates ADS-B messages, making them available for all other containers.

It also provides a website with a map based on tar1090, station statistics (graphs1090), mlat-client, and an mlat-hub to aggregate MLAT results.

In your favorite text editor, create a file named docker-compose.yml in your application directory (/opt/adsb) if you've been following along verbatim.

nano docker-compose.yml
  # ultrafeeder combines a number of functions:
  # - it retrieves and decodes 1090MHz Mode A/C/S data from the SDR(s) using Wiedehopf's branch of readsb
  # - it implements a `tar1090` based map on port 80 (mapped to port 8080 on the host)
  # - it includes graph1090 (system statistics website) on http://xxxxx/graphs1090
  # - it sends ADSB data directly (without the need of additional containers) to the
  #   "new" aggregators, and, if desired, also to ADSBExchange
  # - it includes mlat-client to send MLAT data to these aggregators
  # - it includes an MLAT Hub to consolidate MLAT results and make them available to the built-in map and other services

    tty: true
    container_name: ultrafeeder
    hostname: ultrafeeder
    restart: unless-stopped
      - 'c 189:* rwm'
      - 8080:80               # to expose the web interface
      # --------------------------------------------------
      # general parameters:
      - LOGLEVEL=error
      - TZ=${FEEDER_TZ}
      # --------------------------------------------------
      # SDR related parameters:
      - READSB_DEVICE_TYPE=rtlsdr
      # --------------------------------------------------
      # readsb/decoder parameters:
      # --------------------------------------------------
      # Sources and Aggregator connections:
      # Note - remove the ones you are not using / feeding
      # Make sure that each line ends with a semicolon ";"
      # if you are not using dump978, feel free to remove the first line
      # --------------------------------------------------
      # --------------------------------------------------
      # TAR1090 (Map Web Page) parameters:
      - UPDATE_TAR1090=true
      - TAR1090_ENABLE_AC_DB=true
      - TAR1090_SITESHOW=true
      - TAR1090_RANGERINGSDISTANCES=50,100,150,200
      - TAR1090_RANGERINGSCOLORS='#1A237E','#0D47A1','#42A5F5','#64B5F6'
      - TAR1090_USEROUTEAPI=true
      # --------------------------------------------------
      # GRAPHS1090 (Decoder and System Status Web Page) parameters:
      - GRAPHS1090_DARKMODE=true
      # --------------------------------------------------
      - /opt/adsb/ultrafeeder/globe_history:/var/globe_history
      - /opt/adsb/ultrafeeder/graphs1090:/var/lib/collectd
      - /proc/diskstats:/proc/diskstats:ro
      - /dev:/dev:ro
      - /run:exec,size=256M
      - /tmp:size=128M
      - /var/log:size=32M

In the file above, you will find several parameters that have values denoted as ${xxxx}. These values are read from a file in the same directory named .env that we created earlier. Alternatively, you can simply replace ${xxxx} with the value you want to use, for example READSB_RTLSDR_DEVICE=${ADSB_SDR_SERIAL} --> READSB_RTLSDR_DEVICE=0000001090.

The docker-compose.yml file above will:

  • Create a few mapped docker volumes to store historic message values and autogain values (/var/globe_history), statistics for the graphs (/var/lib/collectd), and make the disk statistics (/proc/diskstats) and USB devices (/dev) available to the container.

  • Create a service named ultrafeeder that will run the container.

    • We're mapping TCP port 8080 through to the container so we can access the web interface.

    • The variable READSB_RTLSDR_DEVICE tells readsb to look for an RTL-SDR device with the serial of 1090 (that we re-serialized in an earlier step).

    • We're passing several environment variables through, including our timezone, latitude and longitude from the .env file (denoted by ${VARIABLE}).

  • We're using tmpfs for volumes that have regular I/O. Any files stored in a tmpfs mount are temporarily stored outside the container's writable layer. This helps to reduce:

    • The size of the container, by not writing changes to the underlying container; and

    • SD Card or SSD wear

You can find an expanded example of the docker-compose.yml file that you can download and edit here if you want to see other options, but the sample above is a good start.

Feeding directly from Ultrafeeder

There are several aggregators, both non-profit and commercial, that can directly be sent data from ultrafeeder without the need for an additional feeder container. We have added them in the example docker-compose.yml file above. Here is a partial list of these aggregators. All of them use the beast_reduce_plus format for feeding ADS-B data, and mlat-client for feeding MLAT:


(C)ommercial/ (N)on-profit


Feed details


Run by volunteers that used to be related to adsbexchange port 30004 mlat: port 31090


Run by a Finnish IT and aviation enthusiast port 30004 mlat: port 31090


Run by an aviation enthusiast located in the Netherlands port 30004 mlat: port 31090


N port 30004 mlat: port 31090

The Air Traffic


Run by an aviation enthusiast port 30004 mlat: port 31090



Aviation data-science company (non-profit) port 24999 mlat: no MLAT

ADSB Exchange


Large aggregator owned by JetNet port 30004 mlat: port 31090



Run by a few aviation enthusiasts in Canada and Portugal

adsb: port 30001 mlat: port 31090

Fly Italy ADSB


Run by a few aviation enthusiasts in Italy

adsb: port 4905 mlat: port 30100

When feeding AdsbExchange, Ultrafeeder will send statistics to by default. See the description of the ADSBX_STATS parameter on how to disable this.

Using the MLAT results

A working MLAT configuration is already provided in the example above. See for more details on how to configure more advanced features.

Deploying ultrafeeder

Once the docker-compose.yml file is created, issue the command docker compose up -d to bring up the environment.

docker compose up -d

You should see the following output:

 ⠴ Network adsb_default   Created
 ✔ Container ultrafeeder  Started

We can view the logs for the environment with the command docker compose logs, or continually "tail" them with docker compose logs -f. At this stage, the logs will be fairly extensive and unexciting and look like this:

[s6-init] making user provided files available at /var/run/s6/etc...exited 0.
[s6-init] ensuring user provided files have correct perms...exited 0.
[fix-attrs.d] applying ownership & permissions fixes...
[fix-attrs.d] done.
[cont-init.d] executing container initialization scripts...
[cont-init.d] 00-libsecomp2: executing...
[cont-init.d] 00-libsecomp2: exited 0.
[cont-init.d] 01-print-container-version: executing...
[2023-05-08 13:15:51.203][01-print-container-version] Container Version: 20230505-190743_9e4ed76_main, build date 2023-05-05 15:07:43 -0400
[cont-init.d] 01-print-container-version: exited 0.
... (more logs here)
[cont-init.d] done.
[services.d] starting services
[2023-05-08 13:15:53.482][mlat-client] Started as an s6 service
[services.d] done.
[2023-05-08 13:15:53.542][graphs1090] 646 (process ID) old priority 0, new priority 10
[2023-05-08 13:15:53.568][graphs1090-readback] copying DB from disk to /run/collectd
[2023-05-08 13:15:53.612][readsb] WARNING -- READSB_FORWARD_MLAT_SBS has been set! Do not feed the SBS (BaseStation) output of this container to any aggregators!
[2023-05-08 13:15:53.631][readsb] invoked by: /usr/local/bin/readsb --net --quiet --lat ...etc

We can see our container running with the command docker ps:

CONTAINER ID   IMAGE                                                   COMMAND   CREATED        STATUS                  PORTS                                       NAMES
548becf06f0f            "/init"                  2 days ago     Up 2 days (healthy)      >80/tcp   ultrafeeder

We can see the adsb_default network with the command docker network ls:

9950236691cc   adsb_default   bridge    local
2facb5a2ac76   bridge         bridge    local
0c73e1072dfc   host           host      local
74247d059bbb   none           null      local

Ultrafeeder Web Pages

If configured and started using the example above, the container will make a website available at port 8080 of your host machine. Here are a few web pages that are generated (replace my_host_ip with the name or IP address of your host machine):

  • http://my_host_ip:8080/ : tar1090 map and table of all aircraft received

  • http://my_host_ip:8080/graphs1090/ : page with graphs and operations statistics of your station

  • http://my_host_ip:8080?pTracks : showing all aircraft tracks received in the last 24 hours

  • http://my_host_ip:8080?heatmap&realheat : showing a heatmap of all aircraft in the last 24 hours

  • http://my_host_ip:8080?replay : showing a time-lapse replay of the past few days

Viewing Live Data in Text Format

To see the data being received and decoded by our new container, run the command docker exec -it ultrafeeder viewadsb. This should display a real-time departure-lounge-style screen showing all the aircraft being tracked, for example:

 Hex    Mode  Sqwk  Flight   Alt    Spd  Hdg    Lat      Long   RSSI  Msgs  Ti -
 7CF86F S     2061  BFRT22   10025  219  286  -31.871  116.586 -28.6    14  1
 7C79CA S     1200  YCC       2975  126  152  -32.490  115.887 -28.2    68  0
 7C79CB S     3000  YCD       1525  118  352  -32.221  115.948 -25.6   269  0
 7C79D1 S     1200  YCJ       3575  113  185  -32.375  115.837 -29.2   289  1
 7C79DB S     3000  YCT       1375  119  358  -32.176  115.940 -25.1   126  0
 7C79DC S     1200  YCU       3000   96  229  -32.437  115.929 -28.5   260  5
 7CF9E1 S     2055            1250  178  084                   -29.8    18  0
 7C822A S     3730  ZZW       1500                             -23.5   258  0
 7C7A3F S     1273  VOZ1485    grnd   0                        -25.4    11  3
 7C7A6E S     1200  YGW       2575   99  191  -32.296  115.813 -20.3   522  0
 7C1ABD S     4265  UTY6071  33125  398  197  -30.535  116.638 -23.6   363  0
 7C42D2 S     3664  NWK1663    grnd  59  239  -31.936  115.968 -21.6   258 12
 7C1B35 S                      grnd   9  281                   -28.3     4 15
 7C1B3C S     4306  VOZ9224  34000  405  192  -30.804  116.239 -22.5   150  0
 7C1C68 S     3646  FWA       5000  191  253  -31.803  116.299 -25.4   396  0
 7C6CA2 S     3760  NWK1885   7825  239  193  -31.609  116.244 -13.1   509  0
 7C6CA4 S     4035  NWK2873   3075  141  239  -31.846  116.143 -20.8   566  0
 7C4518 S     1464  QJE1928  13225  437  037  -31.840  116.316 -10.7   516  0
 7C0DAB S     3000  CZH        800   71  303  -32.085  115.923 -18.3   273  0
 7C6DB5 S                    36975                             -31.8    11 32
 7C3F19 S     4063  MQZ       1325  105  240  -31.898  116.043 -16.8   601  0
 7C7F72 S                                                      -31.5     7 37
 7C7796 S     4310  UTY734   34000  381  181  -30.327  116.639 -25.8    82  0
 7CF7C4 S           PHRX1A                                     -20.3    22  1
 7CF7C5 S           PHRX1B                                     -21.6    15  9
 7CF7C6 S           PHRX2A                                     -21.3    15  1
 7CF7C7 S           PHRX2B                                     -28.1     3  2
 7C2FD6 S     4223  NWK2878   4025  212  176  -32.006  115.948  -2.1   831  0

Press CTRL-C to escape this screen.

You should also be able to point your web browser to to view the web interface (change to the IP address or hostname of your docker host). You should see a map showing your currently tracked aircraft, and a link to the "Performance Graphs".

UUID security

The example files above use the same UUID for all feeders. Doing so makes it possible that your information from one site could be tracked on another. An alternative approach is to generate a unique UUID for each website, load those into a variable in .env, and append the UUID to the row in the ULTRAFEEDER_CONFIG section. For example:


Preparing and setting up ultrafeeder with Prometheus and Grafana

See readme-grafana.MD at the container's Github repository web page.

Minimalist setup

If you want to use ultrafeeder only as a SDR decoder but without any mapping or stats/graph websites, without MLAT connections or MLAT-hub, etc., for example to minimize CPU and RAM needs on a low CPU/memory single board computer, then do the following:

  • in the ULTRAFEEDER_CONFIG parameter, remove any entry that starts with mlat or mlathub. This will prevent any mlat-clients or mlathub instances to be launched. If you want to connect the mlat-client(s) to external MLAT servers but you don't want to run the overhead of a MLATHUB, you can leave any entries starting with mlat in the ULTRAFEEDER_CONFIG parameter, and set MLATHUB_DISABLE=true

  • Set the parameter TAR1090_DISABLE=true. This will prevent the nginx web server and any websites from being launched

  • Make sure not to use the label as Telegraf adds a LOT of resource use to the container


It is possible that you won't see any planes, either with the docker command above or when pointing your web browser at the readsb container. This can have a number of root causes - a common one being that active radio transmissions in other frequency bands that are reasonably "close" to the ADS-B band are completely overwhelming your SDR at the default starting gain of 49.6. It may be necessary to lower the starting point for the autogain script to at least allow the detection of some planes in order for the script to work. So if even after a few minutes you don't see any planes at all (and no ADS-B messages in the "Performance Graphs"), you may want to try to force a lower starting gain value into the autogain algorithm. To do this, please do the following. You may have to try different values instead of the value of 34 suggested here:

  • Set the READSB_AUTOGAIN_INITIAL_GAIN variable in the ultrafeeder section of your docker-compose file:

  • Then reset the autogain process with this command and recreate the container to apply the new READSB_AUTOGAIN_INITIAL_GAIN value :

cd /opt/adsb
docker compose up -d
docker exec -it ultrafeeder /usr/local/bin/autogain1090 reset


If you want to look at more options and examples for the ultrafeeder container, you can find the repository here.

Last updated