Skip to content
Host your own · Pharos server

Open your
own cinema.

Pharos runs on your box, against your Plex or Hearth (opens Hearth in a new tab), your downloader, your indexers and your disks. This is the whole setup, wired the way ours is: from an empty machine to friends asking for shows.

12 Reels to thread
1 Port to forward
0 Logins that leave your box
1 Folder to back up
Before the doors open

Have these ready

  • A machine that stays onLinux, a NAS, or Windows or macOS with Docker Desktop.
  • Storage: a drive, a DAS or a NASAn internal drive, a USB or Thunderbolt DAS, or a NAS share the box mounts. Downloads and libraries on one volume, so filing is a move, not a copy.
  • Plex, or Hearth, or bothPharos files into them and reads them back. It is not a player itself.
  • A free TMDB keyTitles, posters and air dates.
  • A VPN accountAny provider the bundled VPN supports, or one with its own SOCKS5 server. For qBittorrent and the indexers.
  • A usenet provider and indexerOnly if you want NZBs in your results.Optional
Twelve reels · in order

Thread the projector

One reel per connector. Each says what it does, the exact settings to change, the lines it fills in your .env, how ours is wired, and what has gone wrong on a real install.

Reel 01

The box and the folders

Pharos is three small containers (the web app, the backend and the front door that puts both on one address) next to the services you already run. Any machine that stays on and runs Docker will do: a Linux server, a NAS, or Windows or macOS with Docker Desktop.

  • Linux
  • Windows
  • macOS
  • Synology
  • Unraid
  • TrueNAS SCALE
  • QNAP
  1. Install Docker with Compose v2, then get the Pharos source.

    git clone https://github.com/CarbonNode/Plexseek.git pharos
    cd pharos
  2. Start with a media folder, or mount separate local disks, DAS arrays and NAS shares. Pharos, your downloader and Plex must see the same files. Admin → Storage controls destinations, drive reserves, copy/hardlink/move rules and filing windows.

    media/
    ├── Downloads      finished downloads land here
    ├── Incomplete     in progress
    ├── Movies
    ├── Series
    ├── Cartoons
    ├── Anime
    └── Anime Movies
  3. Put that folder on any storage the box can mount: an internal drive, a DAS (a USB or Thunderbolt enclosure), or a NAS share. Point MEDIA_ROOT at it.

    # a drive or a DAS: its folder
    MEDIA_ROOT=/mnt/das/media
    # on Windows
    MEDIA_ROOT=D:/Media
    
    # a NAS share, mounted first (NFS or SMB)
    MEDIA_ROOT=/mnt/nas/media
  4. Or run the whole stack on the NAS itself. Any NAS that runs Docker Compose will do: Synology Container Manager, Unraid, TrueNAS SCALE or QNAP Container Station. MEDIA_ROOT is then a local folder on the NAS.

  5. Copy the settings file. The cue sheet further down this page fills it in for you.

    cp selfhost/.env.example selfhost/.env
How ours is wired

One always-on machine holds everything: the containers, the downloader, Plex, and a DAS with the whole library on it. Keeping it on one box means every service sees the same folders.

Lines in your .env
MEDIA_ROOT=/srv/media
TZ=Etc/UTC
PUID=1000
PGID=1000
  • Watch

    For multiple drives, use selfhost/compose.storage.example.yml as your mount template, then register each backend path in Admin → Storage. Missing drives wait; existing libraries are never migrated automatically.

  • Watch

    Auto mode keeps seeding files: it hardlinks on the same filesystem and copies across drives. Choose strict hardlink, copy or verified move separately for each media type.

  • Watch

    A DAS that drops off, even for a second, leaves running containers holding a dead mount (“No such device”). Once it is back, restart the containers that use it.

  • Watch

    On a NAS share, PUID and PGID must be a user the share lets write, or downloads land and then cannot be filed.

  • Watch

    Docker Desktop on Windows does not see mapped network drive letters. Use a local drive or a DAS there, or run the stack on the NAS.

  • Watch

    Folder names come from Admin → Storage when its rules are enabled; otherwise MOVIES_DIR_NAME and the other startup settings apply. Changing a destination does not migrate existing files.

  • Watch

    Every piece of backend state lives in selfhost/state. That is the one folder to back up.

  • Watch

    Only MEDIA_ROOT has to be in .env. Every connection on the reels below can be entered in the app instead (Admin → Settings → Connections), with a Test button for each, and a value saved there wins over .env.

Reel 02

qBittorrent, the downloader

