Skip to main content
ShadowBroker ships with a docker-compose.yml that builds both the backend and frontend images locally, and a compose.sh helper script that auto-detects your container runtime on Linux and macOS.

Prerequisites

  • Docker (with the Compose plugin) or Podman with a compatible compose provider
  • Git (to clone the repository)
  • An API key from aisstream.io — strongly recommended for live maritime vessel tracking (the ships layer is empty without it)

Quick start

1

Clone the repository

git clone https://github.com/BigBodyCobain/Shadowbroker.git
cd Shadowbroker
2

Add your API keys

Copy the example environment file and set your keys:
cp .env.example .env
Open .env and fill in your values:
# Required
AIS_API_KEY=your_aisstream_key

# Optional — higher rate limits for flight data
OPENSKY_CLIENT_ID=
OPENSKY_CLIENT_SECRET=

# Optional — Singapore CCTV cameras
LTA_ACCOUNT_KEY=
AIS_API_KEY is strongly recommended. Without it the ships layer will be empty, but all other layers still work. Get a free key at aisstream.io.
3

Start the containers

Run the helper script, which auto-detects docker compose, docker-compose, podman compose, or podman-compose:
./compose.sh up -d
The script defaults to up -d if no arguments are given.
4

Open the dashboard

Once both containers are healthy, open http://localhost:3000 in your browser.
The frontend waits for the backend to pass its health check before starting. The backend may take up to 90 seconds on first boot.

Standalone deploy (Portainer, NAS, etc.)

If you don’t want to clone the repository, use the pre-built images published to the GitHub Container Registry. Paste this directly into Portainer’s stack editor or any Docker host:
docker-compose.yml
services:
  backend:
    image: ghcr.io/bigbodycobain/shadowbroker-backend:latest
    container_name: shadowbroker-backend
    ports:
      - "8000:8000"
    environment:
      - AIS_API_KEY=your_aisstream_key          # Required — get one free at aisstream.io
      - OPENSKY_CLIENT_ID=                       # Optional — higher flight data rate limits
      - OPENSKY_CLIENT_SECRET=                   # Optional — paired with Client ID above
      - LTA_ACCOUNT_KEY=                         # Optional — Singapore CCTV cameras
      - CORS_ORIGINS=                            # Optional — comma-separated allowed origins
    volumes:
      - backend_data:/app/data
    restart: unless-stopped

  frontend:
    image: ghcr.io/bigbodycobain/shadowbroker-frontend:latest
    container_name: shadowbroker-frontend
    ports:
      - "3000:3000"
    environment:
      - BACKEND_URL=http://backend:8000   # Docker internal networking — no rebuild needed
    depends_on:
      - backend
    restart: unless-stopped

volumes:
  backend_data:
The frontend proxies all /api/* requests through the Next.js server to BACKEND_URL using Docker’s internal networking. Browsers only ever talk to port 3000 — you do not need to expose port 8000 externally.

Environment variables

Backend

VariableRequiredDescription
AIS_API_KEYNo (strongly recommended)Maritime vessel tracking via aisstream.io. Ships layer is empty without it.
OPENSKY_CLIENT_IDNoOAuth2 client ID for higher flight data rate limits
OPENSKY_CLIENT_SECRETNoOAuth2 secret, paired with OPENSKY_CLIENT_ID
LTA_ACCOUNT_KEYNoSingapore LTA CCTV cameras
CORS_ORIGINSNoComma-separated list of allowed CORS origins. Auto-detects LAN IPs if empty.
ADMIN_KEYNo (required in production)Protects /api/settings/* and /api/system/update endpoints. Unprotected if unset.

Frontend

VariableRequiredDescription
BACKEND_URLNoURL the Next.js server uses to proxy API calls to the backend. Defaults to http://backend:8000. Runtime variable — no image rebuild needed.

BACKEND_URL configuration

By default, BACKEND_URL is set to http://backend:8000, which uses Docker’s internal container networking. Most deployments require no changes. If your backend runs on a different host or port, override BACKEND_URL at runtime:
BACKEND_URL=http://myserver.com:9096 docker-compose up -d

Forcing Podman

If both Docker and Podman are installed, compose.sh prefers Docker. To force Podman:
./compose.sh --engine podman up -d
You can also set the engine via an environment variable:
SHADOWBROKER_CONTAINER_ENGINE=podman ./compose.sh up -d
podman compose may delegate to an external compose provider depending on your local Podman configuration. Do not append a trailing . to compose commands — Compose treats it as a service name.

Updating

git pull origin main
./compose.sh down
./compose.sh up --build -d

Troubleshooting

Clear the Docker build cache and force a fresh image build:
docker compose build --no-cache
Prune unused images to free disk space and ensure the new image is used:
docker image prune -f
Stream live logs from the backend container:
./compose.sh logs -f backend
The backend has a start_period of 90 seconds in its health check. This is normal — the FastAPI server installs Playwright and fetches initial data on startup. Wait for the health check to pass before the frontend starts routing traffic.

Build docs developers (and LLMs) love