Skip to content

feat: Run Docker container as non-root user#722

Open
hensing wants to merge 2 commits into
icereed:mainfrom
hensing:rootless
Open

feat: Run Docker container as non-root user#722
hensing wants to merge 2 commits into
icereed:mainfrom
hensing:rootless

Conversation

@hensing

@hensing hensing commented Oct 18, 2025

Copy link
Copy Markdown
Contributor

This PR introduces a rootless setup for the Docker container, enhancing security and resolving file permission issues. The application now runs as a non-root user with a configurable UID and GID.

Changes:

  • Added entrypoint.sh: A new entrypoint script handles the creation of a dedicated user (paperless-gpt) at runtime. It uses PUID and PGID environment variables (defaulting to 1000) to set the user and group IDs.
  • Permissions Fix: The script ensures proper ownership of the /app and /home/paperless-gpt directories, which resolves the mkdir /.config: permission denied error caused by libraries like pdfcpu.
  • Dockerfile Update: The Dockerfile now uses su-exec to drop privileges and executes the application via the new entrypoint script.
  • Docker Compose Update: The docker-compose.yml has been updated to include PUID and PGID environment variables, making it easy for users to configure permissions.
  • Documentation: The README.md has been updated to explain the new PUID/PGID variables and how to use them.

This implementation follows Docker best practices for running services as a non-root user, improving security and preventing common permission problems with mounted volumes.

Summary by CodeRabbit

  • New Features

    • Container now supports running as a non-root user with configurable PUID and PGID (defaults provided).
    • Startup now initializes user/group and directory ownership and launches the application as the unprivileged user using a lightweight helper.
  • Documentation

    • Added "Running as a Non-Root User" section, updated environment variable table and docker-compose examples showing PUID/PGID usage.

@coderabbitai

coderabbitai Bot commented Oct 18, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a non-root runtime flow: Dockerfile installs su-exec and uses an entrypoint script; new entrypoint.sh creates/uses PUID/PGID user and prepares directories; README and docker-compose examples document PUID/PGID usage (README defaults to 1000; docker-compose uses 10001).

Changes