qBittorrent downloads what you pick, and its RSS downloader catches tracked shows the day they air. Use the one bundled in the stack, or point Pharos at yours.

  1. Bundled: start the stack with --profile qbittorrent (or qbittorrent-vpn to run it inside the VPN, reel 04). Its Web UI is on port 8080, and the first start prints a temporary admin password in its log.

    docker compose -f selfhost/docker-compose.yml --env-file selfhost/.env logs qbittorrent | grep -i password
  2. Your own: set QB_URL to it. If it sees the media folder under a different path than /media, map the two with QB_PATH_MAP.

  3. Before you track a single show, add your trackers’ RSS feeds in qBittorrent. A tracked show’s rule takes the feeds that exist when it is created.

  4. Anime is separate: Pharos adds its own Anime/Subs and Anime/Dubs feeds to qBittorrent from ANIME_SUBS_FEED and ANIME_DUBS_FEED. Subs is the default; leave the dubs feed empty to offer subs only. Admin → Settings → Anime changes both later.

WhereSetWhy
Options → Web UIA user name and password (copy them into QB_USERNAME / QB_PASSWORD)
Options → RSSEnable fetching RSS feeds, and Enable auto downloading of RSS torrentsPharos writes the rules; qBittorrent runs them. Neither switch is on by default.
Options → Downloads → Excluded file namesOn, with the list belowA program file riding along in a torrent is never written to disk.
Options → Advanced → Network interfaceYour VPN’s interfaceIf the VPN drops, downloads stop instead of carrying on from your home address.
*.exe *.scr *.bat *.cmd *.com *.pif *.vbs *.vbe *.js *.jse *.wsf *.wsh *.ps1 *.psm1 *.msi *.msp *.hta *.jar *.reg *.cpl *.lnk *.url *.dll
How ours is wired

qBittorrent 5 runs on the host itself, bound to the VPN, and the containers reach it at host.docker.internal.

Lines in your .env
QB_URL=http://qbittorrent:8080
QB_USERNAME=admin
QB_PASSWORD=
QB_PATH_MAP=
ANIME_SUBS_FEED=https://subsplease.org/rss/?r=1080
ANIME_DUBS_FEED=

In the appAdmin → Settings → Connections → qBittorrent: the address, user name, password and path map, each with a Test button.

  • Watch

    Do not make categories by hand. Pharos files with tv, movie, anime, cartoon and anime_movie and qBittorrent creates them on the first add.

  • Watch

    Pharos also reads every torrent’s file list and throws out a release that carries a program file, so the excluded list is a second wall, not the only one.

Reel 03

Prowlarr, the indexers

One search in Pharos fans out to every indexer you add to Prowlarr. Adding a site is a Prowlarr setting, never code.

  1. Bundled: start the stack with --profile prowlarr (port 9696). Your own: set PROWLARR_URL.

  2. Copy the API key from Prowlarr → Settings → General into PROWLARR_API_KEY.

  3. Add the indexers you have access to. Set each one’s privacy correctly: Pharos reads it, and only people you grant private sources can search a private one.

  4. A site behind a Cloudflare check needs FlareSolverr. Run it on the same Docker network, add it in Prowlarr → Settings → Indexers as an indexer proxy tagged cloudflare, then give that tag to the indexers that need it.

    docker run -d --name flaresolverr --network pharos_default \
      --restart unless-stopped ghcr.io/flaresolverr/flaresolverr:latest
    # Prowlarr proxy host: http://flaresolverr:8191
WhereSetWhy
Each indexer → CategoriesLeave them alonePharos asks for the categories it wants on every search.
Settings → General → Proxyhttp://vpn:8888 with the bundled VPN, or your VPN’s SOCKS5 server (reel 04)Indexer traffic should leave from the VPN, not your home address.
How ours is wired

A handful of public indexers plus one private tracker, all in Prowlarr, with FlareSolverr in front of the two sites that sit behind a Cloudflare check.

Lines in your .env
USE_PROWLARR=true
PROWLARR_URL=http://prowlarr:9696
PROWLARR_API_KEY=
PROWLARR_ALLOW_USENET=false

In the appAdmin → Settings → Connections → Prowlarr: the address, API key, indexers to skip and whether usenet results are included.

  • Watch

    FlareSolverr must use the same exit as Prowlarr. A Cloudflare pass is tied to the address that solved it, so a FlareSolverr on your home line hands Prowlarr a pass that fails.

  • Watch

    Behind a proxy, FlareSolverr needs version 3.4.2 or later and its PROXY_URL setting. HTTP_PROXY is ignored.

