Skip to content

refactor: update Containerfile to support non-root user execution and improve security#368

Merged
mostlygeek merged 2 commits intomostlygeek:mainfrom
ryan-steed-usa:containerfile-non-root
Nov 1, 2025
Merged

refactor: update Containerfile to support non-root user execution and improve security#368
mostlygeek merged 2 commits intomostlygeek:mainfrom
ryan-steed-usa:containerfile-non-root

Conversation

@ryan-steed-usa
Copy link
Copy Markdown
Contributor

@ryan-steed-usa ryan-steed-usa commented Oct 27, 2025

Summary

  • Updated LS_VER argument from 89 to 170 to use the latest version
  • Added UID/GID arguments with default values of 0 (root) for backward compatibility
  • Added USER_HOME environment variable set to /root
  • Implemented conditional user/group creation logic that only runs when UID/GID are not 0
  • Created necessary directory structure with proper ownership using mkdir and chown commands
  • Switched to non-root user execution for improved security posture
  • Updated COPY instruction to use --chown flag for proper file ownership

Example usage

The user defaults to root with a home directory of /root so hopefully this won't affect any existing builds. These changes make possible building the container with a dedicated UID/GID. Personally I prefer a rootless Podman setup, with limited container user, using a build command like:

time podman build -t llama-swap --no-cache --build-arg="BASE_TAG=server-cuda" --build-arg="LS_VER=${ curl -s https://api.github.com/repos/mostlygeek/llama-swap/releases/latest | jq -r .tag_name | sed 's/v//'; }" --build-arg="UID=123" --build-arg="GID=123" --build-arg="USER_HOME=/app" -f docker/llama-swap.Containerfile

And run with:

podman run --rm -d --replace -p 127.0.0.1:8080:8080 -v /ai/models:/models -v /ai/.cache:/app/.cache -v /ai/llama-swap-config.yaml:/app/config.yaml --gpus all --name llama-swap localhost/llama-swap:latest

Summary by CodeRabbit

  • Chores
    • Bumped version to 170.
    • Enabled non-root user execution for container deployments to improve security.
    • Ensured app files and configuration have correct ownership and permissions inside the container.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 27, 2025

Walkthrough

Updates the Docker Containerfile: bumps LS_VER 89→170, adds UID/GID and USER_HOME ARGs, conditionally creates a system user/group and sets ownership for /root and /app, uses COPY --chown for the config, and switches to the created non-root user; ENTRYPOINT command remains the same.

Changes

Cohort / File(s) Change Summary
Docker Containerfile
docker/llama-swap.Containerfile
LS_VER bumped from 89 to 170; new build ARGs UID, GID, USER_HOME; conditional system user/group creation when non-zero; HOME and /app created and chowned to UID:GID; COPY --chown=$UID:$GID config.example.yaml /app/config.yaml; switches to created non-root user before runtime; ENTRYPOINT preserved (formatting only).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Dev as Developer
  participant Docker as Docker Build
  participant Image as Built Image
  Dev->>Docker: Build with ARGs LS_VER, UID, GID, USER_HOME
  Docker->>Docker: Set LS_VER, create directories (/app, USER_HOME)
  alt UID/GID != 0
    Docker->>Docker: create group (GID) and user (UID) with HOME=USER_HOME
    Docker->>Docker: chown -R /app and USER_HOME to UID:GID
    Docker->>Docker: COPY --chown=$UID:$GID config.example.yaml -> /app/config.yaml
  else UID/GID == 0
    Docker->>Docker: keep root ownership, plain COPY config
  end
  Docker->>Image: finalize image, set USER to UID (if created)
  Image->>Image: ENTRYPOINT runs same command as before
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

  • Check conditional user/group creation (UID/GID non-zero) and correct flags for useradd/groupadd.
  • Confirm chown recursion targets only intended paths and uses correct variables.
  • Validate COPY --chown=$UID:$GID syntax works with the chosen Docker buildkit/context and variable expansion.
  • Verify ENTRYPOINT formatting change did not alter runtime behavior (no newline/escaping issues).

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "refactor: update Containerfile to support non-root user execution and improve security" accurately and clearly describes the primary objective of the changeset. The title directly corresponds to the main changes: implementing UID/GID build arguments, conditionally creating non-root users/groups, updating file ownership with --chown, and switching container execution to run as a non-root user. The title is specific and concise without being vague or misleading, properly identifying both the artifact being changed (Containerfile) and the key improvement (non-root user support with security benefits). While the changeset includes a secondary version bump to LS_VER, this does not detract from the title since it captures the primary intent of the PR.
✨ Finishing touches
🧪 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.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
docker/llama-swap.Containerfile (1)

