- Add compose stacks for apps already proxied by Caddy: bento-pdf,
it-tools, paperless-ngx, and beszel-hub (with Makefile targets)
- Remove the unused Vault stack and the drawio Caddyfile block
- Fix Caddyfile gramps upstream to port 80 to match the compose file
- Bring immich and gramps-web up to the shared template pattern
(no-new-privileges, log rotation, memory limits, healthchecks)
- Single-quote vaultwarden SSO_AUDIENCE_TRUSTED so regex values like
^\d{18}$ render as valid YAML
- Move root docs into kebab-case topic folders, rebuild the docs index
to cover every doc, and fix all broken links
663 lines
23 KiB
Markdown
663 lines
23 KiB
Markdown
# Docker Stack Troubleshooting Guide
|
||
|
||
## Quick diagnostics
|
||
|
||
Run these first:
|
||
|
||
```bash
|
||
docker ps -a
|
||
docker stats --no-stream
|
||
docker network ls
|
||
docker-compose -f docker-compose.arr-stack.yml config
|
||
```
|
||
|
||
## Common issues
|
||
|
||
### 1. Variables not substituted
|
||
|
||
Symptoms:
|
||
|
||
- Generated file still contains `{{VAR}}`
|
||
- Container fails due to missing env values
|
||
|
||
Fix:
|
||
|
||
```bash
|
||
./substitute_env.sh docker-compose-files/arr-stack/template.yaml docker-compose.arr-stack.yml .env
|
||
```
|
||
|
||
The script prints any unresolved placeholders. Add missing keys to `.env` (or provide defaults in templates).
|
||
|
||
### 2. Recyclarr errors (`base_url must start with http`)
|
||
|
||
Use service names, not `localhost`:
|
||
|
||
```yaml
|
||
sonarr:
|
||
sonarr-main:
|
||
base_url: http://sonarr:8989
|
||
|
||
radarr:
|
||
radarr-main:
|
||
base_url: http://radarr:7878
|
||
```
|
||
|
||
Generate your file from this repo template:
|
||
|
||
```bash
|
||
./substitute_env.sh docker-compose-files/arr-stack/config/recyclarr_template.yml /opt/arr-stack/appdata/recyclarr/recyclarr.yml .env
|
||
```
|
||
|
||
### 2b. Recyclarr errors (`Unable to find include template ...`)
|
||
|
||
Symptoms:
|
||
|
||
- `Unable to find include template with name 'radarr-quality-definition-movie'`
|
||
- Similar errors for other `include: - template:` entries
|
||
|
||
Cause:
|
||
|
||
- You are using an older pre-v8 `recyclarr.yml` that still has `include: - template:` entries.
|
||
- Recyclarr v8 removed official include templates from the default provider.
|
||
|
||
Fix (v8 path used by this repository):
|
||
|
||
```bash
|
||
# 1) Regenerate recyclarr config from the v8 template
|
||
./substitute_env.sh docker-compose-files/arr-stack/config/recyclarr_template.yml /opt/arr-stack/appdata/recyclarr/recyclarr.yml .env
|
||
|
||
# 2) Regenerate compose and recreate recyclarr (v8.6.0 pin)
|
||
./substitute_env.sh docker-compose-files/arr-stack/template.yaml docker-compose.arr-stack.yml .env
|
||
docker-compose -f docker-compose.arr-stack.yml pull recyclarr
|
||
docker-compose -f docker-compose.arr-stack.yml up -d recyclarr
|
||
```
|
||
|
||
### 3. qBittorrent/SAB not using VPN IP
|
||
|
||
In this template, only download clients are expected behind VPN tunnel.
|
||
|
||
- Behind VPN: `gluetun`, `qbittorrent`
|
||
- Not behind VPN by default: `radarr`, `sonarr`, `prowlarr`, `bazarr`, `emby`, `seerr`
|
||
|
||
Checks:
|
||
|
||
```bash
|
||
docker logs gluetun | grep -i connected
|
||
docker exec qbittorrent curl -s ifconfig.me
|
||
```
|
||
|
||
### 4. Dependency/healthcheck startup failures
|
||
|
||
Symptoms:
|
||
|
||
- `dependency failed to start`
|
||
- Services wait forever on `service_healthy`
|
||
|
||
Checks:
|
||
|
||
```bash
|
||
docker inspect gluetun | grep -A 20 Health
|
||
docker inspect prowlarr | grep -A 20 Health
|
||
docker logs <container_name>
|
||
```
|
||
|
||
Fixes:
|
||
|
||
- Increase `start_period` for slow-start services.
|
||
- Test healthcheck command inside the container.
|
||
- Validate generated compose YAML syntax with `docker-compose ... config`.
|
||
|
||
### 5. Network communication failures
|
||
|
||
Checks:
|
||
|
||
```bash
|
||
docker network inspect vpn_network
|
||
docker exec radarr wget -qO- http://prowlarr:9696/ping
|
||
```
|
||
|
||
Fixes:
|
||
|
||
- Ensure relevant services are on `vpn-network` in generated compose.
|
||
- Recreate stack networks:
|
||
|
||
```bash
|
||
docker-compose -f docker-compose.arr-stack.yml down
|
||
docker network prune
|
||
docker-compose -f docker-compose.arr-stack.yml up -d
|
||
```
|
||
|
||
### 5b. qBittorrent stuck in "Created" / `network service:gluetun not found`
|
||
|
||
Symptoms:
|
||
|
||
- `qbittorrent` does not start after updates and remains in `Created`
|
||
- Recreate shows: `Error response from daemon: network service:gluetun not found`
|
||
|
||
Cause:
|
||
|
||
- `network_mode: service:gluetun` can fail during single-service recreate/update flows on some Docker setups.
|
||
|
||
Fix:
|
||
|
||
```bash
|
||
# Ensure template uses container mode for qbittorrent:
|
||
# network_mode: "container:gluetun"
|
||
|
||
# Re-render compose and recreate the pair
|
||
./substitute_env.sh docker-compose-files/arr-stack/template.yaml docker-compose.arr-stack.yml .env
|
||
docker compose -f docker-compose.arr-stack.yml up -d --force-recreate gluetun qbittorrent
|
||
```
|
||
|
||
Verify:
|
||
|
||
```bash
|
||
docker compose -f docker-compose.arr-stack.yml ps gluetun qbittorrent
|
||
docker compose -f docker-compose.arr-stack.yml logs --tail 100 qbittorrent
|
||
```
|
||
|
||
### 5c. qBittorrent fails with `joining network namespace of container: No such container`
|
||
|
||
Symptoms:
|
||
|
||
- `docker compose up -d qbittorrent` exits with:
|
||
`Error response from daemon: joining network namespace of container: No such container: <hash>`
|
||
- Happens after gluetun was recreated (e.g., you ran `docker compose up -d` and
|
||
gluetun got a new container ID because its config changed).
|
||
|
||
Cause:
|
||
|
||
- qbittorrent stores the gluetun container ID at start time. When gluetun is
|
||
recreated its container ID changes, but the old qbittorrent container still
|
||
holds a reference to the obsolete ID. `docker compose up -d` leaves an already-
|
||
running qbittorrent alone, so the stale reference is never cleared.
|
||
|
||
Fix — force-remove the stale container and recreate it:
|
||
|
||
```bash
|
||
docker rm -f qbittorrent
|
||
docker compose -f docker-compose.arr-stack.yml up -d qbittorrent
|
||
```
|
||
|
||
Prevention: any time you explicitly recreate gluetun, recreate qbittorrent in
|
||
the same command so it picks up the new container ID:
|
||
|
||
```bash
|
||
docker compose -f docker-compose.arr-stack.yml up -d --force-recreate gluetun qbittorrent
|
||
```
|
||
|
||
### 5d. qBittorrent HTTPS `502` after stack updates
|
||
|
||
Symptoms:
|
||
|
||
- qBittorrent opens via HTTPS reverse proxy URL and returns `502` after stack updates/recreates.
|
||
|
||
Common causes:
|
||
|
||
- `gluetun` and `qbittorrent` are out of sync after recreate/update and need a paired restart.
|
||
- qBittorrent WebUI can be left in HTTPS mode while your reverse proxy still targets HTTP upstream.
|
||
|
||
Fix:
|
||
|
||
```bash
|
||
# 1) Recreate the VPN + torrent pair together
|
||
docker-compose -f docker-compose.arr-stack.yml up -d --force-recreate gluetun qbittorrent
|
||
|
||
# 2) If 502 persists, force qBittorrent WebUI back to HTTP
|
||
CONF="$(find /opt/arr-stack/appdata/qbittorrent -type f -name 'qBittorrent.conf' | head -n1)"
|
||
echo "Using config: $CONF"
|
||
sudo sed -i 's#^WebUI\\HTTPS\\Enabled=.*#WebUI\\HTTPS\\Enabled=false#' "$CONF"
|
||
sudo sed -i 's#^WebUI\\HTTPS\\CertificatePath=.*#WebUI\\HTTPS\\CertificatePath=#' "$CONF"
|
||
sudo sed -i 's#^WebUI\\HTTPS\\KeyPath=.*#WebUI\\HTTPS\\KeyPath=#' "$CONF"
|
||
docker-compose -f docker-compose.arr-stack.yml restart qbittorrent
|
||
```
|
||
|
||
Verify:
|
||
|
||
```bash
|
||
docker-compose -f docker-compose.arr-stack.yml ps gluetun qbittorrent
|
||
docker-compose -f docker-compose.arr-stack.yml logs --tail 120 qbittorrent
|
||
```
|
||
|
||
### 5e. Radarr/Sonarr reports a missing qBittorrent download path
|
||
|
||
Symptoms:
|
||
|
||
- Health reports that qBittorrent places downloads in a path that does not
|
||
exist inside Radarr or Sonarr.
|
||
- qBittorrent logs contain `Permission denied` for paths such as
|
||
`/data/.torrents/...`.
|
||
- The same torrent repeatedly rechecks or appears to restart because qBittorrent
|
||
first tries the stale path, fails the move, then resumes against the corrected
|
||
incomplete path.
|
||
|
||
Checks:
|
||
|
||
```bash
|
||
grep -E 'DefaultSavePath|TempPath' /opt/arr-stack/appdata/qbittorrent/config/qBittorrent.conf
|
||
docker exec radarr ls -ld /data/torrents /data/torrents/movies /data/torrents/tv
|
||
docker exec sonarr ls -ld /data/torrents /data/torrents/movies /data/torrents/tv
|
||
```
|
||
|
||
Required qBittorrent settings:
|
||
|
||
```text
|
||
Default save path: /data/torrents
|
||
Incomplete path: /data/torrents/incomplete
|
||
movies category: /data/torrents/movies
|
||
tv category: /data/torrents/tv
|
||
```
|
||
|
||
Do not use `/data/.torrents`, and do not add remote path mappings when all
|
||
containers use the documented `/data` layout. After correcting an existing
|
||
torrent's location, force recheck and resume it, then rerun the Radarr/Sonarr
|
||
health check.
|
||
|
||
### 5f. No indexers available with RSS sync enabled
|
||
|
||
Cause:
|
||
|
||
- Prowlarr indexers use an app profile such as `Automatic Search` that has RSS
|
||
disabled. Prowlarr synchronizes that disabled flag to Radarr and Sonarr.
|
||
|
||
Fix:
|
||
|
||
1. In Prowlarr, edit each affected indexer.
|
||
2. Set **App Profile** to `Standard`, or another profile with RSS, automatic
|
||
search, and interactive search enabled.
|
||
3. Open **Settings > Apps** and run **Sync App Indexers** for Radarr and
|
||
Sonarr.
|
||
4. Confirm the synchronized indexers show RSS enabled in both applications.
|
||
|
||
### 5g. Library Import leaves unmatched movie or series folders
|
||
|
||
Radarr expects one movie per folder. Split trilogy, franchise, and collection
|
||
packs into individual `Movie Title (Year)` folders before importing them.
|
||
Sample clips inside a movie folder can be mistaken for the feature; move or
|
||
remove samples before rescanning.
|
||
|
||
Sonarr permits only one root path per series. If duplicate series folders
|
||
exist, compare their episode coverage and quality, merge any unique episodes
|
||
into the preferred folder, update the series path, and verify the episode-file
|
||
count before removing the obsolete folder.
|
||
|
||
Do not delete unmatched folders solely because Library Import skipped them.
|
||
First verify whether they contain unique media, higher-quality replacements,
|
||
or files that were matched to the wrong title. Use exact TMDB/TVDB matches for
|
||
ambiguous titles.
|
||
|
||
### 5h. Download appears to restart mid-progress (qBittorrent OOM-killed)
|
||
|
||
Symptoms:
|
||
|
||
- A torrent regresses to a lower progress percentage mid-download (commonly at
|
||
60–80 % on large files).
|
||
- `qbittorrent.log` shows a new `qBittorrent v... started.` line with no preceding
|
||
error, roughly 10–15 minutes after the previous start.
|
||
- `docker inspect qbittorrent --format '{{.State.OOMKilled}}'` returns `true`.
|
||
- `docker stats --no-stream qbittorrent` shows memory near or at its limit.
|
||
|
||
Cause:
|
||
|
||
- qBittorrent buffers piece data in memory during download and hash-verification.
|
||
For large torrents (20 GB+) this can exceed the 1 GiB container limit, causing
|
||
the cgroup OOM killer to terminate the process. s6-overlay restarts qBittorrent
|
||
inside the same container (Docker restart count stays at 0), which triggers a
|
||
full piece re-verify from disk, visually resetting progress.
|
||
|
||
Fix — raise the limit live without restarting the container or losing progress:
|
||
|
||
```bash
|
||
docker update --memory 3g --memory-swap 6g qbittorrent
|
||
```
|
||
|
||
Then persist the change in the compose file and re-render from the template
|
||
(the template already sets 3 GiB):
|
||
|
||
```bash
|
||
./substitute_env.sh docker-compose-files/arr-stack/template.yaml docker-compose.arr-stack.yml .env
|
||
```
|
||
|
||
Do not lower `memory` below `2G` for a stack that downloads files larger than 10 GB.
|
||
|
||
### 5j. Download appears to restart mid-progress (Decluttarr auth ban loop)
|
||
|
||
Symptoms:
|
||
|
||
- A torrent that was at, say, 60 % progress appears to restart from a lower
|
||
percentage after qBittorrent logs show a process restart.
|
||
- `qbittorrent.log` contains a long run of:
|
||
```
|
||
(W) WebAPI login failure. Reason: IP has been banned, IP: ::ffff:172.18.0.12
|
||
```
|
||
followed by `qBittorrent v... started.`
|
||
- `docker logs decluttarr` shows repeated `401`/`403` errors against
|
||
`http://gluetun:8080/api/v2/auth/login`.
|
||
|
||
Cause:
|
||
|
||
- Decluttarr has blank (or wrong) `QBITTORRENT_USERNAME` / `QBITTORRENT_PASSWORD`
|
||
in `.env`. It polls qBittorrent every 30 seconds; after several failures
|
||
qBittorrent bans `172.18.0.0/16` (the Docker bridge), which forces a qBittorrent
|
||
process restart. After restart, qBittorrent re-verifies pieces on disk, which
|
||
can drop the reported progress before it climbs back.
|
||
|
||
Fix — whitelist the Docker bridge in qBittorrent so internal containers bypass
|
||
authentication:
|
||
|
||
```bash
|
||
# Run from inside the qbittorrent container (LocalHostAuth=false means no creds needed from localhost)
|
||
docker exec qbittorrent curl -s -X POST 'http://localhost:8080/api/v2/app/setPreferences' \
|
||
-d 'json={"bypass_auth_subnet_whitelist":"10.0.10.0/24\n172.18.0.0/16","bypass_auth_subnet_whitelist_enabled":true}'
|
||
|
||
# Verify
|
||
docker exec qbittorrent curl -s 'http://localhost:8080/api/v2/app/preferences' \
|
||
| python3 -c "import json,sys; p=json.load(sys.stdin); print(p.get('bypass_auth_subnet_whitelist'))"
|
||
```
|
||
|
||
qBittorrent saves this to its persisted config on the volume so it survives
|
||
container restarts. Alternatively, fill in `QBITTORRENT_USERNAME` and
|
||
`QBITTORRENT_PASSWORD` in `.env`, re-render the compose, and recreate the stack.
|
||
|
||
Verify fix:
|
||
|
||
```bash
|
||
docker restart decluttarr
|
||
docker logs decluttarr --tail 20
|
||
# Should show: INFO | OK | qBittorrent (http://gluetun:8080)
|
||
```
|
||
|
||
### 5k. Sonarr/Radarr warning: "Download client qBittorrent is set to remove completed downloads"
|
||
|
||
Symptoms:
|
||
|
||
- System status in Sonarr or Radarr shows:
|
||
`Download client qBittorrent is set to remove completed downloads. This can
|
||
result in downloads being removed from your client before Sonarr can import them.`
|
||
|
||
Cause:
|
||
|
||
- `Session\ShareLimitAction=Remove` in `qBittorrent.conf`. When seeding limits
|
||
are reached (ratio or time), qBittorrent removes the torrent entry before
|
||
Sonarr/Radarr can import it.
|
||
- The qBittorrent API `setPreferences` call does **not** persist `max_ratio_act`
|
||
to disk in the hotio image. The value must be changed directly in the config
|
||
file.
|
||
|
||
Fix — edit the config file and recreate the container:
|
||
|
||
```bash
|
||
# 1. Edit the config
|
||
sed -i 's/Session\\ShareLimitAction=Remove/Session\\ShareLimitAction=Pause/' \
|
||
/opt/arr-stack/appdata/qbittorrent/config/qBittorrent.conf
|
||
|
||
# Verify
|
||
grep 'ShareLimitAction' /opt/arr-stack/appdata/qbittorrent/config/qBittorrent.conf
|
||
# Expected: Session\ShareLimitAction=Pause
|
||
|
||
# 2. Recreate qBittorrent (must use rm + up, not restart, due to gluetun network_mode)
|
||
cd /opt/homelab-blueprint
|
||
docker rm -f qbittorrent
|
||
docker compose -f docker-compose.arr-stack.yml up -d qbittorrent
|
||
```
|
||
|
||
With `Pause` set, qBittorrent pauses the torrent when limits are hit instead of
|
||
removing it. Sonarr and Radarr then import the file and signal qBittorrent to
|
||
remove it themselves.
|
||
|
||
Verify the warning clears in Sonarr/Radarr **System → Status** (may need a
|
||
minute for the health check to re-run).
|
||
|
||
> **Note:** The default seeding limits in this stack are `GlobalMaxRatio=0.1`
|
||
> and `GlobalMaxSeedingMinutes=20`. These are intentionally aggressive for
|
||
> public trackers. Raise them in qBittorrent Settings → BitTorrent if you use
|
||
> private trackers, which typically require a minimum ratio of 1.0.
|
||
|
||
### 5l. Sonarr "Downloaded - Waiting to Import" — Episode has a TBA title and recently aired
|
||
|
||
Symptoms:
|
||
|
||
- A completed download sits in the Sonarr queue under **Activity → Queue** with
|
||
status `Downloaded - Waiting to Import`.
|
||
- The status message reads: `Episode has a TBA title and recently aired`.
|
||
- The episode title in TVDB is still `TBA` (common for hot new episodes within
|
||
24–48 hours of airing).
|
||
|
||
Cause:
|
||
|
||
- Sonarr's **Episode Title Required** setting (Settings → Media Management) is
|
||
set to `Always`. When TVDB hasn't published the real title yet, Sonarr holds
|
||
the download rather than importing it with "TBA" in the filename.
|
||
|
||
Fix — change the setting to allow single-episode imports even with a TBA title:
|
||
|
||
```bash
|
||
curl -s -X PUT 'http://localhost:8989/api/v3/config/mediamanagement' \
|
||
-H 'X-Api-Key: <your-sonarr-api-key>' \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{"episodeTitleRequired": "bulkSeasonReleases", ... <rest of current config>}'
|
||
```
|
||
|
||
Or via the UI: **Settings → Media Management → Episode Title Required → Bulk
|
||
Season Releases**.
|
||
|
||
| Value | Behaviour |
|
||
|---|---|
|
||
| `Always` | Block import if title is TBA — safest naming, but you wait |
|
||
| `Bulk Season Releases` (recommended) | Only block for full-season dumps; allow single-episode imports even if TBA |
|
||
| `Never` | Always import regardless of title |
|
||
|
||
After changing the setting, Sonarr will automatically process any held queue
|
||
items within its next scan cycle.
|
||
|
||
**If you already have a TBA file imported and want to rename it:**
|
||
|
||
Once TVDB publishes the real title, go to **Series → Refresh & Scan** in Sonarr
|
||
and it will rename the file automatically. To rename immediately using a title
|
||
from an external source (e.g., Overseerr):
|
||
|
||
```bash
|
||
# On the Proxmox host, via pct exec into the LXC:
|
||
OLD='/data/media/tv/Series Name/Season 03/Series Name (Year) - S03E01 - TBA [...].mkv'
|
||
NEW='/data/media/tv/Series Name/Season 03/Series Name (Year) - S03E01 - Real Title [...].mkv'
|
||
pct exec 101 -- mv "$OLD" "$NEW"
|
||
|
||
# Then trigger a Sonarr rescan to update the database path
|
||
curl -s -X POST 'http://10.0.10.20:8989/api/v3/command' \
|
||
-H 'X-Api-Key: <your-sonarr-api-key>' \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{"name": "RescanSeries", "seriesId": <id>}'
|
||
```
|
||
|
||
### 5m. Prowlarr indexer recommendations and usenet retention gaps
|
||
|
||
When usenet articles expire (typical retention: 12–14 months), SABnzbd logs
|
||
`Article ... unavailable on all servers, discarding` and the download shows an
|
||
ETA of hundreds of days. Public torrent indexers serve as a fallback for
|
||
older content.
|
||
|
||
**Active indexer list (as of 2026-06-22):**
|
||
|
||
| Indexer | Type | Priority | Notes |
|
||
|---|---|---|---|
|
||
| NZBgeek | Usenet | 1 | Primary usenet source |
|
||
| Nzb.life | Usenet | 2 | Secondary usenet source |
|
||
| altHUB | Usenet | 3 | Tertiary usenet source |
|
||
| 1337x | Torrent | 20 | Requires FlareSolverr (Cloudflare-protected) |
|
||
| EZTV | Torrent | 10 | TV-specific; requires FlareSolverr (primary URL eztvx.to is Cloudflare-protected) |
|
||
| Knaben | Torrent | 40 | Aggregator; good coverage of older content |
|
||
| The Pirate Bay | Torrent | 30 | Broad fallback; magnet-based |
|
||
|
||
FlareSolverr must be running for 1337x **and EZTV** — both use eztvx.to/Cloudflare-protected
|
||
primary URLs. FlareSolverr is included in the arr-stack compose and is configured
|
||
in Prowlarr under **Settings → Indexers → FlareSolverr Proxy** (tag ID 1).
|
||
Assign this tag to any Cardigann indexer whose primary URL returns a Cloudflare challenge.
|
||
|
||
**To add a missing indexer via the Prowlarr API:**
|
||
|
||
```bash
|
||
# 1. Get the schema for the indexer you want to add
|
||
curl -s 'http://10.0.10.20:9696/api/v1/indexer/schema' \
|
||
-H 'X-Api-Key: <prowlarr-api-key>' | jq '[.[] | {id: .id, name: .name}]'
|
||
|
||
# 2. Pull the specific schema
|
||
curl -s 'http://10.0.10.20:9696/api/v1/indexer/schema' \
|
||
-H 'X-Api-Key: <prowlarr-api-key>' \
|
||
| jq '.[] | select(.name == "EZTV")'
|
||
|
||
# 3. POST it (set enable: true and appProfileId: 1)
|
||
curl -s -X POST 'http://10.0.10.20:9696/api/v1/indexer' \
|
||
-H 'X-Api-Key: <prowlarr-api-key>' \
|
||
-H 'Content-Type: application/json' \
|
||
-d '<schema JSON with enable: true>'
|
||
|
||
# 4. Sync to Sonarr and Radarr
|
||
curl -s -X POST 'http://10.0.10.20:9696/api/v1/applications/sync' \
|
||
-H 'X-Api-Key: <prowlarr-api-key>'
|
||
```
|
||
|
||
**If EZTV shows "Indexer Disabled / Initial Failure":**
|
||
|
||
EZTV sometimes rate-limits or returns errors on first test. The 1-minute
|
||
disabled-until window is automatic. To re-enable:
|
||
|
||
1. In Prowlarr, go to **Indexers** and click the EZTV pencil icon.
|
||
2. Click **Test** — if it passes, save and the indexer re-enables.
|
||
3. If the test still fails, check that `eztv.re` is reachable from inside
|
||
gluetun (VPN IP may be blocked by EZTV). In that case, try the alternate
|
||
URL `eztvx.to` in the indexer base URL field.
|
||
|
||
**Private trackers (upgrade path):**
|
||
|
||
If you gain access to BTN (BroadcastTheNet) or TorrentLeech, add them via
|
||
Prowlarr — they have far better TV coverage and ratio requirements over
|
||
EZTV/1337x for current-season content.
|
||
|
||
### 6. Data/config persistence problems
|
||
|
||
Checks:
|
||
|
||
```bash
|
||
docker inspect radarr | grep -A 20 Mounts
|
||
ls -la /opt/arr-stack/appdata/radarr
|
||
```
|
||
|
||
Fixes:
|
||
|
||
```bash
|
||
mkdir -p /opt/arr-stack/appdata/radarr
|
||
chown -R <PUID>:<PGID> /opt/arr-stack/appdata/radarr
|
||
```
|
||
|
||
For stacks that use named volumes, inspect them with `docker volume ls` rather
|
||
than changing ownership on an old host appdata directory.
|
||
|
||
### 7. Caddy reverse proxy issues
|
||
|
||
If Caddy refuses to start, validate syntax first:
|
||
|
||
```bash
|
||
./substitute_env.sh docker-compose-files/caddy/Caddyfile_template docker-compose-files/caddy/Caddyfile .env
|
||
./substitute_env.sh docker-compose-files/caddy/template.yaml docker-compose.caddy.yml .env
|
||
chmod 644 docker-compose-files/caddy/Caddyfile
|
||
docker compose -f docker-compose.caddy.yml run --rm caddy \
|
||
caddy validate --config /etc/caddy/Caddyfile
|
||
```
|
||
|
||
If validation reports `open /etc/caddy/Caddyfile: permission denied`, the
|
||
template renderer created the generated file with mode `600`, and the
|
||
container's mapped user cannot read it. Apply the `chmod 644` command above.
|
||
The generated Caddyfile does not contain the Cloudflare token. Do not apply
|
||
that mode to `docker-compose.caddy.yml`, which does contain the rendered
|
||
token.
|
||
|
||
If logs report `mkdir /data/caddy: permission denied` or
|
||
`mkdir /config/caddy: permission denied`, the generated Compose file still
|
||
uses host bind mounts that are not writable through Docker's user-namespace
|
||
mapping. Re-render it from the current Caddy template, which uses named
|
||
volumes, then recreate the container:
|
||
|
||
```bash
|
||
./substitute_env.sh docker-compose-files/caddy/template.yaml docker-compose.caddy.yml .env
|
||
docker compose -f docker-compose.caddy.yml down
|
||
docker compose -f docker-compose.caddy.yml up -d --build
|
||
docker compose -f docker-compose.caddy.yml logs --tail=100 caddy
|
||
```
|
||
|
||
The same risk applies to any writable bind mount when Docker uses rootless or
|
||
user-namespace mapping. The Caddy, Beszel Agent, PostgreSQL/pgAdmin,
|
||
Technitium, Gramps Web, Home Assistant, and Mosquitto templates use
|
||
named volumes for application state to avoid this issue.
|
||
|
||
Arr Stack, Gramps media, Vault config/TLS, and other explicit external storage
|
||
mounts intentionally remain host-managed. Verify their ownership and access on
|
||
the target Docker installation; do not replace those mounts with named volumes
|
||
without planning configuration edits, backups, and existing-data migration.
|
||
|
||
Before changing an existing bind-mount deployment to named volumes, stop the
|
||
stack and copy its current data. Starting against a new empty named volume does
|
||
not import the old bind-mounted data automatically.
|
||
|
||
If certificate issuance fails with errors like:
|
||
|
||
- `could not determine zone for domain ...` / `expected 1 zone, got 0`
|
||
- `dial tcp 1.0.0.1:53: i/o timeout`
|
||
|
||
these are two distinct failure modes with the same underlying cause: Caddy's DNS-01 resolver
|
||
cannot reach `1.1.1.1`/`1.0.0.1` on port 53.
|
||
|
||
**Distinguish the two errors:**
|
||
|
||
| Error | Meaning |
|
||
|---|---|
|
||
| `expected 1 zone, got 0` | Caddy reached a resolver, but it was Technitium (split-DNS). NAT exception misconfigured or alias IP wrong. |
|
||
| `dial tcp 1.0.0.1:53: i/o timeout` | Caddy's packets are being dropped. A firewall block rule on OPT2 is above the pass rule, or the NAT exception is not set up at all. |
|
||
|
||
Check DNS reachability from the Caddy host:
|
||
|
||
```bash
|
||
# Must return Cloudflare SOA, not Technitium SOA (dns01.home.example.com)
|
||
dig +short SOA home.example.com @1.1.1.1
|
||
|
||
# Distinguish firewall drop from redirect:
|
||
dig @1.1.1.1 +tcp +time=5 google.com # timeout = firewall drop
|
||
dig @1.1.1.1 +notcp +time=5 google.com # timeout = same; success = TCP-specific block
|
||
```
|
||
|
||
Keep public resolvers in the Caddy TLS block — do not switch to Technitium as a workaround,
|
||
as it causes `expected 1 zone, got 0` because Technitium is authoritative for the internal zone:
|
||
|
||
```caddy
|
||
tls you@example.com {
|
||
dns cloudflare {env.CLOUDFLARE_DNS_TOKEN}
|
||
resolvers 1.1.1.1 1.0.0.1
|
||
}
|
||
```
|
||
|
||
If your network forces DNS redirection, exclude Caddy from that redirect and
|
||
allow it to query public resolvers on TCP and UDP port `53`.
|
||
|
||
If a proxied app fails over HTTPS with self-signed backend certs, ensure the target block uses:
|
||
|
||
```caddy
|
||
transport http {
|
||
tls_insecure_skip_verify
|
||
}
|
||
```
|
||
|
||
## Update flow (safe)
|
||
|
||
1. Generate compose files from templates.
|
||
2. Validate with `docker-compose -f <file> config`.
|
||
3. Pull images.
|
||
4. Recreate services.
|
||
5. Review logs after startup.
|
||
|
||
```bash
|
||
docker-compose -f docker-compose.arr-stack.yml pull
|
||
docker-compose -f docker-compose.arr-stack.yml up -d
|
||
```
|
||
|
||
---
|
||
|
||
**Last Updated:** 2026-06-22
|