Build latest Raspberry Pi OS image on SD card and boot your Pi. From your Windows PC / Putty etc or direct from the Raspberry Pi follow these step by step guides.
Big Thanks to Daniel Ekman SA2KNG and his very hard work and efforts / this guide: https://github.com/kng/satnogs-client-docker/blob/main/lsf/LSF-GUIDE.md
All scripts used in this guide are maintained here: github.com/Xyleneuk/satnogs
Dashboard Setup
We will assume you have set up an account with SatNOGS and have access to the dashboard. See the screenshot below — note your station number ID on the left in green, and notice middle far right API KEY, click on this to get your API key (needed in set up later on). Make a note of both.
Step 1 — System Update
sudo apt update
sudo apt upgrade
You may need to type y and press enter. Chromium master preferences — press enter N.
Go make a coffee, this may take a little while...
Step 2 — Download Configuration Files
wget https://raw.githubusercontent.com/kng/satnogs-client-docker/refs/heads/main/lsf/10-satnogs.rules
wget https://raw.githubusercontent.com/kng/satnogs-client-docker/refs/heads/main/lsf/satnogs-blacklist.conf
sudo cp 10-satnogs.rules /etc/udev/rules.d/
sudo cp satnogs-blacklist.conf /etc/modprobe.d/
Step 3 — Install Docker
sudo apt install docker.io apparmor docker-compose
sudo adduser $(whoami) docker
Press y to continue when prompted.
Step 4 — Mount Your USB Drive
The station stores LRPT soft-symbol files, IQ recordings, and decoded images on a USB drive. Format it as ext4 and label it satnogs_data, then set it to auto-mount:
sudo mkdir -p /mnt/satnogs
Add this line to /etc/fstab using sudo nano /etc/fstab:
LABEL=satnogs_data /mnt/satnogs ext4 defaults,noatime 0 2
Then mount it:
sudo mount /mnt/satnogs
Step 5 — Create Station Directory and Download Container Files
mkdir -p station-4113
cd station-4113
wget https://raw.githubusercontent.com/Xyleneuk/satnogs/refs/heads/main/docker-compose.yml
wget https://raw.githubusercontent.com/Xyleneuk/satnogs/refs/heads/main/meteor.sh
wget https://raw.githubusercontent.com/Xyleneuk/satnogs/refs/heads/main/station.env
chmod +x meteor.sh
Important: The files from the GitHub repo above are the latest versions including all fixes described later in this guide. Always download fresh copies rather than copying from another install.
Step 6 — Configure station.env
nano station.env
Edit the following lines with your own values:
SATNOGS_API_TOKEN=your_token_from_network.satnogs.org
SATNOGS_STATION_ID=your_station_id
SATNOGS_STATION_LAT=your_latitude
SATNOGS_STATION_LON=your_longitude
SATNOGS_STATION_ELEV=your_elevation_in_metres
SATNOGS_RF_GAIN=30 # start here, increase if signal is weak
SATNOGS_PPM_ERROR=0 # frequency correction for your dongle
Critical — do not change the SATNOGS_POST_OBSERVATION_SCRIPT line. It must point to /opt/scripts/meteor.sh to match the bind mount in docker-compose.yml.
Now reboot the Pi. This applies the USB blacklisting rules and lets your user account access Docker:
sudo reboot
Step 7 — Install satdump
Satdump gives you full-colour, multi-channel MSU-MR images stored locally on the USB drive — much richer than the single-channel images uploaded to the SatNOGS network. Install it from the SatDump releases page or build from source:
sudo apt-get install -y cmake build-essential libfftw3-dev libpng-dev libvolk2-dev
# Follow build instructions at github.com/SatDump/SatDump for your Pi version
# The binary should end up at /usr/bin/satdump — verify with:
satdump --help
Then download the satdump decode script and make it executable:
wget https://raw.githubusercontent.com/Xyleneuk/satnogs/refs/heads/main/satdump_decode_lrpt.sh -O ~/satdump_decode_lrpt.sh
chmod +x ~/satdump_decode_lrpt.sh
Step 8 — Deploy the Container
docker-compose up -d
docker-compose down
docker-compose pull
The pull step updates the image — I found this was needed to get the RTL-SDR visible inside the Docker container.
docker-compose up -d
docker-compose logs -f
Press Ctrl + C to stop following the logs.
Check the SatNOGS dashboard — if you have a green status, go ahead and schedule some observations!
Step 9 — Set Up Automatic Decoding and Disk Cleanup (Crontab)
This step wires everything together. The cron jobs run satdump every 30 minutes to produce local images, and clean up old files every night so the USB drive doesn't fill up.
crontab -e
Add the following lines (replace <user> with your username, e.g. james, and station-<id> with your container project name):
# Decode new LRPT captures with satdump every 30 minutes
*/30 * * * * /home/<user>/satdump_decode_lrpt.sh
# Disk cleanup: IQ dumps older than 2 days
0 3 * * * find /mnt/satnogs/iq -type f -mtime +2 -delete
# Disk cleanup: LRPT .s files older than 4 days
# Must use docker exec as the container user owns these files
0 3 * * * docker exec station-<id>_satnogs_client_1 bash -c "find /var/lib/satnogs-client -maxdepth 1 -name 'LRPT_*.s' -mtime +4 -delete" 2>/dev/null
# Disk cleanup: decoded image directories older than 7 days
30 3 * * * find /mnt/satnogs/decoded -maxdepth 1 -mindepth 1 -type d -mtime +7 -exec rm -rf {} +
# Docker housekeeping
0 4 * * * /usr/bin/docker container prune -f >/dev/null
10 4 * * * /usr/bin/docker image prune -a -f >/dev/null
20 4 * * * /usr/bin/docker volume prune -f >/dev/null
30 4 * * * /usr/bin/docker network prune -f >/dev/null
Important: The LRPT .s soft-symbol files are owned by the Docker container user and cannot be deleted directly from the Pi with a normal rm command. You must always delete them via docker exec as shown above — otherwise they will silently accumulate and eventually fill the USB drive.
Step 10 — Reconfiguration
If you need to go back and edit station.env or docker-compose.yml, restart the container after saving:
docker-compose down
docker-compose up -d --force-recreate
When editing meteor.sh, the bind mount means the container sees changes immediately — no restart needed for that file alone.
How It Works — Two Decode Pipelines
Once everything is running, two independent decode pipelines work on every pass:
Pipeline 1 — SatNOGS network upload
The Docker container receives the signal, demodulates it, and saves a soft-symbol file (LRPT_*.s) to the USB drive. When the pass ends, meteor.sh runs automatically and decodes the .s file into a PNG image, saving it as data_<id>_<timestamp>.png in the SatNOGS output directory. The SatNOGS client then uploads it to network.satnogs.org alongside the waterfall and audio so it appears on your observation page.
Pipeline 2 — Local satdump images
Every 30 minutes the cron job runs satdump_decode_lrpt.sh, which scans the USB for new .s files over 10 MB and decodes them with satdump. This produces full-colour MSU-MR channel images stored locally in /mnt/satnogs/decoded/. These stay on the Pi — they are not uploaded to SatNOGS.
May 2026 Fixes — Why Images Weren't Showing on the Network
After the initial setup in February, decoded images stopped appearing on the SatNOGS network for Meteor M2-4 passes. Here is what was wrong and what was fixed.
Fix 1 — Meteor M2-4 requires a different decode flag
Meteor M2-4 (NORAD 59051) uses an 80k interleaved LRPT mode, whereas the older Meteor M2-3 uses 72k standard mode. The meteor_decode tool needs the -i flag for M2-4. Without it, every M2-4 pass returns MPDUs received: 0 regardless of how strong the signal was. The updated meteor.sh detects the satellite from the TLE and applies the correct flag automatically.
Fix 2 — Decoded images were saved in the wrong place
The SatNOGS client upload loop only picks up files from /tmp/.satnogs/data/ that start with specific prefixes — data* files are uploaded as decoded data, waterfall* as the waterfall, satnogs* as audio. Anything else is silently ignored. The old script was saving images with a meteor_ prefix to a different directory, so they were never uploaded. The updated script saves the image as data_<obs_id>_<timestamp>.png in the correct location.
Fix 3 — Container was using the old built-in script
The docker-compose.yml bind-mounts your local meteor.sh over the one baked into the Docker image. However, that mount only takes effect when the container is created, not while it is running. If the docker-compose.yml was updated after the container was last started, the container keeps using the old image version. Running docker-compose up -d --force-recreate after any change to docker-compose.yml fixes this.
Troubleshooting
Check whether the RTL-SDR is visible inside the container:
docker-compose exec satnogs_client SoapySDRUtil --probe="driver=rtlsdr"
No images on network.satnogs.org after a good pass:
# Check the meteor decode log for that observation
cat /mnt/satnogs/meteor/meteor_<obs_id>_*.log
# Confirm the updated meteor.sh is active inside the container
docker-compose exec satnogs_client grep 'deinterleave' /opt/scripts/meteor.sh
If the second command returns nothing, recreate the container:
docker-compose down
docker-compose up -d --force-recreate
Satdump not producing images:
tail -50 /mnt/satnogs/satdump_autodecode.log
A WARN: no images produced line means the pass was below the signal threshold. Very low elevation passes often produce a .s file that is technically over 10 MB but doesn't contain enough valid data to decode — this is normal.
USB disk filling up:
df -h /mnt/satnogs
# See which .s files are taking up space
du -sh /mnt/satnogs/LRPT_*.s | sort -h | tail -10
Remember — delete .s files via docker exec only, not directly from the Pi shell.
All scripts: github.com/Xyleneuk/satnogs | Station #4113 on the SatNOGS network.