Reel 04

The VPN

Two things should never leave from your home address: qBittorrent’s peers and the searches that reach your indexers. Pharos works with any VPN that gives it a proxy, and every five minutes it checks that the proxy really changes its address.

  1. Bundled VPN: start the stack with --profile vpn and fill in your provider’s settings. They use gluetun’s own names, and its wiki lists what each provider needs. Then point Pharos at it.

    VPN_PROXY=http://vpn:8888
    VPN_SERVICE_PROVIDER=<your provider>
    VPN_TYPE=wireguard
    WIREGUARD_PRIVATE_KEY=…
    WIREGUARD_ADDRESSES=…
  2. Or use your provider’s own SOCKS5 server, or any proxy on a machine that is already tunnelled.

    VPN_PROXY=socks5h://USER:PASSWORD@HOST:1080
  3. VPN_PROXY does not cover qBittorrent’s peer traffic, and that is most of what a torrent client sends. Bundled qBittorrent: start --profile vpn --profile qbittorrent-vpn instead of --profile qbittorrent and set QB_URL=http://vpn:8080. Its Web UI moves to QBIT_VPN_PORT (8081), and it has no route out at all while the VPN is down. Your own qBittorrent: bind it to the VPN interface (reel 02).

  4. Prowlarr has its own proxy: Settings → General → Proxy, pointed at http://vpn:8888 (or your SOCKS5 server), with localhost,127.0.0.1,host.docker.internal on the bypass list.

  5. Open Admin → Settings → Safety. It shows whether the proxy address differs from the direct one. If they match, Pharos refuses private-tracker traffic until it is fixed.

How ours is wired

qBittorrent runs on the host, bound to the VPN interface. The containers that need the VPN go through the VPN’s proxy instead of trusting split tunnelling.

Lines in your .env
VPN_PROXY=http://vpn:8888
VPN_SERVICE_PROVIDER=
VPN_TYPE=wireguard
WIREGUARD_PRIVATE_KEY=
WIREGUARD_ADDRESSES=
VPN_SERVER_COUNTRIES=
QBIT_VPN_PORT=8081

In the appAdmin → Settings → Connections → VPN: the proxy, and “This whole server is on the VPN”. The bundled VPN’s provider settings stay in .env, because its container reads them when it starts.

  • Watch

    On Docker Desktop (Windows and macOS), split tunnelling cannot see container traffic. Containers leave from your home address even while the VPN app says connected.

  • Watch

    Set VPN_ALLOW_SAME_IP=1 only when this whole machine already goes through the VPN, so the direct and proxied addresses are both the VPN’s.

  • Watch

    Use socks5h:// rather than socks5:// so names are looked up inside the tunnel too.

  • Watch

    Behind a VPN, qBittorrent gets no incoming peers unless your provider forwards a port. Downloads still work; they find fewer peers.

Check · Ask from inside the container, never from your own shell
docker exec <container> curl -s https://ifconfig.io
# must print the VPN's address, not your home one
Reel 05

Plex, the screen

Pharos reads your Plex library to badge what you already own and to find subtitles, and files new downloads straight into the folders Plex watches.

  1. Point PLEX_URL at your server. On the same machine as the containers that is host.docker.internal:32400.

  2. Get a token: sign in at app.plex.tv, open any title, choose Get Info → View XML, and copy the X-Plex-Token value from the address bar.

  3. Make one Plex library per folder, named Movies, TV Shows (or Series), Cartoons, Anime and Anime Movies. The library finder sorts by these names.

How ours is wired

Plex runs on the same box with one library per folder, so a finished download is on the shelf as soon as it is filed.

Lines in your .env
PLEX_URL=http://host.docker.internal:32400
PLEX_TOKEN=
PLEX_PATH_MAP=

In the appAdmin → Settings → Connections → Plex: the address, token and path map, and the Test button lists the libraries it found.

  • Watch

    Plex must see the same folders under MEDIA_ROOT. A library pointed at a copy is a library Pharos cannot file into.

  • Watch

    English subtitles come from Plex’s own subtitle search and are written beside the file, so every viewer gets them. If Plex sees the media under another path than /media (say /data), set PLEX_PATH_MAP=/data=/media; otherwise the subtitle stays a Plex selection for one account.

Reel 06

TMDB, the posters

