OpenFactory *arr suite lab with Sonarr, Radarr, Prowlarr and Bazarr

Build a Sonarr + Radarr + Prowlarr + Bazarr Lab on OpenFactory

Four VMs, one prompt: the canonical *arr suite for media automation

May 26, 2026

← Back to Blog

The *arr suite — Sonarr for TV, Radarr for movies, Prowlarr for indexers, Bazarr for subtitles — is the automation backbone of every self-hosted media stack. Sonarr is the eighth-most-deployed app in the 2026 r/selfhosted survey; the rest of the suite is right behind it in install rates.

This post walks through the canonical *arr lab on OpenFactory: four buildable VMs — Sonarr, Radarr, Prowlarr, and Bazarr — each on its own machine, all generated from one prompt and shipped as bootable ISOs.

What you'll build

  • sonarr (10.77.0.10:8989) — TV PVR pointed at Prowlarr for indexers.
  • radarr (10.77.0.20:7878) — movie PVR, same Prowlarr wiring.
  • prowlarr (10.77.0.30:9696) — one-stop indexer manager that pushes config to Sonarr and Radarr.
  • bazarr (10.77.0.40:6767) — subtitles for both, pre-wired to Sonarr and Radarr.

Why build it on OpenFactory

  • Each *arr is its own VM. Upgrade Sonarr without touching Radarr. Reboot Bazarr without breaking the pipeline.
  • The cross-app URLs are in the recipes. Sonarr / Radarr already know where Prowlarr lives; Bazarr already knows where Sonarr and Radarr live. Less click-through-the-UI at first boot.
  • Scenario assertions ride along. The build fails closed if any of the four identity endpoints stops responding with the right app name.
  • Pairs with Jellyfin or Plex. The same prompt vocabulary scales to feed either front-end without changes here.

Topology

Four Debian Trixie VMs on 10.77.0.0/24. Prowlarr is the indexer hub; Bazarr reaches the two PVRs for library metadata.

The prompt

Paste this verbatim into the chat builder at console.openfactory.tech. Nothing above or below it — the builder expects the prompt body to start at the “Build a compact multi-node lab…” line.

Build a compact multi-node lab named `arr-media-automation`.

Output discipline: keep the plan small. Use one startup script per node, about 25 shell lines or less. Do not install the real Sonarr, Radarr, Prowlarr, Bazarr, or any *arr / Mono / .NET dependencies at build time. Write deployment-time config examples and tiny Python stdlib or shell compatibility stubs only. The goal is a buildable preparation lab, not a production deployment.

## Topology

Create 4 buildable `debian-trixie` nodes, all `x86_64`, SSH enabled, DHCP/default route intact with lab aliases, firewall disabled, DNS `1.1.1.1` and `8.8.8.8`, user `ops` password `arr-ops` in `sudo`. Every recipe must set top-level `test_config` to `{ "enabled": false, "tests": [] }`.

- `sonarr`: role `tv-pvr`, 2 GB RAM, 16 GB disk, alias `10.77.0.10/24`, x `110`, y `60`
- `radarr`: role `movie-pvr`, 2 GB RAM, 16 GB disk, alias `10.77.0.20/24`, x `350`, y `60`
- `prowlarr`: role `indexer-mgr`, 2 GB RAM, 12 GB disk, alias `10.77.0.30/24`, x `110`, y `220`
- `bazarr`: role `subtitle-mgr`, 2 GB RAM, 12 GB disk, alias `10.77.0.40/24`, x `350`, y `220`

Connections: `sonarr` and `radarr` to `prowlarr:9696`; `bazarr` to `sonarr:8989` and `radarr:7878`.

## Common Recipe Requirements

All nodes: features `headless`, `ssh`; packages `openssh-server`, `python3`, `curl`, `jq`, `iproute2`, `netcat-openbsd`, `ca-certificates`. Each startup script adds the alias with `IFACE=$(ip route show default | awk '{print $5; exit}')`, `ip link set "$IFACE" up || true`, and `ip addr add <alias> dev "$IFACE" || true`. If `os.startup_scripts[].after` is present, it must be the string `"network-online.target"`, not an array.

## Node Requirements

All four nodes share the same compatibility-service shape with different ports and identity payloads. Each registers a Python stdlib service named `<app>-compat.service`.

`sonarr` (`0.0.0.0:8989`): `GET /ping` -> `200 {"status":"OK","appName":"Sonarr"}`; `GET /api/v3/system/status` -> `200 {"appName":"Sonarr","version":"compat-1.0","instanceName":"Sonarr-lab"}`; `GET /metrics` -> `sonarr_compat_up 1`. Env file `/etc/sonarr/sonarr.env` with `PORT=8989`, `PROWLARR_URL=http://10.77.0.30:9696`.

`radarr` (`0.0.0.0:7878`): same shape, `appName == "Radarr"`. Env `/etc/radarr/radarr.env` with `PORT=7878`, `PROWLARR_URL=http://10.77.0.30:9696`.

