Deployment & Upgrade Guide

April 8, 2026 · View on GitHub

This guide covers fresh installation, configuration, running the system, and upgrading between versions.


Prerequisites

RequirementNotes
DockerVersion 24+ recommended
Docker Composev2 (included with Docker Desktop)
NVIDIA Container ToolkitOnly required if using the LLaMA3 local AI model on a GPU
Internet connectivityRequired for external data feeds (ADS-B, AIS, Celestrak, etc.)

Minimum hardware:

  • 4 CPU cores
  • 8 GB RAM
  • 40 GB disk (for TimescaleDB data — 72-hour AIS/ADS-B retention with compression)

Fresh Installation

1. Clone the Repository

git clone https://github.com/d3mocide/Sovereign_Watch.git
cd Sovereign_Watch

2. Configure Environment

cp .env.example .env

Edit .env with your values. At minimum, configure:

# Your monitoring area
CENTER_LAT=45.5152
CENTER_LON=-122.6784
COVERAGE_RADIUS_NM=150

# Required — the stack will not start without these
POSTGRES_PASSWORD=choose-a-strong-password
JWT_SECRET_KEY=        # generate with: openssl rand -hex 32

# Required for maritime data
AISSTREAM_API_KEY=your-key-here

# Optional maritime failover
AISHUB_USERNAME=your-aishub-username
AIS_PRIMARY_SOURCE=aisstream

# Required for 3D terrain maps (optional — falls back to free CARTO)
VITE_MAPBOX_TOKEN=your-token-here

# Required for AI analysis (at least one)
ANTHROPIC_API_KEY=sk-ant-...
# or GEMINI_API_KEY=AIzaSy...

See Configuration Reference for the complete variable reference.

If AISHUB_USERNAME is set, the maritime poller can fail over to AISHub automatically after repeated AISStream connection failures. Leaving it blank preserves the original AISStream-only behavior.

3. Build and Start

make prod          # production (static frontend build, stable)
# or
make dev           # development (Vite HMR, uvicorn --reload, live source mounts)

Windows without WSL: run the underlying command directly: docker compose up -d --build (prod) or docker compose -f docker-compose.yml -f compose.dev.yml up -d --build (dev)

This starts all services:

ContainerRole
sovereign-nginxReverse proxy, serves frontend on port 80
sovereign-frontendProd: nginx serving compiled static bundle · Dev: Vite HMR server
sovereign-backendFastAPI fusion API (runs schema migrations on startup)
sovereign-timescaledbTimescaleDB (PostgreSQL + time-series extensions)
sovereign-redpandaKafka-compatible message bus
sovereign-redisRedis cache and pub/sub
sovereign-adsb-pollerADS-B aviation ingestion
sovereign-ais-pollerAIS maritime ingestion
sovereign-space-pulseSGP4, SatNOGS, and Space Weather
sovereign-infra-pollerInternet outages + submarine cables
sovereign-rf-pulseRF infrastructure ingestion
sovereign-js8callJS8Call HF radio terminal

4. Verify Startup

# Check all containers are running
docker compose ps

# View aggregate logs
docker compose logs -f

# View a specific service
docker compose logs -f sovereign-backend
docker compose logs -f sovereign-adsb-poller
docker compose logs -f sovereign-space-pulse

5. Access the Interface

InterfaceURL
Tactical Map (UI)http://localhost
API Swagger Docshttp://localhost/api/docs
API ReDochttp://localhost/api/redoc

Initial Data Load Times

After a fresh start, allow time for the pollers to populate data:

Data TypeExpected Time
Aviation (ADS-B)~5–10 seconds
Maritime (AIS)~10–30 seconds (first vessel arrives)
Space (SGP4/SatNOGS)~30–60 seconds (initial fetch + propagation)
RF Infrastructure (ARD, NOAA NWR)~2–5 minutes
JS8Call Radio Traffic~5–10 seconds
RepeaterBook (if enabled)~3–10 minutes
Submarine cables~1–2 minutes
Internet outages~2–5 minutes

Stopping the System

make down                   # stop all containers (data preserved in volumes)
docker compose down -v      # DESTRUCTIVE — also deletes all TimescaleDB data

Upgrading an Existing Installation

1. Pull Latest Code

git pull

2. Rebuild and Restart

make prod          # or: make dev

Docker Compose will rebuild only the containers whose images have changed and restart them.

3. Apply Database Migrations (when required)

Some releases include database schema changes. The release notes will specify if a migration is needed.

Check RELEASE_NOTES.md before upgrading.

Example: Applying a migration

docker compose exec -T timescaledb psql -U postgres -d sovereign_watch < ./backend/db/migrate_rf_plus.sql

Available migrations (applied in filename order where dependencies exist):

FilePurpose
backend/db/migrate_add_constellation.sqlAdds constellation column to satellites
backend/db/migrate_rf_plus.sqlCreates RF infrastructure tables
backend/db/migrate_orbital_tracks_cd.sqlCreates orbital_tracks hypertable; strips TLE from per-row storage
backend/db/migrate_tracks_72h_retention.sqlExtends tracks retention to 72 h; tightens compression to 1 h

