open source · MIT · zero telemetry

The Beacon Stack

TV, movies, and BitTorrent — each app works standalone, and all three share indexers, quality profiles, download clients, and media-handling settings through Pulse when you want them to. One docker compose up for the whole stack.

beacon-stack/deploy
$ docker compose up -d
4 apps in the stack
1 control plane
0 telemetry

Four apps, one config layer

Pilot, Prism, and Haul each run independently with local configuration. Pulse is the hub that lets all three share one indexer list (the search APIs that catalog releases), one set of quality profiles (ranked rules for what to grab), one download client — Haul registers itself — and a common media-handling settings namespace.

Per-app default theme. 18 presets, all switchable.

How Pulse connects everything

Each Beacon service registers with Pulse on startup and heartbeats every 30 seconds. Shared state — indexers, quality profiles, download clients, media-handling settings — flows outward from Pulse to every subscribed service on a 30-second sync, and immediately when Pulse fires a push hook on save. When Haul registers, Pulse auto-creates the download-client entry so Pilot and Prism find it without anyone copying an API key.

Pulse isn't Beacon-only. Any Go service can join through the client SDK at pkg/sdk — register, discover peers, pull assigned indexers, and read shared config with one client instance.

Register once, configured automatically

When Pilot starts, it registers with Pulse: name, URL, capabilities (content:tv, content:movies, etc.). Pulse hands back the indexers, quality profiles, download clients, and shared media-handling settings that match those capabilities.

Add an indexer to Pulse — its catalog category (Movies, TV) maps to the capability set, and Pulse auto-assigns it to every matching service. A new TV-only indexer reaches Pilot within 30 seconds; a Movies-and-TV one reaches both Pilot and Prism. One API key to update, not three.

One stack, four apps

Pilot TV series
Pulse Control plane
Prism Movies
Haul BitTorrent

Built for people who run their own stack

Register once, configure everywhere

Add an indexer to Pulse and every service whose capabilities match picks it up — within 30 seconds, or instantly via push hook. One API key, one quality profile, one place to change things when they break.

Standalone or connected

Pilot, Prism, and Haul each run fine with local config. Pulse is purely additive — bring it in when you're ready for the shared layer.

Zero telemetry, MIT licensed

No analytics, no crash reporting, no update checks. Everything runs in your network and stays there. All four apps are MIT licensed and built in the open.

How the compose files fit together

The beacon-stack/deploy repo ships several Compose files because the stack tries to stay simple by default and flexible when you need it to be. Here's how they layer.

Always docker-compose.yml

The base file. Defines Postgres, Pulse, Pilot, Prism, Haul, and the two init sidecars. docker compose up -d loads this without you having to ask. For most users this is the only file that ever runs.

Auto-loaded if present docker-compose.override.yml

Compose has a built-in convention: if a file named docker-compose.override.yml sits next to the base file, it merges automatically on top of it. We use that for your personal tweaks — extra bind mounts, healthcheck timing for slow hardware, pinning a specific image tag. The file is gitignored, and docker-compose.override.example.yml in the repo shows the patterns you can copy.

Opt-in docker-compose.vpn.yml

Routes Haul through a Gluetun VPN container. Loaded by setting COMPOSE_FILE=docker-compose.yml:docker-compose.vpn.yml in your .env (so plain docker compose up -d picks it up forever after) — or by passing -f docker-compose.yml -f docker-compose.vpn.yml on every command if you'd rather be explicit. Requires Docker Compose v2.20+ for the !reset directive that detaches Haul from the bridge network and rehomes it into Gluetun's namespace.

Opt-in COMPOSE_PROFILES

Profiles enable optional containers without a separate Compose file. COMPOSE_PROFILES=flaresolverr in your .env spins up a FlareSolverr container alongside the rest of the stack for indexers behind Cloudflare bot protection. (You also need to uncomment the PULSE_FLARESOLVERR_URL line in docker-compose.yml so Pulse actually routes its scrapes through it.) Most users never need this.

How they layer

  • Default docker-compose.yml — the whole stack, no VPN.
  • Personalized docker-compose.yml + docker-compose.override.yml — your local tweaks merged on top automatically.
  • VPN'd docker-compose.yml + docker-compose.vpn.yml — Haul tunnels through Gluetun.
  • Both docker-compose.yml + docker-compose.override.yml + docker-compose.vpn.yml — personalized and VPN'd.

Compose merges files in the order it loads them; later files win on conflicts. By default, docker compose up auto-detects two files: the base and docker-compose.override.yml (if present), with the override applied last. The moment you set COMPOSE_FILE in .env, that auto-detection turns off and you become responsible for the full chain — so if you want both your overrides and the VPN, write COMPOSE_FILE=docker-compose.yml:docker-compose.override.yml:docker-compose.vpn.yml. Always keep docker-compose.vpn.yml last; its !reset directive only applies if it merges after the base's network config for Haul.

Up in three commands

The full stack — Postgres, Pulse, Pilot, Prism, Haul, and an optional VPN container — lives in beacon-stack/deploy. Point three paths at your media directories and run.

beacon-stack/deploy →
terminal
# Clone and configure
$ git clone https://github.com/beacon-stack/deploy
$ cd deploy && cp .env.example .env
# Set TV_PATH, MOVIES_PATH, DOWNLOADS_PATH in .env

# Start everything
$ docker compose up -d