23-24: Consider conditionally running chown only for non-root users.

When building with the default UID=0 and GID=0, the chown command is redundant since directories are already owned by root. While this doesn't cause issues, wrapping it in a conditional would be a minor optimization.

-RUN mkdir --parents $HOME /app
-RUN chown --recursive $UID:$GID $HOME /app
+RUN mkdir --parents $HOME /app && \
+    if [ $UID -ne 0 ]; then \
+      chown --recursive $UID:$GID $HOME /app; \
+    fi
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f852689 and 7c1c051.

📒 Files selected for processing (1)
  • docker/llama-swap.Containerfile (1 hunks)
🔇 Additional comments (5)
docker/llama-swap.Containerfile (5)

7-10: LGTM!

The default build arguments provide backward compatibility by defaulting to root user execution (UID=0, GID=0).


27-27: LGTM!

The USER directive correctly switches to the configured user, and maintains backward compatibility by staying as root when UID=0.


35-35: LGTM!

The --chown flag ensures the configuration file has the correct ownership, whether running as root or a non-root user.


38-38: LGTM!

The ENTRYPOINT formatting change has no behavioral impact.


5-5: Version 170 exists and is valid. The release was published on 2025-10-26, confirming the version bump is legitimate. All platform artifacts are available, including checksums for download integrity verification.

@ryan-steed-usa
Copy link
Copy Markdown
Contributor Author

Closes #369

… improve security

- Updated LS_VER argument from 89 to 170 to use the latest version
- Added UID/GID arguments with default values of 0 (root) for backward compatibility
- Added USER_HOME environment variable set to /root
- Implemented conditional user/group creation logic that only runs when UID/GID are not 0
- Created necessary directory structure with proper ownership using mkdir and chown commands
- Switched to non-root user execution for improved security posture
- Updated COPY instruction to use --chown flag for proper file ownership
@mostlygeek
Copy link
Copy Markdown
Owner

Hi, thanks for this contribution.

I think hard coding app:app, (uid:10001, gid:10001), with $HOME=/app into the container would be OK. Since llama-swap doesn't write any data out to disk that change shouldn't break backwards compatibility.

- Changed default UID and GID from 0 (root) to 10001 for security best practices
- Updated USER_HOME from /root to /app to avoid running as root user
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
docker/llama-swap.Containerfile (1)

22-24: Consider simplifying directory creation.

With USER_HOME=/app and HOME=$USER_HOME, the mkdir command creates /app twice. While harmless (thanks to --parents), you could simplify to:

-RUN mkdir --parents $HOME /app
+RUN mkdir --parents /app

Alternatively, keep the current form if you anticipate USER_HOME being set to something other than /app in future builds.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c1c051 and 7b9b4dd.