Fresh installations do not need to run migration scripts — the full schema is applied automatically on first start via backend/db/init.sql.

4. Verify After Upgrade

docker compose ps          # All containers running
docker compose logs -f     # Watch for startup errors

Hot Reload (Development)

Sovereign Watch is configured for Hot Module Replacement in development:

ServiceTriggerAction
FrontendSave any .tsx / .ts / .css fileVite automatically hot-reloads in the browser (no restart)
Backend APISave any .py fileUvicorn auto-reloads (no restart needed)
Ingestion PollersSave any .py fileRequires manual restart: docker compose restart sovereign-adsb-poller

Important: Only rebuild containers when Dockerfile or dependency files (pyproject.toml, uv.lock, package.json, pnpm-lock.yaml) change:

# Rebuild a specific service
docker compose up -d --build sovereign-adsb-poller
# or rebuild everything
make prod

Container Management

Viewing Logs

# All services
docker compose logs -f

# Specific service
docker compose logs -f sovereign-space-pulse
docker compose logs -f sovereign-rf-pulse

# Last 50 lines
docker compose logs --tail=50 sovereign-backend

Restarting a Service

docker compose restart sovereign-adsb-poller

Running One-Off Commands

# Python shell in backend
docker compose run --rm sovereign-backend python

# psql database shell
docker compose exec timescaledb psql -U postgres -d sovereign_watch

# Redis CLI
docker compose exec sovereign-redis redis-cli

Checking Resource Usage

docker compose stats

Nginx Reverse Proxy

Nginx (sovereign-nginx) routes all traffic through a single port (80):

PathDestination
/Frontend React app
/api/Backend FastAPI (sovereign-backend:8000)
/js8/JS8Call WebSocket bridge (sovereign-js8call)

The Nginx configuration is at nginx/. No changes are needed for standard deployments.


Data Retention

Sovereign Watch uses multiple hypertables with independent retention policies to balance storage vs. tactical depth:

HypertableDataRetentionCompression
tracksAIS vessels, ADS-B aircraft72 hoursAfter 4 hours
satnogs_observationsMulti-source spectrum verification30 daysAfter 1 day
space_weather_kpNOAA Kp-Index / Storm levels7 daysAfter 1 day
jamming_eventsADS-B interference / GPS jamming7 daysAfter 1 day

Note on Satellites: Orbital positions are no longer stored in a hypertable. They are deterministic (reproducible from TLE via SGP4) so positions are computed in-memory on demand by the API via /api/tracks/history/{norad_id}. This significantly reduces I/O while maintaining full historical fidelity.

See agent_docs/TIMESCALE_RETENTION.md for the full retention reference including monitoring queries and manual cleanup commands.


Security Notes

  • Change POSTGRES_PASSWORD from the default password before deploying outside a local machine.
  • ALLOWED_ORIGINS defaults to localhost — add your domain if accessing remotely.
  • API keys (Anthropic, Mapbox, AISStream, RadioReference) are injected at container build time via environment variables — never commit them to the repository.
  • The API does not require authentication by default. Consider placing Nginx behind a VPN or adding HTTP Basic Auth via Nginx for any internet-facing deployment.

Remote Access and ALLOWED_ORIGINS

When you open the UI from another machine, the browser sends the backend the exact frontend origin, for example http://192.168.1.50 or https://watch.example.com. That origin must be present in ALLOWED_ORIGINS or the browser will block backend requests and WebSocket upgrades.

Use the exact origin you type into the browser, including protocol and non-default ports:

# Remote IP on port 80
ALLOWED_ORIGINS=http://192.168.1.50

# Remote domain over HTTPS
ALLOWED_ORIGINS=https://watch.example.com

# Local + remote access during testing
ALLOWED_ORIGINS=http://localhost,http://127.0.0.1,http://192.168.1.50

After changing ALLOWED_ORIGINS in .env, rebuild the backend so the new environment reaches FastAPI:

docker compose up -d --build sovereign-backend

Common symptoms of a bad ALLOWED_ORIGINS value:

  • Orbital or tactical panels load but fail to populate live data
  • Browser console shows CORS failures or rejected /ws/ / API requests
  • WebSocket-backed views work on localhost but fail when accessed by remote IP or domain

Troubleshooting

Container Fails to Start

# Check which container failed
docker compose ps

# View its logs
docker compose logs sovereign-backend

Database Connection Errors

TimescaleDB takes 15–30 seconds to initialize on first run. Backend services retry automatically. If errors persist after 60 seconds:

docker compose restart sovereign-timescaledb
docker compose restart sovereign-backend

No Data on the Map

  1. Check poller logs: docker compose logs sovereign-adsb-poller
  2. Verify your AOR (CENTER_LAT / CENTER_LON) is in a populated area
  3. Check Redpanda is healthy: docker compose logs sovereign-redpanda
  4. Verify your AISSTREAM_API_KEY for maritime data
  5. If you enabled maritime failover, verify AISHUB_USERNAME and AIS_PRIMARY_SOURCE

Frontend Not Loading

docker compose logs sovereign-frontend
docker compose logs sovereign-nginx

Resetting Everything

# Full reset — destroys all data
docker compose down -v
make prod