Titles, posters, air dates and the Hearth matching all come from The Movie Database. A key is free.

  1. Make a free account at themoviedb.org, then Settings → API → request a v3 key.

  2. Put it in TMDB_API_KEY. The stack passes it to the backend and builds it into the web app.

How ours is wired

One key used for nothing else, because the web app carries it to the browser.

Lines in your .env
TMDB_API_KEY=

In the appAdmin → Settings → Connections → TMDB. Keep it in .env as well: the web app’s own poster lookups use the copy built into it.

  • Watch

    The key is built into the web app and visible in the browser. Use a key you made for Pharos.

  • Watch

    Changing it means rebuilding the web container: up -d --build web.

Reel 07

Filing, in Admin

File completed downloads into the libraries you choose. Storage rules control the drives, transfer methods and timing; the unzipper checks archives before opening them.

  1. Nothing to do on a new install: the organizer starts on, watching /media/Downloads and filing into the library folders your .env names (/media/Movies, /media/Series and so on). Admin → Settings → Media Organizer shows them and can change them.

  2. For more control, open Storage & drives. Register your mounted paths, choose download inboxes and destinations for each media type, set reserves and filing windows, preview, then enable storage rules. These rules take over filing from the older organizer.

  3. Choose hardlink-or-copy to keep seeding, copy for independent files, hardlink-only to wait for a compatible destination, or move to remove verified source media after its torrent stops. Test with one completed title and check Preview & activity.

How ours is wired

Downloads → Movies, Series, Cartoons, Anime and Anime Movies, with the junk purged and archives inspected before they open.

In the appAdmin → Settings → Storage & drives: mounted disks and shares, inboxes, destinations, fallback drives, reserves, transfer modes, schedules, pause and preview. Archive folders remain in Unzipper.

  • Watch

    Once storage rules are enabled, edit filing there. Turning them off restores the older organizer. Neither changing a destination nor registering a drive migrates an existing library.

  • Watch

    Filing windows control when a new transfer starts; a copy already running finishes safely. Download-client schedules and incomplete folders remain in the client.

  • Watch

    An archive that needs a password, holds a program file, or has no video in it is left where it is and logged as [GATE]. That is deliberate: read the reason before you unpack one by hand.

Reel 08 · optional

Hearth, the second library

Hearth is a second home for movies and shows beside Plex. Pharos lists both on one shelf, badges what each one holds, and plays from either.

New to Hearth? See what it does on its own site (opens Hearth in a new tab).

  1. In Hearth, open the Videos library tools → API access… and create a key named Pharos. It starts with hk_ and is shown once.

  2. On the Hearth server, list Pharos’s web address in HEARTH_API_CORS_ORIGINS and set HEARTH_PUBLIC_URL, so a browser may play what Pharos lists.

  3. In Pharos, set HEARTH_URL to Hearth’s LAN address (not its public one) and HEARTH_API_KEY to the key.

  4. The other direction is HearthSeek: in Pharos open Hearth in the side menu, create a connection token (hs_…), and paste it into Hearth → Torrents → HearthSeek → Connect Pharos.

How ours is wired

Plex and Hearth side by side: one search, one shelf, “In Plex”, “In Hearth” or “In library” on every poster.

Lines in your .env
HEARTH_URL=http://host.docker.internal:8770
HEARTH_API_KEY=

In the appAdmin → Settings → Connections → Hearth: the address, API key and public address.

  • Watch

    Deleting and the duplicate, CAM and SD scanners only ever touch Plex. Hearth’s library is read, never changed.

Check · The source answers
curl -s http://localhost:3000/api/library/sources
Reel 09 · optional

Usenet, the second source

With a usenet provider and an indexer, a search also returns NZBs, and Pharos hands them to NZBGet. Nothing here uses qBittorrent.

  1. Add your usenet indexer to Prowlarr as a Newznab indexer and set PROWLARR_ALLOW_USENET=true.

  2. In NZBGet, add your provider’s server and a category named plexseek whose destination is your Downloads folder.

  3. Run NZBGet where it can reach the indexer: Pharos passes it the indexer’s link, and NZBGet fetches it.

How ours is wired

NZBGet on the same Docker network as the indexer, with one category whose destination is the Downloads folder.

Lines in your .env
PROWLARR_ALLOW_USENET=true
NZBGET_URL=http://nzbget:6789
NZBGET_USERNAME=nzbget
NZBGET_PASSWORD=
NZBGET_CATEGORY=plexseek