📒 Files selected for processing (1)
  • docker/llama-swap.Containerfile (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: ryan-steed-usa
Repo: mostlygeek/llama-swap PR: 368
File: docker/llama-swap.Containerfile:14-20
Timestamp: 2025-10-27T16:04:23.381Z
Learning: In docker/llama-swap.Containerfile, the user/group creation logic intentionally supports adding a non-root user (non-zero UID) to the root group (GID=0) as a valid configuration. This allows flexible UID/GID customization where users can specify only a UID or both UID and GID.
📚 Learning: 2025-10-27T16:04:23.381Z
Learnt from: ryan-steed-usa
Repo: mostlygeek/llama-swap PR: 368
File: docker/llama-swap.Containerfile:14-20
Timestamp: 2025-10-27T16:04:23.381Z
Learning: In docker/llama-swap.Containerfile, the user/group creation logic intentionally supports adding a non-root user (non-zero UID) to the root group (GID=0) as a valid configuration. This allows flexible UID/GID customization where users can specify only a UID or both UID and GID.

Applied to files:

  • docker/llama-swap.Containerfile
🔇 Additional comments (2)
docker/llama-swap.Containerfile (2)

14-20: User creation logic looks good.

The conditional user/group creation logic correctly implements the flexible UID/GID customization discussed in previous reviews. With the defaults of 10001 for both UID and GID, the container will run as non-root by default while still allowing build-time customization.

Based on learnings.


26-35: Excellent implementation of user switching and file ownership.

The sequence is correct:

  1. Ownership of /app is set (line 24) before switching users
  2. USER directive switches to the non-root user
  3. Downloads happen as the non-root user in their owned directory
  4. COPY --chown ensures correct ownership of the config file

This properly enables non-root execution while maintaining necessary permissions.

Comment on lines +7 to +11
# Set default UID/GID arguments
ARG UID=10001
ARG GID=10001
ARG USER_HOME=/app

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

PR description inconsistency with actual defaults.

The PR description states "defaults of 0 for backward compatibility," but the actual defaults are UID=10001, GID=10001. However, these non-zero defaults align with the maintainer's preference to hardcode uid:10001 and gid:10001, and the maintainer noted this should remain backward-compatible since llama-swap doesn't write to disk.

Consider updating the PR description to reflect the actual default values.

🤖 Prompt for AI Agents
In docker/llama-swap.Containerfile around lines 7 to 11 the ARG defaults are
UID=10001 and GID=10001 but the PR description incorrectly claims defaults of 0;
update the PR description to state the actual defaults are UID=10001 and
GID=10001, and optionally note the maintainer rationale that these non-zero
defaults are intentional and remain backward-compatible because llama-swap does
not write to disk.

@mostlygeek mostlygeek merged commit f91a8b2 into mostlygeek:main Nov 1, 2025
1 check passed
@mostlygeek
Copy link
Copy Markdown
Owner

@ryan-steed-usa thanks for the contribution

@ryan-steed-usa
Copy link
Copy Markdown
Contributor Author

I've updated the arguments as suggested but note that llama.cpp might write to /models and $HOME/.cache in cases where download arguments are used, like: -mu, --model-url or -hf, -hfr, --hf-repo. Personally, I use -hf to easily download and update models (this can become a bit of a pain in offline situations when unwilling to wait for network timeouts but can be mitigated with the -m, --model argument).

Depending on container runtime, and container configuration, a simple command like this might suffice to update ownership:

<container runtime, i.e. docker or podman> exec -u root -it <container name> bash -c 'chown -R app:app <mounted volumes>'

For example (but my deployment also required additional OS level ownership update due to rootless configuration, which I've omitted):

podman exec -u root -it llama-swap bash -c 'chown -R app:app /models /app/.cache /app/config.yaml'

I think that some container hosting systems have, in the past, required the root user in containers. These users could build images using arguments like:

--build-arg="UID=0" --build-arg="GID=0" --build-arg="USER_HOME=/root"

Let me know if these potential caveats are undesirable and I'll revert.

@mostlygeek
Copy link
Copy Markdown
Owner

let’s see if there are issued filed. It’s good security practice to run as a lesser privileged user. For mounting in shared volumes it’s ok to expect user/group permissions are set correctly.

@ryan-steed-usa
Copy link
Copy Markdown
Contributor Author

I couldn't agree more. Thank you!

@ryan-steed-usa ryan-steed-usa deleted the containerfile-non-root branch November 1, 2025 00:18
0uep pushed a commit to lynxai-team/llama-swap that referenced this pull request Nov 21, 2025
… improve security (mostlygeek#368)

Set default container user/group to lower privilege app user 

* refactor: update Containerfile to support non-root user execution and improve security

- Updated LS_VER argument from 89 to 170 to use the latest version
- Added UID/GID arguments with default values of 0 (root) for backward compatibility
- Added USER_HOME environment variable set to /root
- Implemented conditional user/group creation logic that only runs when UID/GID are not 0
- Created necessary directory structure with proper ownership using mkdir and chown commands
- Switched to non-root user execution for improved security posture
- Updated COPY instruction to use --chown flag for proper file ownership

* chore: update containerfile to use non-root user with proper UID/GID

- Changed default UID and GID from 0 (root) to 10001 for security best practices
- Updated USER_HOME from /root to /app to avoid running as root user
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.

2 participants