fix(decypharr): add appuser entry so fusermount3 can resolve UID 1000 - #25
Conversation
Fixes #24 When the `user: "${PUID}:${PGID}"` directive from #23 runs Decypharr as UID 1000, fusermount3 calls `id -un` to resolve the username. The base ghcr.io/sirrobot01/decypharr image has no /etc/passwd entry for UID 1000, so fusermount3 prints: fusermount3: could not determine username This causes the rclone FUSE/WebDAV mount to fail entirely, leaving all Downloaded files unresolvable and Sonarr/Radarr in a permanent error state with "timeout waiting for mount files". Fix: introduce a thin Dockerfile.decypharr that extends the upstream image with a single `adduser` call. Also bumps the pinned image to v2.4 (the version the reporter confirmed works after the patch) and updates docker-compose.yml to use the local build.
📝 WalkthroughWalkthroughThe change adds a local Decypharr image wrapper based on upstream v2.4, creates an ChangesDecypharr mount fix
Estimated code review effort: 2 (Simple) | ~10 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Summary by QodoFix Decypharr FUSE mount by adding UID 1000 passwd entry via wrapper image
AI Description
Diagram
High-Level Assessment
Files changed (2)
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Dockerfile.decypharr`:
- Line 23: Update the Dockerfile.decypharr user setup so the `/etc/passwd` entry
uses the configured `PUID` consumed by `docker-compose.yml`, rather than
hardcoding UID 1000; alternatively, explicitly constrain supported `PUID` values
to 1000 and ensure the compose configuration enforces that constraint.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 9e892e0d-2cbd-478b-8cd5-566a2bbb9128
📒 Files selected for processing (2)
Dockerfile.decypharrdocker-compose.yml
| # -D no password (daemon account) | ||
| # -H do not create a home directory | ||
| # -s /bin/sh minimal shell (required by some FUSE helpers) | ||
| RUN adduser -u 1000 -D -H -s /bin/sh appuser |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | sed -n '1,200p'
echo "== locate decypharr docker/compose =="
fd -a 'Dockerfile\.decypharr|decypharr' . | sed -n '1,200p'
echo "== dockerfile decypharr =="
if [ -f Dockerfile.decypharr ]; then
cat -n Dockerfile.decypharr
fi
echo "== compose files references to PUID =="
rg -n '\bPUID\b|uid_env|PUID.*Decypharr|Decypharr.*PUID|adduser -u 1000' -S . || trueRepository: nordicnode/TorBox-Media-Server
Length of output: 4935
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== docker-compose.yml decypharr section =="
cat -n docker-compose.yml | sed -n '1,220p' | rg -n 'decypharr|PUID|user:|appuser|environment|volumes|depends|build:' -C 12 || cat -n docker-compose.yml | sed -n '1,220p'
echo "== compose service excerpts around decypharr/user keys =="
python3 - <<'PY'
from pathlib import Path
p=Path('docker-compose.yml')
text=p.read_text()
for i,line in enumerate(text.splitlines(),1):
if ':decypharr' in line or line.startswith(' user:') or 'PUID=${PUID:-' in line:
start=max(1,i-8); end=min(len(text.splitlines()), i+8)
lines=text.splitlines()
print(f'--- around line {i} ---')
for n in range(start,end+1):
if n==i: print(f'{n:4}: {lines[n-1]} <--')
else: print(f'{n:4}: {lines[n-1]}')
PY
echo "== deterministic parse of docker-compose user/env patterns =="
python3 - <<'PY'
from pathlib import Path
import re
text=Path('docker-compose.yml').read_text().splitlines()
# simple per-service collection: service names start at column 0, keys indented
services=[]; cur=None
for i,line in enumerate(text,1):
if not line.startswith(' ') and not line.startswith('\t') and ':' in line and not line.startswith('#'):
parts=line.split(':',1); keys=parts[0].split()
if keys:
cur=(keys[0],i,line)
services.append(cur)
print('services detected:')
for s in services:
print(s)
# collect decypharr block
service=None; lines=[]; decypharr_end=None
for i,line in enumerate(text,1):
if line.rstrip() == 'decypharr:':
service=i
decypharr_end=i+1
elif service and i>service:
if line.startswith(' ') and not line.startswith(' '):
decypharr_end=i
break
if not line or line.startswith('#'):
continue
lines.append((i,line))
if service and lines:
print('decypharr user/env content lines:')
for i,line in lines:
print(f'{i}: {line}')
print('contains PUID expression with custom env:', any('PUID=${PUID:-' in l for _,l in lines))
PYRepository: nordicnode/TorBox-Media-Server
Length of output: 13491
🌐 Web query:
ghcr.io sirrobot01 decypharr Dockerfile adduser fusermount3 rclone
💡 Result:
Decypharr is a media gateway for Debrid services and Usenet that uses a Docker-based architecture to provide a unified interface for applications like Sonarr and Radarr [1][2]. The project's Dockerfile includes configurations to support Rclone mounting, which is a key feature for exposing Debrid content as a local filesystem [3][4]. Technical details regarding your query: Dockerfile Configuration: The project's Dockerfile is designed to handle Rclone dependencies and FUSE-based mounting [5][6]. It installs fuse3, shadow, and other utilities [5]. A specific line, echo "user_allow_other" >> /etc/fuse.conf, is included to enable non-root users to mount FUSE filesystems [5]. The Dockerfile also includes logic to download and install the current Rclone binary for the host's architecture (amd64, arm64, or arm) [5][6]. User Management: The inclusion of shadow and su-exec in the Dockerfile indicates that the image supports user and group ID management (via PUID/PGID), which is standard practice for containerized media applications to ensure consistent file permissions on mounted volumes [3][5]. FUSE and Rclone Integration: Decypharr utilizes Rclone's remote control (RC) server to manage mounts [4][7]. For successful operation, especially when using Rclone mounting features, the following requirements are typically noted in documentation and configuration: - Device: Access to /dev/fuse is required [3]. - Capabilities: The container must be run with the SYS_ADMIN capability [3]. - Mount Propagation: A /mnt volume with rshared propagation is recommended for Rclone mounts to be accessible outside the container [3]. Troubleshooting: If you encounter mounting issues, ensure that the container has sufficient privileges (SYS_ADMIN) and that /dev/fuse is correctly exposed [3]. Some users have noted that ensuring the Rclone configuration files have appropriate permissions (or running the container with correct PUID/PGID) is necessary for the Rclone RC server to initialize mounts correctly [8]. The application documentation also highlights that if FUSE or fusermount3 is not available, the system may fall back to WebDAV access [9][4].
Citations:
- 1: https://github.com/sirrobot01/decypharr
- 2: https://github.com/sirrobot01/decypharr/tree/main
- 3: https://deepwiki.com/sirrobot01/decypharr/2-getting-started
- 4: https://deepwiki.com/sirrobot01/decypharr/4.6-rclone-integration
- 5: https://github.com/sirrobot01/decypharr/blob/main/Dockerfile
- 6: https://github.com/sirrobot01/decypharr/blob/207d43b1/Dockerfile
- 7: sirrobot01/decypharr@6f9fafd
- 8: Rclone failing to mount after v2.0 release sirrobot01/decypharr#241
- 9: sirrobot01/decypharr@eba24c9
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== upstream dockerfile v2.4 =="
curl -fsSL "https://raw.githubusercontent.com/sirrobot01/decypharr/v2.4/Dockerfile" | sed -n '1,220p'
echo "== static shell behavior for current compose substitution =="
python3 - <<'PY'
import pathlib, re, sys
text = pathlib.Path("docker-compose.yml").read_text().splitlines()
service = None
for i, line in enumerate(text, 1):
if line.rstrip() == "decypharr:":
service = i
elif service and i > service and not line.startswith(" ") and not line.startswith(" "):
break
if line.startswith(" user:") or "PUID=${PUID:-" in line:
print(f"{i}: {line}")
PY
python3 - <<'PY'
import pathlib
text = pathlib.Path("docker-compose.yml").read_text()
m = re.search(r'user:\s*(.*?)[\n#]', text, re.S)
print("user expression:", m.group(1).strip() if m else None)
print("allows custom PUID != default:", 'PUID:-1000' in text)
print("local passwd arg fixed to 1000:", 'RUN adduser -u 1000' in pathlib.Path("Dockerfile.decypharr").read_text())
PYRepository: nordicnode/TorBox-Media-Server
Length of output: 3476
Keep the passwd entry aligned with PUID.
docker-compose.yml reads PUID from the environment and uses it for decypharr.user, but Dockerfile.decypharr only creates /etc/passwd for UID 1000. A supported non-default PUID will run as an unmapped UID, so the mount path that relies on username resolution can fail again. Generate the passwd entry for the configured PUID or reject/constrain PUID to 1000.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Dockerfile.decypharr` at line 23, Update the Dockerfile.decypharr user setup
so the `/etc/passwd` entry uses the configured `PUID` consumed by
`docker-compose.yml`, rather than hardcoding UID 1000; alternatively, explicitly
constrain supported `PUID` values to 1000 and ensure the compose configuration
enforces that constraint.
Code Review by Qodo
Context used✅ Compliance rules (platform):
10 rules 1. Install build context missing
|
| build: | ||
| context: . | ||
| dockerfile: Dockerfile.decypharr | ||
| args: | ||
| DECYPHARR_VERSION: "v2.4" | ||
| image: torbox-decypharr:v2.4 |
There was a problem hiding this comment.
1. Install build context missing 🐞 Bug ☼ Reliability
docker-compose.yml now builds Decypharr from Dockerfile.decypharr, but setup.sh/setup.ps1 only copy docker-compose.yml into the install directory. Running docker compose up from the install directory (as generated manage.sh does) will fail because the referenced Dockerfile.decypharr is not present in the build context.
Agent Prompt
## Issue description
`docker-compose.yml` now uses `build:` with `dockerfile: Dockerfile.decypharr`, but the installation workflow only copies `docker-compose.yml` into the install directory. Since users operate the stack from the install directory (via generated `manage.sh` and setup startup), Decypharr cannot be built and startup fails.
## Issue Context
- `setup.sh` and `setup.ps1` both copy only `docker-compose.yml` to the install directory.
- The generated `manage.sh` runs Compose from that install directory, so `build.context: .` resolves to the install directory.
## Fix Focus Areas
- setup.sh[1319-1324]
- setup.ps1[547-559]
- docker-compose.yml[28-33]
- setup.sh[1429-1436]
## Suggested fix
1. Copy `Dockerfile.decypharr` into the install directory alongside `docker-compose.yml` in both:
- `generate_docker_compose()` in `setup.sh`
- `Invoke-GenerateDockerCompose` in `setup.ps1`
2. (Optional but helpful) Adjust `manage.sh pull/update` messaging or flow so it rebuilds the local image when appropriate (e.g., `docker compose build --pull decypharr`).
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| # -D no password (daemon account) | ||
| # -H do not create a home directory | ||
| # -s /bin/sh minimal shell (required by some FUSE helpers) | ||
| RUN adduser -u 1000 -D -H -s /bin/sh appuser |
There was a problem hiding this comment.
2. Hardcoded uid 1000 only 🐞 Bug ≡ Correctness
Dockerfile.decypharr only adds a passwd entry for UID 1000, while docker-compose.yml supports
running Decypharr as arbitrary ${PUID}:${PGID}. If a user sets PUID to a non-1000 value and the
upstream image lacks that UID entry, fusermount3’s id -un lookup can still fail and the mount
issue remains.
Agent Prompt
## Issue description
The wrapper image creates only UID 1000 (`appuser`). However, the stack explicitly supports custom PUID/PGID values at runtime. For custom PUIDs, the container may still lack a matching `/etc/passwd` entry and trigger the same fusermount3 username-resolution failure the PR is aiming to prevent.
## Issue Context
- Compose runs the container as `${PUID:-1000}:${PGID:-1000}`.
- Setup/README indicate PUID/PGID are configurable.
- The Dockerfile hardcodes `-u 1000`.
## Fix Focus Areas
- Dockerfile.decypharr[15-23]
- docker-compose.yml[28-36]
- README.md[242-255]
- .env.example[41-50]
## Suggested fix
Option A (build-time param, simplest):
1. Add `ARG APP_UID=1000` (and optionally `ARG APP_GID=1000`).
2. Use `RUN adduser -u ${APP_UID} ...` (and create/update group if needed for your base OS).
3. In `docker-compose.yml`, pass build args from env: `APP_UID: "${PUID:-1000}"` (and `APP_GID: "${PGID:-1000}"`).
4. Document that changing PUID/PGID requires rebuilding the image.
Option B (runtime entry, most flexible):
Create the passwd entry on container startup using the actual runtime UID/GID, so PUID changes don’t require rebuilds.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Qodo Fixer🍒 Ready to be cherry-picked — ✅ Merged (0) · ☑ Fixed (2) 🔗 Fix PR: #26 This fix PR was closed automatically. Its branch is preserved so you can cherry pick the changes into the original PR. Prompt for coding agent Process — 2 fixed
|
Fixes #24
When the
user: "${PUID}:${PGID}"directive from #23 runs Decypharr as UID 1000, fusermount3 callsid -unto resolve the username. The base ghcr.io/sirrobot01/decypharr image has no /etc/passwd entry for UID 1000, so fusermount3 prints:fusermount3: could not determine username
This causes the rclone FUSE/WebDAV mount to fail entirely, leaving all Downloaded files unresolvable and Sonarr/Radarr in a permanent error state with "timeout waiting for mount files".
Fix: introduce a thin Dockerfile.decypharr that extends the upstream image with a single
addusercall. Also bumps the pinned image to v2.4 (the version the reporter confirmed works after the patch) and updates docker-compose.yml to use the local build.Description
Describe your changes and the reasoning behind them.
Related Issue / PR
Fixes # (issue) or relates to # (PR)
Type of Change
Please delete options that are not relevant.
How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce.
bash tests/test_e2e.shChecklist
shfmton shell scripts)Summary by CodeRabbit