masqueradarr
getting started · 02 install → first stream

Get masqueradarr running

masqueradarr ships as Docker images. Pick a deployment shape, set a handful of environment variables, then walk the first-run wizard from first admin to first playlist.

01 Choose a deployment shape

Two ways to run it

Both are official Docker images. Reach for the compose stack for a normal deployment; reach for the all-in-one for a quick trial or a small home server with nothing external to manage.

● Option A · compose stack

App + MongoDB, two services

The standard image (iflip721/masqueradarr) plus a mongo service, wired by docker-compose.yml. The app self-provisions its config from your .env on every boot — there's no host config file to manage.

● Option B · all-in-one

App + MongoDB in one container

A second image (the AIO variant of iflip721/masqueradarr) bundles app + mongod + config bootstrap, so the whole stack runs from a single docker run with no external database. One /data volume persists everything.

Option A — bring up the compose stack

# 1 · copy the env template and fill it in (see the .env table below)
cp .env.example .env

# 2 · bring the stack up — app + MongoDB
docker compose up -d

# 3 · open the app  →  http://localhost:3000  (or your DOMAIN)

At minimum set MONGO_ROOT_USER / MONGO_ROOT_PASS, your DOMAIN, and the host volume paths (COMPOSE_PATH, BACKUPS_PATH, MONGO_DATA_PATH). To publish on a host port other than 3000, set MASQUERADARR_PORT and update DOMAIN to match.

Option B — run the all-in-one

# one container — app + MongoDB + config bootstrap, no external database
docker run -d \
  -p 3000:3000 \
  -v masqueradarr-data:/data \
  iflip721/masqueradarr:latest
all-in-one caveat

On amd64 the bundled MongoDB 7.0 needs a CPU with AVX. On hosts without it (some older or low-power boxes), use the compose stack with an external Mongo instead. To publish on a different host port, change the left side of the -p mapping — e.g. -p 8080:3000 (the container always serves on 3000 internally).

02 The .env file

What each variable does

All runtime settings live in MongoDB and are editable on the Settings screen. The .env only bootstraps infrastructure on first boot — most values are seeded with $setOnInsert, so they apply on the first provision only. Change them in the UI afterward; a redeploy won't clobber your UI changes.

VariablePurpose
MASQUERADARR_PORTHost port mapped to masqueradarr (default 3000).
MONGO_ROOT_USER
MONGO_ROOT_PASS
MongoDB root credentials; also assemble the app's mongoUri.
DOMAINPublic base URL written into composed playlist / guide links. Set it correctly up front — changing it later rewrites every playlist's URL.
DISPLAY_NAMEApp display name.
TZContainer timezone (used by the scheduler).
COMPOSE_PATHHost dir for composed .m3u + XMLTV exports (uid-1000 writable).
BACKUPS_PATHHost dir for scheduled backups (uid-1000 writable).
MONGO_DATA_PATHHost dir for persistent MongoDB data.
MONGO_HOST_PORTHost port mapped to mongod (default 27017).
MONGO_URI
MONGO_HOST
Optional — point the app at an external / Atlas MongoDB instead of the compose mongo service.
DNS_LOG_LEVELOutbound-DNS trace verbosity (13); seeds the setting on first boot.
MASQ_EDGEOptional (default off). 1 inverts the topology so the Rust proxy owns the public port — see the topology section.
03 Volumes & permissions

Host directories must be writable by the container users

The compose stack bind-mounts three host directories so your exports, backups and database survive a rebuild. Create each one before composing the stack, and make it writable by the right uid — the app's node user is uid 1000; mongod runs as uid 999.

# create the host volume dirs and grant the container users write access
mkdir compose && chown -R 1000:1000 ./compose && chmod -R 777 ./compose   # node  (uid 1000)
mkdir backups && chown -R 1000:1000 ./backups && chmod -R 777 ./backups   # node  (uid 1000)
mkdir mongo   && chown -R  999:999  ./mongo   && chmod -R 777 ./mongo     # mongod (uid 999)
no host config file

