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
- Linux / macOS
- Windows
Add your API keys
Copy the example environment file and set your keys:Open
.env and fill in your values:Start the containers
Run the helper script, which auto-detects The script defaults to
docker compose, docker-compose, podman compose, or podman-compose:up -d if no arguments are given.Open the dashboard
Once both containers are healthy, open http://localhost:3000 in your browser.
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
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
| Variable | Required | Description |
|---|---|---|
AIS_API_KEY | No (strongly recommended) | Maritime vessel tracking via aisstream.io. Ships layer is empty without it. |
OPENSKY_CLIENT_ID | No | OAuth2 client ID for higher flight data rate limits |
OPENSKY_CLIENT_SECRET | No | OAuth2 secret, paired with OPENSKY_CLIENT_ID |
LTA_ACCOUNT_KEY | No | Singapore LTA CCTV cameras |
CORS_ORIGINS | No | Comma-separated list of allowed CORS origins. Auto-detects LAN IPs if empty. |
ADMIN_KEY | No (required in production) | Protects /api/settings/* and /api/system/update endpoints. Unprotected if unset. |
Frontend
| Variable | Required | Description |
|---|---|---|
BACKEND_URL | No | URL 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:
Forcing Podman
If both Docker and Podman are installed,compose.sh prefers Docker. To force Podman:
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
- Linux / macOS
- Windows
Troubleshooting
Dashboard still shows old data after updating
Dashboard still shows old data after updating
Clear the Docker build cache and force a fresh image build:
Remove stale images
Remove stale images
Prune unused images to free disk space and ensure the new image is used:
Check container logs
Check container logs
Stream live logs from the backend container:
Backend fails health check on first boot
Backend fails health check on first boot
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.