Back to Worldmonitor

šŸŒ Self-Hosting World Monitor

SELF_HOSTING.md

2.5.237.2 KB
Original Source

šŸŒ Self-Hosting World Monitor

Run the full World Monitor stack locally with Docker/Podman.

šŸ“‹ Prerequisites

  • Docker or Podman (rootless works fine)
  • Docker Compose or podman-compose (pip install podman-compose or uvx podman-compose)
  • Node.js 22+ (for running seed scripts on the host)

šŸš€ Quick Start

bash
# 1. Clone and enter the repo
git clone https://github.com/koala73/worldmonitor.git
cd worldmonitor
npm install

# 2. Start the stack
docker compose up -d        # or: uvx podman-compose up -d

# 3. Seed data into Redis
./scripts/run-seeders.sh

# 4. Open the dashboard
open http://localhost:3000

The dashboard works out of the box with public data sources (earthquakes, weather, conflicts, etc.). API keys unlock additional data feeds.

šŸ”‘ API Keys

Create a docker-compose.override.yml to inject your keys. This file is gitignored — your secrets stay local.

yaml
services:
  worldmonitor:
    environment:
      # šŸ¤– LLM — pick one or both (used for intelligence assessments)
      GROQ_API_KEY: ""            # https://console.groq.com (free, 14.4K req/day)
      OPENROUTER_API_KEY: ""      # https://openrouter.ai (free, 50 req/day)

      # šŸ“Š Markets & Economics
      FINNHUB_API_KEY: ""         # https://finnhub.io (free tier)
      FRED_API_KEY: ""            # https://fred.stlouisfed.org/docs/api/api_key.html (free)
      EIA_API_KEY: ""             # https://www.eia.gov/opendata/ (free)

      # āš”ļø Conflict & Unrest
      ACLED_ACCESS_TOKEN: ""      # https://acleddata.com (free for researchers)

      # šŸ›°ļø Earth Observation
      NASA_FIRMS_API_KEY: ""      # https://firms.modaps.eosdis.nasa.gov (free)

      # āœˆļø Aviation
      AVIATIONSTACK_API: ""       # https://aviationstack.com (free tier)

      # 🚢 Maritime
      AISSTREAM_API_KEY: ""       # https://aisstream.io (free)

      # 🌐 Internet Outages (paid)
      CLOUDFLARE_API_TOKEN: ""    # https://dash.cloudflare.com (requires Radar access)

      # šŸ”Œ Self-hosted LLM (optional — any OpenAI-compatible endpoint)
      LLM_API_URL: ""             # e.g. http://localhost:11434/v1/chat/completions
      LLM_API_KEY: ""
      LLM_MODEL: ""

  ais-relay:
    environment:
      AISSTREAM_API_KEY: ""       # same key as above — relay needs it too

šŸ’° Free vs Paid

StatusKeys
🟢 No key neededEarthquakes, weather, natural events, UNHCR displacement, prediction markets, stablecoins, crypto, spending, climate anomalies, submarine cables, BIS data, cyber threats
🟢 Free signupGROQ, FRED, EIA, NASA FIRMS, AISSTREAM, Finnhub, AviationStack, ACLED, OpenRouter
🟔 Free (limited)OpenSky (higher rate limits with account)
šŸ”“ PaidCloudflare Radar (internet outages)

🌱 Seeding Data

The seed scripts fetch upstream data and write it to Redis. They run on the host (not inside the container) and need the Redis REST proxy to be running.

bash
# Run all seeders (auto-sources API keys from docker-compose.override.yml)
./scripts/run-seeders.sh

āš ļø Important: Redis data persists across container restarts via the redis-data volume, but is lost on docker compose down -v. Re-run the seeders if you remove volumes or see stale data.

To automate, add a cron job:

bash
# Re-seed every 30 minutes
*/30 * * * * cd /path/to/worldmonitor && ./scripts/run-seeders.sh >> /tmp/wm-seeders.log 2>&1

šŸ”§ Manual seeder invocation

If you prefer to run seeders individually:

bash
export UPSTASH_REDIS_REST_URL=http://localhost:8079
export UPSTASH_REDIS_REST_TOKEN=wm-local-token
node scripts/seed-earthquakes.mjs
node scripts/seed-military-flights.mjs
# ... etc

šŸ—ļø Architecture

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                 localhost:3000               │
│                   (nginx)                    │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ Static Files │      /api/* proxy            │
│  (Vite SPA)  │         │                    │
│              │    Node.js API (:46123)       │
│              │    50+ route handlers         │
│              │         │                     │
│              │    Redis REST proxy (:8079)   │
│              │         │                     │
│              │      Redis (:6379)            │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
         AIS Relay (WebSocket → AISStream)
ContainerPurposePort
worldmonitornginx + Node.js API (supervisord)3000 → 8080
worldmonitor-redisData store6379 (internal)
worldmonitor-redis-restUpstash-compatible REST proxy8079
worldmonitor-ais-relayLive vessel tracking WebSocket3004 (internal)

šŸ”Ø Building from Source

bash
# Frontend only (for development)
npx vite build

# Full Docker image
docker build -t worldmonitor:latest -f Dockerfile .

# Rebuild and restart
docker compose down && docker compose up -d
./scripts/run-seeders.sh

āš ļø Build Notes

  • The Docker image uses Node.js 22 Alpine for both builder and runtime stages
  • Blog site build is skipped in Docker (separate dependencies)
  • The runtime stage needs gettext (Alpine package) for envsubst in the nginx config
  • If you hit npm ci sync errors in Docker, regenerate the lockfile with the container's npm version:
    bash
    docker run --rm -v "$(pwd)":/app -w /app node:22-alpine npm install --package-lock-only
    

🌐 Connecting to External Infrastructure

Shared Redis (optional)

If you run other stacks that share a Redis instance, connect via an external network:

yaml
# docker-compose.override.yml
services:
  redis:
    networks:
      - infra_default

networks:
  infra_default:
    external: true

Self-Hosted LLM

Any OpenAI-compatible endpoint works (Ollama, vLLM, llama.cpp server, etc.):

yaml
# docker-compose.override.yml
services:
  worldmonitor:
    environment:
      LLM_API_URL: "http://your-host:8000/v1/chat/completions"
      LLM_API_KEY: "your-key"
      LLM_MODEL: "your-model-name"
    extra_hosts:
      - "your-host:192.168.1.100"  # if not DNS-resolvable

šŸ› Troubleshooting

IssueFix
šŸ“” 0/55 OK on health checkSeeders haven't run — ./scripts/run-seeders.sh
šŸ”“ nginx won't startCheck podman logs worldmonitor — likely missing gettext package
šŸ”‘ Seeders say "Missing UPSTASH_REDIS_REST_URL"Stack isn't running, or run via ./scripts/run-seeders.sh (auto-sets env vars)
šŸ“¦ npm ci fails in Docker buildLockfile mismatch — regenerate with docker run --rm -v $(pwd):/app -w /app node:22-alpine npm install --package-lock-only
🚢 No vessel dataSet AISSTREAM_API_KEY in both worldmonitor and ais-relay services
šŸ”„ No wildfire dataSet NASA_FIRMS_API_KEY
🌐 No outage dataRequires CLOUDFLARE_API_TOKEN (paid Radar access)