You never manage a config.json by hand. The app writes /app/config/config.json inside the container from your .env on every boot (creds URL-encoded into the mongoUri) — it's image-internal and regenerated each start. Point at an external Mongo with MONGO_URI / MONGO_HOST if you'd rather not run the bundled one.

04 Where the video bytes flow · MASQ_EDGE

The default topology — and the opt-in inversion

masqueradarr runs two processes: the Node control plane and the Rust data-plane (masq-proxy). Which one owns the public socket is a single switch, MASQ_EDGE. Leave it off unless you have a reason. Same public port and DOMAIN either way; fully reversible.

default · MASQ_EDGE off Node is the front door
clients
Clients
public :3000
all traffic
front door
Node — public front door
0.0.0.0:3000 · SPA · /api/* · WS · gate · relay
relay bytes · loopback seams
sidecar
masq-proxy
127.0.0.1:8787 · /health · /probe · engine
inverted · MASQ_EDGE=1 Rust is the front door
clients
Clients
public :3000
all traffic
public edge
masq-proxy — public edge
0.0.0.0:3000 · serves streams in-process · token-gated
reverse-proxy · authorize (30s TTL)
internal
Node — internal
127.0.0.1:8080 · SPA · /api/* · WS · control plane
which one do I want?

The whole idea of edge mode is to take Node's single-threaded event loop out of the video byte path. Turn it on for many concurrent or high-bitrate viewers, to keep the management UI snappy under streaming load, or on constrained hardware (Pi / small VPS). Leave it off for a personal setup with a handful of viewers — the default sidecar path is simpler, more battle-tested, and keeps strictly per-request token revocation. Rust's loopback :8787 listener is unchanged in both modes, so the channel-probe scheduler always works.

05 First run — admin to first playlist

The one-time setup wizard

On first launch there are no users — the app reports needsSetup and the SPA walks you through creating the first admin account. On a fresh install the database starts empty; built-in sources appear as zero-channel shells and only populate after your first Sync now. Here's the order.

first-run sequence setup → domain → playlist → users
1
Setup screen
Create the first admin account
Pick a username and password on the one-time Setup screen — this becomes the administrator. You're taken to Login; sign in.
2
Settings
Set your Domain
Set Domain to the public address your players reach this server at (e.g. https://tv.example.com). It's woven into every published playlist URL. changing it later rewrites every playlist's URL
3
Playlists → Add playlist
Add a playlist source
The Add Playlist modal offers every built-in source plus custom playlists (clone / file / URL / HDHomeRun). A built-in provisions a zero-channel shell row.
4
Playlist → Sync now
Populate the channels
Run Sync now to fetch the live catalog. Until you do, the playlist shows zero channels everywhere. For an authenticated source (dulo), capture a login session from Settings first.
5
EPG Sources · Channel Mapping
(Optional) add guide data & link it
Add EPG Sources (Gracenote / EPG-PW / Jesmann / Custom XMLTV) and sync them, then use Channel Mapping to link channels to the guide so each shows now/next.
6
Users
Create users — then grant them playlists
Add each viewer with a username, password and role. Creating a user is not enough: grant which playlists they may use (Allowed Global Playlist / Allowed Custom Playlists). a user with nothing granted gets an empty M3U and can't stream — admins bypass this and see everything.
7
IPTV client
Connect a player
Each user gets a personal, tokenized .m3u + XMLTV guide URL from their Dashboard. Paste the M3U into TiviMate / VLC / Plex / Jellyfin — the download is token-free, but the stream is token-gated to that user.
coming from TVApp2?

There is no in-place upgrade path — masqueradarr is a new application with a new data model (MongoDB instead of flat files). Stand it up fresh and re-add your sources through the UI.

README · Getting started · First run · Configuration .env.example · the annotated template next → Playlists
masqueradarr · getting started next → playlists