`prowlarr` (`0.0.0.0:9696`): `GET /ping` -> `200 {"status":"OK","appName":"Prowlarr"}`; `GET /api/v1/system/status` -> `200 {"appName":"Prowlarr","version":"compat-1.0"}`; `GET /metrics` -> `prowlarr_compat_up 1`. Env `/etc/prowlarr/prowlarr.env` with `PORT=9696`.

`bazarr` (`0.0.0.0:6767`): `GET /api/system/health` -> `200 {"app":"Bazarr","status":"OK"}`; `GET /api/system/status` -> `200 {"app":"Bazarr","version":"compat-1.0"}`; `GET /metrics` -> `bazarr_compat_up 1`. Env `/etc/bazarr/bazarr.env` with `PORT=6767`, `SONARR_URL=http://10.77.0.10:8989`, `RADARR_URL=http://10.77.0.20:7878`.

## Scenario

Emit exactly one group scenario named `arr-media-automation-validation`. Put `custom_tests[].assertions[]` inside the scenario entry; leave `scenarios[].tests` empty. Every assertion needs `on_vm`. Use only `port_listening`, `command_output`, and `http_responds`; do not emit `vm_boots`, `network_reachable`, or `service_running`.

- `Stack ports listen`: `port_listening` for `sonarr:8989`, `radarr:7878`, `prowlarr:9696`, `bazarr:6767`.
- `Sonarr identity`: on `sonarr`, `curl -fsS http://localhost:8989/api/v3/system/status | jq -e '.appName == "Sonarr"' >/dev/null && echo sonarr-ok`.
- `Radarr identity`: on `radarr`, `curl -fsS http://localhost:7878/api/v3/system/status | jq -e '.appName == "Radarr"' >/dev/null && echo radarr-ok`.
- `Prowlarr identity`: on `prowlarr`, `curl -fsS http://localhost:9696/api/v1/system/status | jq -e '.appName == "Prowlarr"' >/dev/null && echo prowlarr-ok`.
- `Bazarr health`: on `bazarr`, `curl -fsS http://localhost:6767/api/system/health | jq -e '.status == "OK"' >/dev/null && echo bazarr-ok`.
- `Cross-app reachability`: on `sonarr`, `nc -z -w 5 10.77.0.30 9696 && echo prowlarr-reachable`; on `bazarr`, `nc -z -w 5 10.77.0.10 8989 && nc -z -w 5 10.77.0.20 7878 && echo arrs-reachable`.

Preserve warnings that real Sonarr/Radarr/Prowlarr/Bazarr application binaries, API key rotation, download-client integration (qBittorrent / SABnzbd / NZBGet), indexer credentials, quality profile design, library import path mappings, off-host backups, and `10.77.0.0/24` aliasing are deployment-time concerns.

Running it

  1. Open the chat builder at console.openfactory.tech and paste the prompt into a new conversation.
  2. Review the streamed build plan. You'll see the topology, per-node recipes, and the scenario assertions that will run after boot. Edit the prompt and re-run if anything is off.
  3. Click Build group. OpenFactory fans the plan out to per-node ISO builds. When every ISO reaches built, boot the group on the runner network from the same UI.
  4. Exercise the stack. The scenario assertions run automatically against the live VMs. From the host you can also hit the service ports directly to confirm end-to-end behavior.

Driving OpenFactory from an AI agent instead of the browser? The same flow is exposed through the OpenFactory MCP server — submit the prompt programmatically, get the build-plan preview back, and call create_build / start_vm on the resulting recipes. Single-image builds go straight through the openfactory CLI.

What's still your responsibility

The prompt produces a buildable preparation lab — the right topology, the right ports listening, deployment-time config templates dropped in the right places, and tiny compatibility services that prove the wiring works. A few things still sit outside the recipe and need operator attention before this carries real load:

  • Real *arr binaries. Install from the upstream Linuxserver containers or .NET releases; the env files already point at the neighbours.
  • API keys. Each *arr generates its own on first launch — copy them between services so Bazarr and Sonarr/Radarr can talk back.
  • Download client. qBittorrent, SABnzbd, NZBGet, or whatever fits your sources. Wire it into Sonarr/Radarr after the binaries ship.
  • Indexer credentials. Prowlarr will eat indexer cookies and API keys; out of scope of the recipe by design.
  • Quality profiles + path mappings. The one-time setup that decides whether the library looks the way you want.
  • Off-host backups. Each *arr keeps a SQLite database with library state. Worth snapshotting.

Where to go next

The *arr suite needs a front-end. Pair this lab with the Jellyfin media stack post or the Plex media stack post. And the Enterprise & GxP page covers larger rollouts.

Ready to ship this in production?

OpenFactory's free flow is for browsing. Persistent VMs, SSH access, snapshots, your own ISO, and fleet deployment live on a paid plan.