In the appAdmin → Settings → Connections → NZBGet: the address, user name and password.

  • Watch

    Change NZBGet’s settings in its web UI or in nzbget.conf. Writing config through its saveconfig API once erased the server list, the main folder and the login.

  • Watch

    Usenet results have no seeders, and Pharos never applies the seeder floor to them.

Reel 10 · optional

Outside access

Pharos answers on one address, like a Plex server on its one port: the web app and the backend sit behind a small front door (Caddy) on port 3000. At home that is already done. For friends, give it a name and open one port, or use a tunnel.

  1. At home: open http://<your box>:3000. Nothing to set.

  2. Forward a port, like Plex. Get a name that points at your home address: your own domain, or a free one at duckdns.org. With duckdns, start the stack with --profile duckdns so the name follows your address when it changes.

    PHAROS_SITE=yourname.duckdns.org
    PLEXSEEK_PUBLIC_API_URL=https://yourname.duckdns.org
    PHAROS_WEB_URL=https://yourname.duckdns.org
    DUCKDNS_SUBDOMAIN=yourname
    DUCKDNS_TOKEN=…
  3. On your router, forward TCP port 443 to this box’s port 8443 (PHAROS_HTTPS_PORT), then start the stack again. Caddy fetches the HTTPS certificate and renews it by itself. Send your friends https://yourname.duckdns.org.

  4. Or a tunnel, when your provider gives you no public address (CGNAT) or you would rather open no port: point Cloudflare Tunnel or Tailscale Funnel at http://<your box>:3000, leave PHAROS_SITE empty, and set PLEXSEEK_PUBLIC_API_URL and PHAROS_WEB_URL to the tunnel’s address.

How ours is wired

A tunnel publishes Pharos, so nothing on the box has an open port. Most homes are better served by the port forward: no account anywhere, and HTTPS on its own.

Lines in your .env
PHAROS_SITE=yourname.duckdns.org
PHAROS_HTTPS_PORT=8443
PLEXSEEK_PUBLIC_API_URL=https://yourname.duckdns.org
PHAROS_WEB_URL=https://yourname.duckdns.org
DUCKDNS_SUBDOMAIN=
DUCKDNS_TOKEN=

In the appAdmin → Settings → Connections → Site: the web address people open and the public hostnames. The name, the ports and the certificate stay in .env, because the front door reads them when it starts.

  • Watch

    Never forward port 3000 itself. It is plain HTTP, and every password would cross the internet unencrypted.

  • Watch

    Only 443 has to be open: Caddy proves the name over that same port. Port 80 is not needed.

  • Watch

    If the whole box goes out through a VPN, the name points at the VPN and a port forward cannot reach you. Use a tunnel then.

  • Watch

    Many routers keep a dynamic name updated themselves (DDNS). Use that instead of --profile duckdns if yours does.

Check · From a machine outside your network
curl -s https://yourname.duckdns.org/api/health
# an answer means the port, the name and the certificate all work
Reel 11

Opening night

Start the stack, make the first admin, and invite your people. Everyone signs in with their own account; nobody shares yours.

  1. Start everything. Leave out a --profile for any service you run yourself.

    docker compose -f selfhost/docker-compose.yml --env-file selfhost/.env \
      --profile qbittorrent --profile prowlarr up -d --build
  2. Open http://<your box>:3000, the front door. With no admin set in .env, it sends you to /setup, which asks for a one-time token from the backend log or selfhost/state/data/setup_token.txt. The token stops working once the first admin exists.

    docker compose -f selfhost/docker-compose.yml --env-file selfhost/.env logs backend | grep ACCOUNTS
  3. Invite people from Admin → Users. The same page decides who may use private indexers: “Private sources by default” (admins only, everyone or nobody) and a switch on each person. PRIVATE_SOURCES_FOR only sets the starting default.

  4. A request from someone who is not an admin waits on Admin → Requests until you approve it, unless you set them to auto-approve. REQUEST_APPROVALS=off turns the gate off.

  5. Optional: let people reset a forgotten password by email. Fill in the SMTP_ lines (any mail provider’s SMTP server; for Gmail use an app password) or Admin → Connections → Email, and set PHAROS_WEB_URL so the link points at your address. Without email, you get a notice and make the reset link on Admin → Users yourself.

  6. Optional: connect an AI assistant. In claude.ai or the Claude app, add a custom connector with your Pharos address followed by /mcp, and sign in with a Pharos account. It acts as that person, so approvals still apply, and each friend can connect their own. Claude Code and other clients take a token from Integrations → AI assistants instead.

    https://<your Pharos address>/mcp
How ours is wired

