To quote ye old wikipedia: ADS-B is "surveillance technology in which an aircraft determines its position via satellite navigation and periodically broadcasts it, enabling it to be tracked". It also provides a good way for pilots to receive telemetry from nearby planes, increasing safety particularly in areas with spotty radar coverage or awful weather. As of Jan 1 2020, most flights in the US are required to broadcast ADS-B messages. There are exceptions for military flights and what not where broadcasting an exact position might be a security issue.

I lived in fly-over country a few years back and got interested in monitoring ADS-B. In fly-over country it might be interesting to see what's flying over. I learned about FlightAware's "build your own ground station". It's pretty easy stuff. Get a Raspberry Pi, a USB receiver doodad, install FlightAware's software, and away you go. The data is visible on their website and you get a free Enterprise level account. This worked really well for me for a long time and the Enterprise account saved my ass on business travel a few times.

I have a few major issues with FlightAware's setup, however. First, I'm sending data out of my network using software I don't control. Second, by default, their setup allows root level access to the computer by FlightAware's remote system, specifically so they can make sure the device is running the latest software. They can trigger updates and run commands locally on the Raspberry Pi. Third, their system has a tendency to eat microSD cards by writing JSON blobs to disk every second.

Rolling Our Own

So, what do? If you don't care about the Enterprise account, it turns out one can self-host all the bits that FlightAware provides.

The basic requirements are the same as the FlightAware system. You need a computer that can run Linux. It doesn't have to be a Raspberry Pi. You need an ADS-B USB receiver. I'm using FlightAware's receiver since I already had one. You'll need an antenna, the longer the better.

From a software perspective, you'll need one of two things. Either you'll need Docker or you'll need to reproduce my setup locally. We'll use Docker for the rest of this post but it shouldn't be difficult to stand up the software locally.


I'm going to spend a bunch of time babbling about the software and the drama I had completing the project. If you're tired of me talking and just want to stand things up, here's the tl;dr if you're running amd64 or arm:

# On the docker server:
$ wget
# Change the LAT / LON variables to match your location. You can get those from the URL in Google Maps.
$ docker-compose up -d

# On your desktop/laptop
$ firefox http://docker_server:8080

The Long-winded Part

So, what's in those docker images you just downloaded?


The core software is dump1090, a decoder for Mode-S / ADS-B messages for software defined radios. There are a million repos and forks for dump1090 and it seems like everyone in the community has their own. I settled on using FlightAware's version since it is actively maintained. To talk to the radio, the docker image needs access to the USB bus via /dev/bus/usb.

Immediately, however, a problem arises. In its default configuration, FlightAware's version of dump1090 writes out JSON files every second to disk. In the FlightAware pi setup, they then beam those files up to FlightAware's servers and make them accessible via nginx for their local web interface. Well that's a huge list of the exact stuff I'm trying to avoid.

In deep time, dump1090 featured a built-in web server that offered those JSON files and skipped the file and nginx steps. However, in not-so-deep time, that http server got removed by pretty much every fork. I get it. Maintaining a built-in web server is a big lift and doesn't serve the core purpose of the dump1090 software. It leaves us in a bit of a bind, however. Combine it with FlightAware's apparent hatred of documentation and, sadly, it's time to spelunking through C code.

Lo and behold, FlightAware's dump1090 has retained the ability to just dump the packet stream out to the network, in CSV format. The --net option triggers this mode and serves up the stream on port 30003. I could grab that stream and serve up the data myself, bypassing the file stage.

So what's in this totally undocumented CSV stream? Turns out the data is arriving in what's called "SBS-1 BaseStation Port 30003" format, developed by Kinetic Avionics Limited for the BaseStation SDR product. Once again, the vendor didn't document much. So, some fine folks got together to reverse engineer and document the format. That documentation lives over here, lost in the 1990s.

To access the live stream of CSV data, do like so, with the hostname adjusted for your environment:

$ telnet docker 30003


There are very few libraries that understand this format and none in my preferred language, Go. I sat down and threw together a go library for handling SBS-1 data.

With my library in hand, I could now sit down and write a go-based HTTP server to offer up those JSON files. This is the second docker image. Seemed simple enough but nothing in this project is simple. FlightAware's dump1090 repo contained documention (yay) for the JSON output. Wrote up a convertor and, thinking I was done, I went hunting for web UIs that I could self-host.

Almost none of them worked. The few that did work offered incomplete data. WTF? Well, turns out that documentation is a lie. Turns out the version of that file in every fork is a lie. It was probably true long ago but since then everyone has modified the JSON structure and never updated the documentation. Almost every UI I ran across expected different fields. Most of them were paired with a personal fork of dump1090.

The only thing for it was to try and support the JSON format expected by the web UIs that looked interesting to me. I've done my best in the sbs-http repo to support the JSON I was seeing. One problem remains, however. Many of the dump1090 apps are leaving the CSV untouched but deriving extra data for their specific JSON output. In some cases, it's not clear where that data is coming from. My JSON doesn't contain that data so some UIs won't work quite right.

My http daemon thing does offer something I've not seen in the dump1090 variants. It offers subsecond resolution. The internal state of the sky is kept live, based off the constant stream coming out of dump1090. The JSON is generated off that live state. So, should one want to, the HTTP server can be polled as fast as one wants.

To access the JSON output, do like so, with the hostname adjusted for your environment:

$ wget http://docker:8000/data/aircraft.json


The compose file offers up the UI out of the mutability fork of dump1090. It's pretty easy on the resources and has links out to FlightAware, FlightRadar, and other sites so you can get the full details on the flights. If you updated the compose file with your lat/lon, the map will show your location as well as the distances for flights.

If you'd like a much heavier, but more featureful, UI I also support the UI from the mictronics fork of dump1090. That's available in the sungo/mictronics-ui image. You should be able to just change the image in the compose file. One word of warning: When mictronics first loads on your browser, it dumps a huge file into websql so it can cross reference data and figure out, for instance, if the flight is civilian or military. This will cause the tab to hang and most browser will scream about it. Hit 'wait' and eventually all will be well. The use of websql also means the mictronics UI won't work in private mode. You can see why it's not the default in my compose file.


The road was long and frustrating and obnoxious. In the end, though, I've got what I think is a fairly simple setup and one I've rebuilt a few times. Most recently, I installed a fresh rpi 3b+ with raspbian, installed docker, ran docker-compose up -d, and everything Just Worked. Hopefully that will be your experience as well.