Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Dockerfile.decypharr
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# =============================================================================
# Dockerfile.decypharr
#
# Thin wrapper around the upstream Decypharr image that adds a /etc/passwd
# entry for UID 1000 ("appuser"). This is required because the container is
# run as UID 1000 (via `user: PUID:PGID` in docker-compose.yml) and
# fusermount3 calls `id -un` to resolve the username before mounting the
# FUSE filesystem. Without a matching passwd entry fusermount3 exits with:
#
# fusermount3: could not determine username
#
# …which prevents the rclone WebDAV mount from ever establishing (issue #24).
# =============================================================================

ARG DECYPHARR_VERSION=v2.4
FROM ghcr.io/sirrobot01/decypharr:${DECYPHARR_VERSION}

# adduser flags:
# -u 1000 match the PUID used at runtime
# -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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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 . || true

Repository: 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))
PY

Repository: 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:


🏁 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())
PY

Repository: 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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

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

12 changes: 11 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,18 @@ services:
# ── Decypharr ──────────────────────────────────────────────────
# Mocks qBittorrent API for Radarr/Sonarr, connects to TorBox,
# handles WebDAV mounting via built-in rclone, and creates symlinks.
#
# We build a thin local image (Dockerfile.decypharr) on top of the
# upstream release. The only change is an `adduser` for UID 1000 so
# that fusermount3 can resolve the username when the container runs as
# a non-root user (fixes issue #24 / regression from #23).
decypharr:
image: ghcr.io/sirrobot01/decypharr:v2.0
build:
context: .
dockerfile: Dockerfile.decypharr
args:
DECYPHARR_VERSION: "v2.4"
image: torbox-decypharr:v2.4
Comment on lines +28 to +33

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

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

container_name: decypharr
user: "${PUID:-1000}:${PGID:-1000}"
restart: unless-stopped
Expand Down
Loading