Cohort / File(s) Summary
Dockerfile & Entrypoint
Dockerfile, entrypoint.sh
Dockerfile adds su-exec runtime dependency, copies entrypoint.sh, makes it executable, and replaces CMD with an ENTRYPOINT calling the script. entrypoint.sh (new) enforces strict mode, derives default PUID/PGID, creates group/user if missing, creates and chowns /app/* and /home/paperless-gpt, sets HOME, and launches the app via su-exec.
docker-compose example
docker-compose.yml
Adds an environment block for PUID and PGID (set to 10001 in the file) with comments illustrating non-root configuration placement.
Documentation
README.md
Adds PUID and PGID entries to Environment Variables table (defaults noted as 1000) and a new "Running as a Non-Root User" section showing how to obtain IDs and an example docker-compose snippet using PUID/PGID.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Docker as Docker container start
    participant Entrypoint as entrypoint.sh
    participant Sys as System (user/group)
    participant FS as Filesystem
    participant App as Application process

    Docker->>Entrypoint: start (env: PUID/PGID)
    Entrypoint->>Sys: ensure group (create if missing)
    Sys-->>Entrypoint: group ready
    Entrypoint->>Sys: ensure user (create if missing)
    Sys-->>Entrypoint: user ready
    Entrypoint->>FS: mkdir /app/prompts,/app/config,/app/db,/home/paperless-gpt
    FS-->>Entrypoint: dirs created
    Entrypoint->>FS: chown -R to paperless-gpt:paperless-gpt
    FS-->>Entrypoint: ownership set
    Entrypoint->>Entrypoint: export HOME=/home/paperless-gpt
    Entrypoint->>App: su-exec paperless-gpt -- run binary
    App-->>Docker: running as non-root user
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped in light, no root to chase,
I made a user, gave it space,
PUID, PGID, a gentle fix,
su-exec sets the safety mix,
Now the app hums in the proper place.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "feat: Run Docker container as non-root user" directly corresponds to the primary objective of this changeset. The title accurately reflects the main accomplishment across all modified files—introducing an entrypoint script with su-exec integration, configurable PUID/PGID variables, and documentation. The title is concise, clear, and specific enough that a reviewer scanning the history would immediately understand the core change without needing to examine the detailed file modifications.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@hensing

hensing commented Oct 18, 2025

Copy link
Copy Markdown
Contributor Author

@icereed Alternatively, and possibly even better, we could also set the docker container directly to UID 1000 or 33 (www). However, this is currently not backward compatible.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e391065 and f589fe5.

📒 Files selected for processing (4)
  • Dockerfile (1 hunks)
  • README.md (2 hunks)
  • docker-compose.yml (1 hunks)
  • entrypoint.sh (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (5)
entrypoint.sh (1)

1-28: Excellent Alpine compatibility and idempotent user/group setup.

The script correctly uses Alpine-compatible commands (addgroup, adduser, su-exec) and implements proper idempotency checks with getent before creating users and groups. The use of -D -S flags for system users, proper HOME setup, and structured entrypoint flow are all correct.

Dockerfile (1)

91-92: Dockerfile changes are correct and well-integrated.

  • su-exec is properly added as a runtime dependency (line 91-92) for privilege dropping in the entrypoint.
  • The entrypoint script is copied, made executable (line 100-102), and correctly positioned before app execution.
  • Switching to ENTRYPOINT (line 111) is the right pattern for wrapper scripts that manage setup before launching the main application.
  • All paths align correctly with the working directory and binary location.

Also applies to: 100-102, 111-111

docker-compose.yml (1)

8-13: Docker Compose configuration is clear and well-documented.

The PUID/PGID environment variables are correctly positioned before env_file, with helpful comments guiding users on how to find their IDs. The defaults (1000) match the entrypoint script and are standard for non-root user scenarios. Users can easily customize these values as needed.

README.md (2)

534-535: Environment variables table entries are clear and well-referenced.

The new PUID and PGID entries are properly documented with defaults (1000) and include cross-references to the detailed "Running as a Non-Root User" section. This guides users to both the quick reference and in-depth explanation.


940-956: New "Running as a Non-Root User" section provides practical guidance.

The documentation clearly explains the feature, provides actionable commands to find user/group IDs, and includes a concrete example showing customization. The placement is logical and the language is accessible. The example with PUID=1001, PGID=1001 effectively demonstrates that users can override defaults.

Comment thread entrypoint.sh Outdated
@matgoebl

Copy link
Copy Markdown
Contributor

Hello @hensing ,
Thanks for implementing non-root - this is what I also prefer in my cluster :-)
However kubesec.io recommends UID and GID > 10000, e.g. 10001.

@hensing

hensing commented Oct 19, 2025

Copy link
Copy Markdown
Contributor Author

@matgoebl thank you for the comment -> done :)

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 0

🧹 Nitpick comments (1)
entrypoint.sh (1)

8-12: Validation addresses the prior critical issue, but error message needs refinement.

The validation successfully prevents PUID=0 or PGID=0 and rejects negative values, resolving the previous concern. However, the error message on line 10 is grammatically awkward: "must non-root (0)" should be clarified—perhaps "must be positive integers and not equal to 0" or "must be greater than 0" for clarity.

Consider updating line 10 to:

echo "ERROR: PUID and PGID must be positive integers greater than 0 (got PUID=${PUID}, PGID=${PGID})"

This ensures users receive a clear, actionable error message if they accidentally set invalid values.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f589fe5 and a084591.

📒 Files selected for processing (3)
  • README.md (2 hunks)
  • docker-compose.yml (1 hunks)
  • entrypoint.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • docker-compose.yml
  • README.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (5)
entrypoint.sh (5)

1-6: Good foundation: portable shell and safe defaults.

The use of POSIX /bin/sh with set -e ensures portability and fail-fast behavior. The parameter expansion syntax is correct and the default UID/GID of 10001 aligns with security best practices mentioned in the PR comments.


14-21: Idempotent user/group creation with sound security practices.

The use of getent checks ensures the script is idempotent and safe to run multiple times. The adduser flags (-D -S -h -s /sbin/nologin) correctly create a system user with a disabled login shell, which follows Docker best practices.


23-27: Directory setup and permission handling is robust.

The use of mkdir -p ensures idempotency, and the recursive chown on both /app and /home/paperless-gpt directly addresses the permission issues (such as "mkdir /.config: permission denied") mentioned in the PR objectives. This ensures all files are writable by the non-root user.


29-30: HOME variable correctly targets user directory.

Setting and exporting HOME=/home/paperless-gpt ensures that any libraries or subprocesses writing configuration files will place them in the correct directory with proper permissions, avoiding permission denied errors.


32-34: Signal handling and privilege dropping are correctly implemented.

Using exec su-exec ensures the application process replaces the shell, allowing signals (SIGTERM, etc.) to be delivered directly to the application for graceful shutdown. The informational message aids visibility during container startup.


Summary

Your implementation successfully addresses the prior critical concern about PUID/PGID validation and follows Docker best practices for running as a non-root user. The script is idempotent, portable, and handles permission issues well.

One minor improvement recommended: Update the error message on line 10 to be grammatically clear and more actionable for users. The current wording "must non-root (0)" is awkward and could be misinterpreted.

Overall, the entrypoint script is solid and ready for deployment.

@hensing

hensing commented Nov 22, 2025

Copy link
Copy Markdown
Contributor Author

@icereed any chanced to get this into a release in the near future? can I help with something?

@Vyerni

Vyerni commented Mar 31, 2026

Copy link
Copy Markdown

Has this gone stale? Would love to move away from it running as root

@hensing

hensing commented Apr 16, 2026

Copy link
Copy Markdown
Contributor Author

Hey @icereed - any chances to get this merged?
maybe even the whole package from ivanzud#1?

@Vyerni

Vyerni commented May 8, 2026

Copy link
Copy Markdown

Going to guess we might not get to see this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants