{"id":564,"date":"2026-07-27T05:59:11","date_gmt":"2026-07-27T05:59:11","guid":{"rendered":"https:\/\/lance.conryclan.com\/home\/?p=564"},"modified":"2026-07-27T06:48:46","modified_gmt":"2026-07-27T06:48:46","slug":"configuring-a-satnogs-station","status":"publish","type":"post","link":"https:\/\/lance.conryclan.com\/home\/configuring-a-satnogs-station\/","title":{"rendered":"Configuring a SatNOGS station"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The SatNOGS documentation is mess, with bits and bobs spread across multiple places (main site, wiki, forums etc).  To enable me to remember how to re-configure if required, here&#8217;s the process I followed for an RTL-SDR \u279c Raspberry Pi.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p class=\"wp-block-paragraph\">I wanted to share a special thanks to Bali from the Libre Space discussion group for taking the bully by the horns and creating a <a href=\"https:\/\/community.libre.space\/t\/tutorial-how-to-install-satnogs-groundstation-for-newbie-pro-for-static-and-rotator\/14200\">tutorial offering what the official documentation can&#8217;t<\/a>.  <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This whole setup is docker based, with only configuration files living directly on the RPi host.<\/p>\n\n\n\n<p class=\"has-large-font-size wp-block-paragraph\">Setting up the RPi<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Unfortunately even though it runs within docker, the host still needs an old version of Debian, so prepare your SD card with raspios-bookworm.  Using the RPi Imager makes it simple.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To use an RTL-SDR we need to force it not to use the default drivers:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Paste the following into <em>\/etc\/modprobe.d\/blacklist-sdr.conf<\/em><\/p>\n\n\n\n<pre class=\"wp-block-code\">&lt;code&gt;blacklist rtl2832_sdr\nblacklist dvb_usb_rtl28xxu\nblacklist rtl2832\nblacklist airspy\nblacklist hackrf\nblacklist msi001\nblacklist msi2500&lt;\/code&gt;<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Add <em>rtl-sdr.rules<\/em><\/p>\n\n\n\n<pre class=\"wp-block-code\">&lt;code&gt;wget https:\/\/raw.githubusercontent.com\/rtlsdrblog\/rtl-sdr-blog\/refs\/heads\/master\/rtl-sdr.rules\nsudo mv .\/rtl-sdr.rules \/etc\/udev\/rules.d\/\nsudo chown root: \/etc\/udev\/rules.d\/rtl-sdr.rules&lt;\/code&gt;<\/pre>\n\n\n\n<p class=\"has-large-font-size wp-block-paragraph\">Setup Docker<\/p>\n\n\n\n<pre class=\"wp-block-code\">&lt;code&gt;sudo apt update\nsudo apt install docker-compose docker.io&lt;\/code&gt;<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Create a directory in your home directory which will hold the station&#8217;s configuration.  Create the folder with your station ID, so that if you end up with additional stations you just need additional folders.<\/p>\n\n\n\n<pre class=\"wp-block-code\">&lt;code&gt;mkdir ~\/station4938\ncd ~\/station4938&lt;\/code&gt;<\/pre>\n\n\n\n<p class=\"has-medium-font-size wp-block-paragraph\"><strong>Defining the configuration<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Inside the station folder create <em>station.env<\/em><\/p>\n\n\n\n<pre class=\"wp-block-code\">&lt;code&gt;SATNOGS_API_TOKEN=&amp;lt;NETWORK_TOKEN&gt;\nSATNOGS_ARTIFACTS_API_TOKEN=&amp;lt;DB_TOKEN&gt;\nSATNOGS_DB_API_TOKEN=&amp;lt;DB_TOKEN&gt;\nSATNOGS_STATION_ELEV=0\nSATNOGS_STATION_ID=4938\nSATNOGS_STATION_LAT=-43.02166\nSATNOGS_STATION_LON=147.29000\nSATNOGS_ARTIFACTS_ENABLED=True\nSATNOGS_SOAPY_RX_DEVICE=driver=rtlsdr\nSATNOGS_ANTENNA=RX\nSATNOGS_LOG_LEVEL=DEBUG\n#SATNOGS_LOG_LEVEL=INFO\n\nSATNOGS_RX_SAMP_RATE=2.048e6\n#Supported gain values: 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\nSATNOGS_RF_GAIN=15.7\nSATNOGS_PPM_ERROR=0\n\nGR_SATELLITES_ENABLED=true\nGR_SATELLITES_APP=gr_satellites\nGR_SATELLITES_KEEPLOGS=true\n\nUDP_DUMP_HOST=0.0.0.0\n\nAUTO_SCHEDULER_EXTRA=-P \/var\/lib\/satnogs-client\/priority.txt -d 1.5 -m 5&lt;\/code&gt;<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Note that the SATNOGS_ARTIFACTS_API_TOKEN and SATNOGS_DB_API_TOKEN have the same values.  They are used by different docker containers (client and auto-scheduler).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Get the NETWORK_TOKEN from <a href=\"https:\/\/network.satnogs.org\/\">https:\/\/network.satnogs.org\/<\/a>, and the DB_TOKEN from <a href=\"https:\/\/db.satnogs.org\/\">https:\/\/db.satnogs.org\/<\/a><\/p>\n\n\n\n<p class=\"has-medium-font-size wp-block-paragraph\"><strong>Defining Docker-Compose<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Still in the station folder, create <em>docker-compose.yaml<\/em>, and paste the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\">&lt;code&gt;version: &#039;3.8&#039;\nservices:\n\n  rigctld:\n    image: librespace\/hamlib:latest\n    user: &#039;500&#039;\n    read_only: true\n    environment:\n      MODEL: &#039;1&#039;\n      PORT: &#039;4532&#039;\n    restart: &#039;unless-stopped&#039;\n    command: &#039;rigctld&#039;\n\n  satnogs_client:\n    image: librespace\/satnogs-client:master-unstable\n    user: &#039;500&#039;\n    read_only: true\n    init: true\n    env_file:\n      - .\/station.env\n    environment:\n      SATNOGS_RIG_IP: &#039;rigctld&#039;\n      SATNOGS_RIG_PORT: &#039;4532&#039;\n    command: &#039;satnogs-client&#039;\n    device_cgroup_rules:\n      - &#039;c 189:* rwm&#039;\n    devices:\n      - &#039;\/dev\/bus\/usb&#039;\n    volumes:\n      - type: &#039;tmpfs&#039;\n        target: &#039;\/tmp&#039;\n      - type: &#039;volume&#039;\n        source: &#039;satnogs-client-latest&#039;\n        target: &#039;\/var\/lib\/satnogs-client&#039;\n    restart: &#039;unless-stopped&#039;\n    stop_grace_period: 1s\n\n  auto-scheduler:\n    image: librespace\/satnogs-auto-scheduler:0.4     # stable\n    # image: librespace\/satnogs-auto-scheduler:master  # latest\n    user: &#039;500&#039;\n    command: &#039;bash -c &quot;while true; do sleep 30; schedule_single_station -s $$SATNOGS_STATION_ID $$AUTO_SCHEDULER_EXTRA -l INFO &gt; \/var\/lib\/satnogs-client\/.cache\/auto-scheduler\/last_schedule.log 2&gt;&amp;amp;1; sleep 3600; done&quot;&#039;\n    read_only: true\n    env_file:\n      - .\/station.env\n    environment:\n      CACHE_DIR: &#039;\/var\/lib\/satnogs-client\/.cache\/auto-scheduler&#039;\n    depends_on:\n      - satnogs_client\n    volumes:\n      - type: &#039;tmpfs&#039;\n        target: &#039;\/tmp&#039;\n      - type: &#039;volume&#039;\n        source: &#039;satnogs-client-latest&#039;\n        target: &#039;\/var\/lib\/satnogs-client&#039;\n    restart: unless-stopped  # du not use with exiting client as this will just loop\n    stop_grace_period: 1s\n\nvolumes:\n  satnogs-client-latest:&lt;\/code&gt;<\/pre>\n\n\n\n<p class=\"has-medium-font-size wp-block-paragraph\"><strong>Creating a priorities file<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The satellite auto-scheduler will automatically schedule satellite passes to monitor.  To ensure that the satellites I want to monitor are given preference, I need to create a priorities file containing one satllite per line, with <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The priority file should have columns of <strong>**NORAD**<\/strong> <strong>**priority**<\/strong> <strong>**UUID**<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A comment may be added to the end of the line for human reading.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Information is available from the <a href=\"https:\/\/db.satnogs.org\/satellites\/\">SatNOGS database<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">e.g. for the <a href=\"https:\/\/db.satnogs.org\/satellite\/XSKZ-5603-1870-9019-3066#transmitters\">SSTV transmitter on the ISS<\/a>:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">`25544 0.9 8qgr8X9wKAwb9BwA6uZYqX` # ISS SSTV Transmitter<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Below is a sample with SSTV transmitting satellites.<\/p>\n\n\n\n<pre class=\"wp-block-code\">&lt;code&gt;# SSTV\n\n57172 0.99 AjYqcyjoAjJrmfsYtH7oux \u00a0 # UmKA 1 \u00a0 \u00a0RS40S\n67290 0.98 n5r2A8UVR5hmytgVVPnULu \u00a0 # SAKHACUBE-CHOLBON RS18S\n57189 0.97 kreKfmt4vs4n22TyJZ4Mft \u00a0 # VIZARD-meteo \u00a0RS38S\n57203 0.96 JnBA9dq5WnWQugErAnPDcz \u00a0 # UTMN-2 \u00a0 \u00a0RS27S\n57180 0.95 AUF5QGKakLf5FGfK5aCZfZ \u00a0 # MONITOR-3 RS58S\n61762 0.94 n5r2A8UVR5hmytgVVPnULu \u00a0 # ARCTICSAT 1 \u00a0 RS74S\n25544 0.93 8qgr8X9wKAwb9BwA6uZYqX \u00a0 # Zarya ISS RS0ISS\n40908 0.92 q6CVKJirfoUjNzD2A4rxsk \u00a0 # LILACSAT 2\n67291 0.91 AeDo8VuKWD3VWRgfDk7bt7 \u00a0 # QMR-KWT 2\n67279 0.90 KBkJKKjq4VDwdZWUSKZCMp \u00a0 # GALAPAGOS-UTE-SWSU\n57182 0.60 PRJ9oz5oKUAVgCrPPB9u3j \u00a0 # Monitor-4 RS57S&lt;\/code&gt;<\/pre>\n\n\n\n<p class=\"has-large-font-size wp-block-paragraph\">Starting it up<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The while shebang can then be started up, which will retrieve the docker images, create containers and get it all running.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code class=\"\" data-line=\"\">docker-compose up -d<\/code><\/p>\n\n\n\n<p class=\"has-large-font-size wp-block-paragraph\">Debugging<\/p>\n\n\n\n<p class=\"has-medium-font-size wp-block-paragraph\"><strong>Access running docker containers<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">While docker is running, access a shell within with:  <code class=\"\" data-line=\"\">&lt;mark style=&quot;background-color:#00420e;color:#b1ffd5&quot; class=&quot;has-inline-color&quot;&gt; &lt;\/mark&gt;&lt;mark style=&quot;background-color:#00420e;color:#f3fff3&quot; class=&quot;has-inline-color&quot;&gt;docker-compose exec auto-scheduler sh&lt;\/mark&gt;&lt;mark style=&quot;background-color:#00420e;color:#b1ffd5&quot; class=&quot;has-inline-color&quot;&gt; &lt;\/mark&gt;<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Review auto schedule log<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\">&lt;code&gt;&gt; docker-compose exec auto-scheduler sh\n# check last update\nls -lah \/var\/lib\/satnogs-client\/.cache\/auto-scheduler\/last_schedule.log\n#view log\ncat \/var\/lib\/satnogs-client\/.cache\/auto-scheduler\/last_schedule.log&lt;\/code&gt;<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Run a test schedule<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\">&lt;code&gt;&gt; docker-compose exec auto-scheduler sh\n# run with passed args\nschedule_single_station -s $SATNOGS_STATION_ID $AUTO_SCHEDULER_EXTRA --dryrun -l INFO\n\n# or with custom args\nschedule_single_station -s $SATNOGS_STATION_ID -P \/var\/lib\/satnogs-client\/priority.txt -d 1.5 -m 5 --dryrun -l INFO&lt;\/code&gt;<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Review run state<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The docker images are read-only, with run state stored on a ramdisk.  All run state may be found in \/var\/lib\/satnogs-client\/. <\/p>\n\n\n\n<pre class=\"wp-block-code\">&lt;code&gt;ls -lah \/var\/lib\/satnogs-client\/&lt;\/code&gt;<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I think that&#8217;s it.  I guess I&#8217;ll know when future-me needs to follow these instructions. :\/<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Lance<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">July 2026<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The SatNOGS documentation is mess, with bits and bobs spread across multiple places (main site, wiki, forums etc). To enable me to remember how to re-configure if required, here&#8217;s the process I followed for an RTL-SDR \u279c Raspberry Pi.<\/p>\n","protected":false},"author":1,"featured_media":567,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[14],"tags":[],"class_list":["post-564","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-amateur-radio"],"_links":{"self":[{"href":"https:\/\/lance.conryclan.com\/home\/wp-json\/wp\/v2\/posts\/564","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lance.conryclan.com\/home\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/lance.conryclan.com\/home\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/lance.conryclan.com\/home\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lance.conryclan.com\/home\/wp-json\/wp\/v2\/comments?post=564"}],"version-history":[{"count":8,"href":"https:\/\/lance.conryclan.com\/home\/wp-json\/wp\/v2\/posts\/564\/revisions"}],"predecessor-version":[{"id":578,"href":"https:\/\/lance.conryclan.com\/home\/wp-json\/wp\/v2\/posts\/564\/revisions\/578"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/lance.conryclan.com\/home\/wp-json\/wp\/v2\/media\/567"}],"wp:attachment":[{"href":"https:\/\/lance.conryclan.com\/home\/wp-json\/wp\/v2\/media?parent=564"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/lance.conryclan.com\/home\/wp-json\/wp\/v2\/categories?post=564"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lance.conryclan.com\/home\/wp-json\/wp\/v2\/tags?post=564"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}