Declarative Docker Compose for self-hosted media, search, and NVR infrastructure.
Quick start · Profiles · Network isolation · License
Why this exists:
I run self-hosted media, search, and NVR services across several machines. Docker Compose profiles let a single repository serve every host — each machine only starts the services it needs.
What it does:
Provides declarative docker-compose.yml files for three self-hosted stacks — media (*arr automation, music/audiobooks, Jellyfin), search (SearXNG + Valkey), and NVR (Frigate with optional Coral TPU and Intel QSV acceleration). Every host-specific value is a ${VARIABLE} declared in .env.example — no real paths, IPs, or secrets are committed.
git clone https://github.com/j3n0v4/docker-compose-stack.git
cd docker-compose-stackEach stack is self-contained — pick the one you need:
cd media-stack && cp .env.example .env # *arr, music, Jellyfin
cd search-engine && cp .env.example .env # SearXNG + Valkey
cd frigate && cp .env.example .env # Frigate NVREdit .env, set your paths and ports, then start a profile:
# media-stack
docker compose --profile arr --profile music --profile jellyfin up -d
# search-engine
docker compose --profile core up -d
# frigate
docker compose --profile nvr up -dBring services down with docker compose --profile <name> down.
| Stack | Profile | Services | Host |
|---|---|---|---|
| media-stack | arr |
sonarr, radarr, lidarr, prowlarr, bazarr, recyclarr | media |
| media-stack | music |
music-assistant, audiobookshelf | music |
| media-stack | jellyfin |
jellyfin | media |
| search-engine | core |
searxng, valkey | search |
| frigate | nvr |
frigate | nvr |
See media-stack/README.md, search-engine/README.md, and frigate/README.md for the full service tables.
Each named network exists so a container can only reach the services it actually talks to:
| Network | Services | Why |
|---|---|---|
arr |
sonarr, radarr, lidarr, prowlarr, bazarr, recyclarr, jellyfin | Genuinely inter-communicating: prowlarr serves indexers, bazarr talks to sonarr/radarr, recyclarr syncs profiles, jellyfin reaches sonarr/radarr for metadata |
music |
audiobookshelf | No real cross-talk; kept on a shared network for consistency |
search (internal) |
searxng, valkey | searxng talks to valkey (cache/limiter) |
nvr (internal) |
frigate | Isolated NVR — no external network access |
music-assistant uses network_mode: host and cannot be isolated. A service is only reachable from others that share its network(s).
The arr, music, and jellyfin profiles mount media over NFS from the NAS before any container starts. The compose file expects these mount points on the host, configured via DOWNLOADS_DIR and MEDIA_DIR:
${DOWNLOADS_DIR}(/mnt/downloads)${MEDIA_DIR}/tv,/movies,/anime,/cartoons,/music,/audiobooks
If the NFS share is unavailable, the containers start but the mount paths are empty. Mount the shares first, then run docker compose up.
All configurable values are declared in .env.example. Copy it to .env and set your own values. API keys are not stored in either file — export them in your shell before docker compose up:
export SONARR_API_KEY=... RADARR_API_KEY=...This repository assumes a reverse proxy (nginx, etc.) exists elsewhere for TLS. Neither stack terminates TLS — point your reverse proxy at the published ports and terminate TLS there.
MIT © 2026 JD Cordero