Family and friends each have an account. A friend’s request waits for an admin to approve it; trusted people are switched to auto-approve.

Lines in your .env
PLEXSEEK_ADMIN_USERNAME=
PLEXSEEK_ADMIN_PASSWORD=
PRIVATE_SOURCES_FOR=admins
SMTP_HOST=
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_FROM=
Check · The backend is healthy
curl -s http://localhost:3000/api/health
Reel 12

Keeping it running

Updating is a pull and a rebuild. Your state folder survives both.

  1. Update.

    git pull
    docker compose -f selfhost/docker-compose.yml --env-file selfhost/.env up -d --build
  2. Back up selfhost/state. Nothing else holds state.

  3. After reinstalling or restoring qBittorrent, check that the excluded file names list is still on. A reinstall loses it without a word.

How ours is wired

Updates after a pull, one backup of the state folder, and a VPN check after anything on the network changes.

The cue sheet

Write your .env

Pick the booth closest to yours, switch what you have, and fill in your own values. The file and the start command build as you type. It all stays in this tab. Only MEDIA_ROOT is required: the connections can wait for Admin → Settings → Connections once Pharos is running.

What your booth has
The box
The initial media folder on a drive, DAS or mounted NAS share. For separate drives, add the storage compose override, then choose destinations and filing windows in Admin → Storage.
Who owns the media files (id -u on the host).
Their group (id -g).
First admin
Leave both empty to make the admin on /setup instead.
Private indexers start out for
Only the starting default: Admin → Users changes it later, per person too.
qBittorrent
Bundled: set it in the Web UI after the first start, then run the start command again.
The RSS feed tracked anime read (subs is the default audio). Pharos adds it to qBittorrent itself.
A Torznab feed from your Prowlarr’s Nyaa indexer works; Admin → Settings → Anime can fill it in later.
Prowlarr
Prowlarr → Settings → General.
Prowlarr indexer names Pharos should never search.
VPN
Does not cover qBittorrent’s peers: run it inside the VPN or bind it to the VPN interface.
gluetun’s wiki lists each provider’s name and settings.
Protocol
Plex
X-Plex-Token from any title’s View XML.
Only if Plex sees your media under a different path than /media.
TMDB
Built into the web app, so use a key made for Pharos.
When the film breaks

Nine fixes

The things that go wrong on a first night, and the reel that fixes each one.

Downloads finish but nothing reaches the library

Open Admin → Settings → Media Organizer: check it is on and that Monitored Folder and each library folder read /media/… and exist on disk under MEDIA_ROOT.

Reel 07 · Filing, in Admin
A tracked show never downloads

qBittorrent’s RSS switches are off, or the feeds were added after the show was tracked. Turn on both RSS options, then open the show’s rule in qBittorrent’s RSS downloader and tick the feeds it should read.

Reel 02 · qBittorrent, the downloader
Search comes back empty

Check PROWLARR_API_KEY, that Prowlarr has at least one enabled indexer, and that a Cloudflare-protected site has the cloudflare tag and a working FlareSolverr.

Reel 03 · Prowlarr, the indexers
No posters anywhere

TMDB_API_KEY is empty or wrong. The web app carries the key, so rebuild the web container after setting it.

Reel 06 · TMDB, the posters
It works at home but not for friends

Check from outside your network: the router must forward TCP 443 to the box’s port 8443, PHAROS_SITE must be the name you share, and that name must point at your current home address. Behind CGNAT no port forward can work; use a tunnel.

Reel 10 · Outside access
Downloads sit at 0%

The VPN is down, so a qBittorrent inside it or bound to it has no way out. Check the vpn container’s log, or the interface in qBittorrent → Advanced.

Reel 04 · The VPN
Private-tracker searches are refused

Pharos found the same address through VPN_PROXY as without it. Admin → Settings → Safety shows both; fix the proxy, or set VPN_ALLOW_SAME_IP=1 if the whole machine is behind the VPN.

Reel 04 · The VPN
Claude cannot connect

claude.ai reaches your server from the internet, so it needs the HTTPS address friends use (see Outside access), followed by /mcp. On a LAN-only box use Claude Code or Claude Desktop with a token from Integrations → AI assistants.

Reel 11 · Opening night
The setup page wants a token

That is the one-time first-admin token. Read it from the backend log (grep ACCOUNTS) or selfhost/state/data/setup_token.txt.

Reel 11 · Opening night

Doors open at yours.

The same code ours runs, in a stack made for your box. Nothing in it talks to anyone else’s install, and no login you type leaves it.