From 5937a408157c4586e22bbc1ff96fedb81e3219f3 Mon Sep 17 00:00:00 2001 From: Sol455 Date: Fri, 9 Jan 2026 15:50:15 +0000 Subject: [PATCH 1/5] fix: extend docker-tar1090 with runtime nginx proxy injection Fixes circular dependency causing 5-7s timeouts - proxy now reads from /run/readsb/aircraft.json file instead of HTTP fetch. Changes: - Extend ghcr.io/sdr-enthusiasts/docker-tar1090 instead of building from scratch - Add 08-inject-proxy-config to inject nginx proxy_pass at runtime (build-time sed doesn't persist because tar1090 regenerates nginx config) - Disable binCraft via nginx sub_filter so frontend uses JSON format - Remove redundant readsb service (docker-tar1090 has readsb built-in) - Remove shared volumes (single container, no inter-container sharing) - Use #!/command/with-contenv bash shebang to inherit environment vars - Fall back to adsb.lol when local data has 0 aircraft (not just on error) --- Dockerfile | 44 +++++++---------------- Dockerfile.tar1090 | 18 ---------- docker-compose.yml | 49 +++++++++---------------- docker/01-start-proxy | 4 +++ docker/08-inject-proxy-config | 22 ++++++++++++ docker/entrypoint.sh | 20 ----------- docker/lighttpd-proxy.conf | 7 ---- docker/lighttpd-tar1090.conf | 20 ----------- proxy/server.js | 68 ++++++++++++++++++++++++----------- 9 files changed, 103 insertions(+), 149 deletions(-) delete mode 100644 Dockerfile.tar1090 create mode 100644 docker/01-start-proxy create mode 100644 docker/08-inject-proxy-config delete mode 100644 docker/entrypoint.sh delete mode 100644 docker/lighttpd-proxy.conf delete mode 100644 docker/lighttpd-tar1090.conf diff --git a/Dockerfile b/Dockerfile index 93406badc..f14683edc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,35 +1,17 @@ -FROM debian:bullseye-slim +FROM ghcr.io/sdr-enthusiasts/docker-tar1090:latest -RUN apt-get update && apt-get install -y \ - nginx \ - lighttpd \ - git \ - curl \ - && rm -rf /var/lib/apt/lists/* +# Install Node.js for our adsb.lol proxy +RUN apt-get update && apt-get install -y nodejs npm && rm -rf /var/lib/apt/lists/* -RUN mkdir -p /usr/local/share/tar1090/html +# Copy our proxy server (reads from file, avoids circular dependency) +COPY proxy/server.js /opt/proxy/server.js -COPY html/ /usr/local/share/tar1090/html/ -COPY *.conf /usr/local/share/tar1090/ -COPY *.sh /usr/local/share/tar1090/ -RUN chmod +x /usr/local/share/tar1090/*.sh +# Runtime nginx config injection - runs AFTER 07-nginx-configure regenerates config +# Must be numbered > 07 to run after nginx config is generated +# (Build-time sed doesn't persist because tar1090 regenerates nginx config at startup) +COPY docker/08-inject-proxy-config /etc/s6-overlay/startup.d/08-inject-proxy-config +RUN chmod +x /etc/s6-overlay/startup.d/08-inject-proxy-config -COPY docker/lighttpd-tar1090.conf /etc/lighttpd/conf-available/89-tar1090.conf -RUN lighttpd-enable-mod tar1090 - -RUN sed -i 's/server.port\s*=.*/server.port = 8504/' /etc/lighttpd/lighttpd.conf && \ - sed -i 's|server.document-root\s*=.*|server.document-root = "/usr/local/share/tar1090/html/"|' /etc/lighttpd/lighttpd.conf - -COPY <<'EOF' /usr/local/bin/start.sh -#!/bin/bash -set -e - -echo "Starting tar1090 web interface on port 8504..." -lighttpd -D -f /etc/lighttpd/lighttpd.conf -EOF - -RUN chmod +x /usr/local/bin/start.sh - -EXPOSE 8504 - -CMD ["/usr/local/bin/start.sh"] +# Start proxy as a background service using s6-overlay cont-init +COPY docker/01-start-proxy /etc/cont-init.d/01-start-proxy +RUN chmod +x /etc/cont-init.d/01-start-proxy diff --git a/Dockerfile.tar1090 b/Dockerfile.tar1090 deleted file mode 100644 index 1d121b09d..000000000 --- a/Dockerfile.tar1090 +++ /dev/null @@ -1,18 +0,0 @@ -FROM ghcr.io/sdr-enthusiasts/docker-tar1090:latest - -USER root - -RUN apt-get update && apt-get install -y \ - curl \ - && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ - && apt-get install -y nodejs \ - && rm -rf /var/lib/apt/lists/* - -COPY proxy/server.js /opt/proxy/server.js - -COPY docker/lighttpd-proxy.conf /etc/lighttpd/conf-enabled/90-proxy.conf - -COPY docker/entrypoint.sh /opt/entrypoint.sh -RUN chmod +x /opt/entrypoint.sh - -ENTRYPOINT ["/opt/entrypoint.sh"] diff --git a/docker-compose.yml b/docker-compose.yml index bd8d2957c..dac7f7e0e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,52 +1,35 @@ services: - readsb: - image: ghcr.io/sdr-enthusiasts/docker-readsb-protobuf:latest - container_name: readsb - hostname: readsb + tar1090: + build: + context: . + dockerfile: Dockerfile + container_name: tar1090 + hostname: tar1090 restart: unless-stopped - network_mode: host + # network_mode: host # Use on Linux; doesn't work on macOS Docker + ports: + - "8080:80" # tar1090 web UI (use 8080 on macOS, 80 on Linux) environment: - TZ=UTC + # readsb config (built into docker-tar1090) - READSB_DEVICE_TYPE=none + - READSB_NET_ENABLE=true + - READSB_NET_CONNECTOR=${READSB_NET_CONNECTOR:-} - READSB_LAT=${RECEIVER_LAT:-0} - READSB_LON=${RECEIVER_LON:-0} - READSB_ALT=${RECEIVER_ALT:-0} - READSB_RX_LOCATION_ACCURACY=2 - READSB_STATS_RANGE=true - - READSB_NET_ENABLE=true - - DISABLE_WEBAPP=true - - READSB_NET_CONNECTOR=192.168.8.183,30005,beast_in - volumes: - - readsb-autogain:/run/autogain - - readsb-collectd:/run/collectd - tmpfs: - - /var/log:size=32M - - tar1090: - build: - context: . - dockerfile: Dockerfile.tar1090 - container_name: tar1090 - hostname: tar1090 - restart: unless-stopped - network_mode: host - depends_on: - - readsb - environment: - - TZ=UTC - - BEASTHOST=127.0.0.1 - - BEASTPORT=30005 + # tar1090 map config - LAT=${RECEIVER_LAT:-0} - LONG=${RECEIVER_LON:-0} - TAR1090_DEFAULTCENTERLAT=${RECEIVER_LAT:-0} - TAR1090_DEFAULTCENTERLON=${RECEIVER_LON:-0} - - READSB_URL=http://127.0.0.1:80/data/aircraft.json + # adsb.lol proxy config - ADSBLOL_ENABLED=${ADSBLOL_ENABLED:-false} - RECEIVER_LAT=${RECEIVER_LAT:-0} - RECEIVER_LON=${RECEIVER_LON:-0} - ADSBLOL_RADIUS=${ADSBLOL_RADIUS:-40} - PROXY_PORT=3005 - -volumes: - readsb-autogain: - readsb-collectd: + tmpfs: + - /var/log:size=32M diff --git a/docker/01-start-proxy b/docker/01-start-proxy new file mode 100644 index 000000000..52c097032 --- /dev/null +++ b/docker/01-start-proxy @@ -0,0 +1,4 @@ +#!/command/with-contenv bash +# Start the adsb.lol fallback proxy +echo "[01-start-proxy] Starting aircraft data proxy on port ${PROXY_PORT:-3005}..." +node /opt/proxy/server.js & diff --git a/docker/08-inject-proxy-config b/docker/08-inject-proxy-config new file mode 100644 index 000000000..a5e06468a --- /dev/null +++ b/docker/08-inject-proxy-config @@ -0,0 +1,22 @@ +#!/command/with-contenv bash +# Inject proxy location block AFTER tar1090's 07-nginx-configure runs +# This must run at startup (not build time) because 07-nginx-configure regenerates the config + +echo "[08-inject-proxy-config] Injecting adsb.lol proxy config into nginx..." + +# Add proxy location for aircraft.json +sed -i '/include \/etc\/nginx\/nginx-tar1090-webroot.conf;/i\ location = /data/aircraft.json { proxy_pass http://127.0.0.1:3005/data/aircraft.json; }' \ + /etc/nginx/sites-enabled/tar1090 + +# Disable binCraft in receiver.json so frontend uses JSON format (required for adsb.lol) +# Uses nginx sub_filter to modify the response on-the-fly +sed -i '/include \/etc\/nginx\/nginx-tar1090-webroot.conf;/i\ location = /data/receiver.json { alias /run/readsb/receiver.json; sub_filter_types application/json; sub_filter '\''\"binCraft\": true'\'' '\''\"binCraft\": false'\''; sub_filter '\''\"zstd\": true'\'' '\''\"zstd\": false'\''; sub_filter_once off; }' \ + /etc/nginx/sites-enabled/tar1090 + +# Verify the injection worked +if grep -q "proxy_pass" /etc/nginx/sites-enabled/tar1090; then + echo "[08-inject-proxy-config] Proxy config injected successfully" +else + echo "[08-inject-proxy-config] ERROR: Failed to inject proxy config" + exit 1 +fi diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh deleted file mode 100644 index 9785aca1c..000000000 --- a/docker/entrypoint.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -set -e - -echo "Starting aircraft data proxy service..." -node /opt/proxy/server.js & -PROXY_PID=$! - -echo "Waiting for proxy to be ready..." -sleep 2 - -cleanup() { - echo "Shutting down..." - kill $PROXY_PID 2>/dev/null || true - exit 0 -} - -trap cleanup SIGTERM SIGINT - -echo "Starting tar1090..." -exec /init "$@" diff --git a/docker/lighttpd-proxy.conf b/docker/lighttpd-proxy.conf deleted file mode 100644 index 93bd9ce73..000000000 --- a/docker/lighttpd-proxy.conf +++ /dev/null @@ -1,7 +0,0 @@ -server.modules += ( "mod_proxy" ) - -$HTTP["url"] =~ "^/data/aircraft\.json" { - proxy.server = ( "" => ( - ( "host" => "127.0.0.1", "port" => 3005 ) - )) -} diff --git a/docker/lighttpd-tar1090.conf b/docker/lighttpd-tar1090.conf deleted file mode 100644 index 94fe15812..000000000 --- a/docker/lighttpd-tar1090.conf +++ /dev/null @@ -1,20 +0,0 @@ -server.modules += ( "mod_alias" ) -server.modules += ( "mod_setenv" ) - -alias.url += ( - "/data/" => "/run/readsb/" -) - -$HTTP["url"] =~ "^/(libs|flags|images)/" { - setenv.add-response-header += ( "Cache-Control" => "public, max-age=3600" ) -} - -$HTTP["url"] =~ "^/data/" { - setenv.add-response-header += ( "Cache-Control" => "no-cache, no-store, must-revalidate" ) -} - -setenv.add-response-header += ( - "Access-Control-Allow-Origin" => "*", - "Access-Control-Allow-Methods" => "GET, OPTIONS", - "Access-Control-Allow-Headers" => "Content-Type" -) diff --git a/proxy/server.js b/proxy/server.js index 4a30ca42a..b28dd50a5 100644 --- a/proxy/server.js +++ b/proxy/server.js @@ -1,12 +1,13 @@ const http = require('http'); const https = require('https'); +const fs = require('fs').promises; -const READSB_URL = process.env.READSB_URL || 'http://127.0.0.1:80/data/aircraft.json'; +const LOCAL_DATA_PATH = process.env.LOCAL_DATA_PATH || '/run/readsb/aircraft.json'; const ADSBLOL_ENABLED = process.env.ADSBLOL_ENABLED === 'true'; const RECEIVER_LAT = parseFloat(process.env.RECEIVER_LAT || '0'); const RECEIVER_LON = parseFloat(process.env.RECEIVER_LON || '0'); const ADSBLOL_RADIUS = parseInt(process.env.ADSBLOL_RADIUS || '40'); -const PORT = parseInt(process.env.PROXY_PORT || '3000'); +const PORT = parseInt(process.env.PROXY_PORT || '3005'); const ADSBLOL_API = `https://api.adsb.lol/v2/lat/${RECEIVER_LAT}/lon/${RECEIVER_LON}/dist/${ADSBLOL_RADIUS}`; @@ -80,31 +81,58 @@ function convertAdsbLolToReadsb(adsbLolData) { }; } -async function getAircraftData() { +async function fetchAdsbLol() { + console.log('Fetching from adsb.lol...'); + const adsbLolData = await fetchUrl(ADSBLOL_API); + const convertedData = convertAdsbLolToReadsb(adsbLolData); + console.log(`adsb.lol: ${convertedData.aircraft?.length || 0} aircraft`); + + // Write to local file so tar1090's backend process can read it try { - console.log('Attempting to fetch from local readsb...'); - const localData = await fetchUrl(READSB_URL); - console.log(`✓ Local readsb: ${localData.aircraft?.length || 0} aircraft`); - return { data: localData, source: 'local' }; + await fs.writeFile(LOCAL_DATA_PATH, JSON.stringify(convertedData)); + } catch (err) { + console.log(`Warning: Could not write to ${LOCAL_DATA_PATH}: ${err.message}`); + } + + return { data: convertedData, source: 'adsb.lol' }; +} + +async function readLocalFile() { + try { + const data = await fs.readFile(LOCAL_DATA_PATH, 'utf8'); + return JSON.parse(data); } catch (error) { - console.log(`✗ Local readsb failed: ${error.message}`); + return null; + } +} - if (!ADSBLOL_ENABLED) { - console.log('✗ adsb.lol fallback disabled'); - throw new Error('Local feed unavailable and fallback disabled'); - } +async function getAircraftData() { + // Try local file first (readsb writes to /run/readsb/aircraft.json) + const localData = await readLocalFile(); + + if (localData && localData.aircraft?.length > 0) { + console.log(`Local file: ${localData.aircraft.length} aircraft`); + return { data: localData, source: 'local' }; + } + // Try adsb.lol fallback if enabled + if (ADSBLOL_ENABLED) { + const reason = localData ? '0 aircraft from local' : 'local file not found'; + console.log(`Falling back to adsb.lol (${reason})...`); try { - console.log('Attempting fallback to adsb.lol...'); - const adsbLolData = await fetchUrl(ADSBLOL_API); - const convertedData = convertAdsbLolToReadsb(adsbLolData); - console.log(`✓ adsb.lol fallback: ${convertedData.aircraft?.length || 0} aircraft`); - return { data: convertedData, source: 'adsb.lol' }; + return await fetchAdsbLol(); } catch (fallbackError) { - console.log(`✗ adsb.lol fallback failed: ${fallbackError.message}`); - throw new Error('Both local and fallback feeds unavailable'); + console.log(`adsb.lol fallback failed: ${fallbackError.message}`); } } + + // Return local data even if empty (if we got a response) + if (localData) { + return { data: localData, source: 'local' }; + } + + // No data sources available + throw new Error('No data sources available'); } const server = http.createServer(async (req, res) => { @@ -136,7 +164,7 @@ const server = http.createServer(async (req, res) => { server.listen(PORT, () => { console.log(`Aircraft data proxy listening on port ${PORT}`); - console.log(`Local feed: ${READSB_URL}`); + console.log(`Local data file: ${LOCAL_DATA_PATH}`); console.log(`adsb.lol fallback: ${ADSBLOL_ENABLED ? 'enabled' : 'disabled'}`); if (ADSBLOL_ENABLED) { console.log(`adsb.lol API: ${ADSBLOL_API}`); From 41faa6dc4d1f01aba9198c0a269e3acab97676a6 Mon Sep 17 00:00:00 2001 From: Sol455 Date: Fri, 9 Jan 2026 16:09:09 +0000 Subject: [PATCH 2/5] fix: update workflow to use Dockerfile (not Dockerfile.tar1090) --- .github/workflows/docker_build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml index d5c5761b1..8fda82aa4 100644 --- a/.github/workflows/docker_build.yml +++ b/.github/workflows/docker_build.yml @@ -40,7 +40,7 @@ jobs: uses: docker/build-push-action@v6 with: context: . - file: Dockerfile.tar1090 + file: Dockerfile platforms: linux/arm64 push: false load: true From f2973ae7beb28a256a830db60527f030babc46a1 Mon Sep 17 00:00:00 2001 From: Sol455 Date: Mon, 12 Jan 2026 14:07:31 +0000 Subject: [PATCH 3/5] Change default port to 8078 for consistency --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index dac7f7e0e..8689eb051 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: restart: unless-stopped # network_mode: host # Use on Linux; doesn't work on macOS Docker ports: - - "8080:80" # tar1090 web UI (use 8080 on macOS, 80 on Linux) + - "8078:80" # tar1090 web UI environment: - TZ=UTC # readsb config (built into docker-tar1090) From d612d913070bb9829c38ceb21cccf60b709d69d6 Mon Sep 17 00:00:00 2001 From: Sol455 Date: Fri, 16 Jan 2026 14:26:13 +0000 Subject: [PATCH 4/5] Address PR review feedback - Enable network_mode: host for Debian deployment - Return empty aircraft array (200) instead of error when no data sources available --- docker-compose.yml | 4 +--- proxy/server.js | 13 +++++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 8689eb051..234fa20c3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,9 +6,7 @@ services: container_name: tar1090 hostname: tar1090 restart: unless-stopped - # network_mode: host # Use on Linux; doesn't work on macOS Docker - ports: - - "8078:80" # tar1090 web UI + network_mode: host environment: - TZ=UTC # readsb config (built into docker-tar1090) diff --git a/proxy/server.js b/proxy/server.js index b28dd50a5..f6233fef4 100644 --- a/proxy/server.js +++ b/proxy/server.js @@ -146,11 +146,16 @@ const server = http.createServer(async (req, res) => { }); res.end(JSON.stringify(data)); } catch (error) { - res.writeHead(503, { 'Content-Type': 'application/json' }); + console.log(`Data fetch failed: ${error.message}`); + res.writeHead(200, { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + 'X-Data-Source': 'none' + }); res.end(JSON.stringify({ - error: error.message, - aircraft: [], - now: Date.now() / 1000 + now: Date.now() / 1000, + messages: 0, + aircraft: [] })); } } else if (req.url === '/health') { From 211468d277e897b234b07935de15a08dc22aa9b5 Mon Sep 17 00:00:00 2001 From: Sol455 Date: Fri, 16 Jan 2026 14:48:14 +0000 Subject: [PATCH 5/5] Convert proxy to proper s6-overlay service - Replace background process (&) with proper s6 service at /etc/services.d/proxy - Enables automatic restarts, logging integration, and graceful shutdown --- Dockerfile | 6 +++--- docker/01-start-proxy | 4 ---- docker/services.d/proxy/run | 2 ++ 3 files changed, 5 insertions(+), 7 deletions(-) delete mode 100644 docker/01-start-proxy create mode 100644 docker/services.d/proxy/run diff --git a/Dockerfile b/Dockerfile index f14683edc..d9f92f5a7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,6 @@ COPY proxy/server.js /opt/proxy/server.js COPY docker/08-inject-proxy-config /etc/s6-overlay/startup.d/08-inject-proxy-config RUN chmod +x /etc/s6-overlay/startup.d/08-inject-proxy-config -# Start proxy as a background service using s6-overlay cont-init -COPY docker/01-start-proxy /etc/cont-init.d/01-start-proxy -RUN chmod +x /etc/cont-init.d/01-start-proxy +# Run proxy as a proper s6-overlay service (auto-restart, logging, graceful shutdown) +COPY docker/services.d/proxy /etc/services.d/proxy +RUN chmod +x /etc/services.d/proxy/run diff --git a/docker/01-start-proxy b/docker/01-start-proxy deleted file mode 100644 index 52c097032..000000000 --- a/docker/01-start-proxy +++ /dev/null @@ -1,4 +0,0 @@ -#!/command/with-contenv bash -# Start the adsb.lol fallback proxy -echo "[01-start-proxy] Starting aircraft data proxy on port ${PROXY_PORT:-3005}..." -node /opt/proxy/server.js & diff --git a/docker/services.d/proxy/run b/docker/services.d/proxy/run new file mode 100644 index 000000000..798932a62 --- /dev/null +++ b/docker/services.d/proxy/run @@ -0,0 +1,2 @@ +#!/command/with-contenv bash +exec node /opt/proxy/server.js