Skip to content

refactor(docker): enhance Docker setup and documentation#1245

Merged
Audionut merged 12 commits intoAudionut:masterfrom
maksii:docker-impro
Feb 15, 2026
Merged

refactor(docker): enhance Docker setup and documentation#1245
Audionut merged 12 commits intoAudionut:masterfrom
maksii:docker-impro

Conversation

@maksii
Copy link

@maksii maksii commented Feb 11, 2026

  • Updated docker-compose.yml for improved service configuration and added health checks.
  • Refined Dockerfile to ensure proper permissions and environment settings.
  • Enhanced upload.py to restore built-in data files when using Docker volumes.
  • Improved documentation for environment variables and volume mounts in docker-gui-wiki-full.md and docker-wiki-full.md.
  • Added notes on WebUI configuration and best practices for mounting data directories.

Summary by CodeRabbit

  • New Features

    • Safer container startup with ownership handling and non-root execution; automatic restoration of built-in data; WebUI defaults (port, healthchecks, help CMD)
  • Bug Fixes

    • Session-secret handling improved and cached; reliable WebUI config auto-generation; config loading adds retry and error-boundary protection
  • Documentation

    • Expanded Docker WebUI guide with required env vars, mounting, ownership, and troubleshooting
  • Chores

    • Added Docker ignore patterns and enforced LF for entrypoint scripts; updated compose and container defaults

- Updated docker-compose.yml for improved service configuration and added health checks.
- Refined Dockerfile to ensure proper permissions and environment settings.
- Enhanced upload.py to restore built-in data files when using Docker volumes.
- Improved documentation for environment variables and volume mounts in docker-gui-wiki-full.md and docker-wiki-full.md.
- Added notes on WebUI configuration and best practices for mounting data directories.
@coderabbitai
Copy link

coderabbitai bot commented Feb 11, 2026

Warning

Rate limit exceeded

@maksii has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 13 minutes and 27 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

Adds Docker build/run support and WebUI orchestration: new .dockerignore and .gitattributes, Dockerfile and docker-compose updates, a docker-entrypoint.sh for ownership/privilege handling, data restoration at startup, session-secret caching, frontend config load/error handling, and expanded Docker documentation.

Changes

Cohort / File(s) Summary
Docker config & build
/.dockerignore, /.gitattributes, Dockerfile
Add Docker build exclusions and LF enforcement for scripts; Dockerfile adds gosu, healthcheck, TMPDIR, data-preservation steps, combined chmod/chown, docker-entrypoint integration, and CMD/EXPOSE/STOPSIGNAL changes.
Compose & runtime config
docker-compose.yml
Rename service/container, add PUID/PGID and UA_BROWSE_ROOTS guidance, expand volumes for granular persistence, set healthcheck and stop_grace_period, switch to explicit webui command.
Entrypoint / permissions
docker-entrypoint.sh
New startup script ensuring directories exist, normalizing ownership/permissions, handling PUID/PGID, making /root traversable when needed, and dropping privileges via gosu before launching the app.
App startup & data handling
upload.py
Add restoration of built-in data/ from defaults/data/ when mounts hide files; avoid overwriting user files and caches; delay WebUI config creation until after restore; add runtime config reload capability.
WebUI auth & server
web_ui/auth.py, web_ui/server.py
Cache resolved session secret, handle SESSION_SECRET_FILE when it is a directory (auto-create session_secret with 0600), surface config load warnings to UI, and restrict config file loads to repository data/ .py files with clearer error reporting.
WebUI frontend & templates
web_ui/static/js/config_app.js, web_ui/templates/config.html
Add React error boundary, retry-capable config loading and in-page warning banner; provide non-empty loading HTML to avoid blank page before React mounts.
Docs
docs/docker-gui-wiki-full.md, docs/docker-wiki-full.md, docs/web-ui-basic.md
Expand Docker-focused guidance: mandatory UA_BROWSE_ROOTS in Docker, PUID/PGID usage, session-secret options, volume/ownership semantics, compose examples, Unraid notes, and troubleshooting adjustments.

Sequence Diagram(s)

sequenceDiagram
    participant Container as Container (Docker)
    participant Entrypoint as docker-entrypoint.sh
    participant App as upload.py
    participant FS as File System
    participant WebUI as WebUI

    Container->>Entrypoint: start (env: PUID/PGID, args)
    Entrypoint->>FS: ensure data, tmp, session_secret, config dirs exist
    Entrypoint->>FS: chown/chmod based on PUID/PGID
    alt TARGET_UID set & not zero
        Entrypoint->>FS: chmod /root (711)
        Entrypoint->>Entrypoint: drop privileges via gosu -> exec App
    else
        Entrypoint->>App: exec python /Upload-Assistant/upload.py [args]
    end
    App->>FS: detect hidden /Upload-Assistant/data (volume mount)
    alt data hidden by mount
        App->>FS: copy missing files from defaults/data -> data (skip caches)
        FS-->>App: report restored files/errors
    end
    App->>FS: ensure data/config.py exists for WebUI if requested
    App->>WebUI: start web server (port 5000)
    WebUI->>Container: respond to /api/health for HEALTHCHECK
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • PR #1141: Overlaps WebUI/startup and Docker-related changes (upload.py WebUI handling, session-secret logic, Docker startup files).
  • PR #1126: Modifies Dockerfile mkbrr download/workflow similar to the inline-invocation and build-step refactor.

Suggested reviewers

  • wastaken7

Poem

🐇 I hopped inside the Docker den,
Chowned the paths and fixed the fen,
Secrets snug in files so tight,
Restored the data through the night,
Now WebUI wakes — the rabbit grins again!

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor(docker): enhance Docker setup and documentation' accurately summarizes the main changes, which involve substantial updates to Docker configuration, documentation, and related support scripts.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into master

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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.

@maksii
Copy link
Author

maksii commented Feb 11, 2026

Attempt to address the current documentation and image challenges.
Image builds okay on the latest Ubuntu and Windows

New logic handles a fresh start for empty folder structure and would adjust permissions and access.

partially resolve #1187

- Removed existing defaults directory before creating a new one to ensure a clean setup.
- Updated the copy command to maintain the necessary data structure for the application.
- Added ConfigErrorBoundary to catch rendering errors and display a user-friendly message.
- Enhanced loading state in config.html to provide feedback during initial load and potential connection issues.
- Updated loadConfigOptions function to include retry logic for loading configuration options.
- Updated .dockerignore to exclude Python bytecode and cache directories more effectively.
- Improved docker-compose.yml to clarify environment variable usage for user/group permissions.
- Modified Dockerfile to include gosu for privilege management and adjusted entrypoint script for better directory ownership handling.
- Enhanced upload.py to skip unwanted directories during data restoration and added error handling for directory creation.
- Updated documentation in docker-gui-wiki-full.md to reflect changes in environment variable recommendations and directory permissions.
…ion secret handling

- Clarified the requirement for `UA_BROWSE_ROOTS` in Docker, emphasizing its necessity for proper file browsing functionality.
- Enhanced explanations regarding the handling of `SESSION_SECRET_FILE`, including potential pitfalls with Docker bind-mounts and recommendations for file management.
- Updated various documentation files to ensure consistency and clarity in usage instructions for environment variables and paths.
…er setup

- Updated docker-compose.yml and entrypoint.sh to improve explanations regarding the handling of SESSION_SECRET_FILE and directory ownership.
- Enhanced documentation in docker-gui-wiki-full.md to reflect changes in directory permissions and the behavior of the entrypoint script when PUID/PGID are set.
- Emphasized the importance of proper directory structure for session_secret management and the implications of using Docker bind-mounts.
@maksii maksii marked this pull request as ready for review February 12, 2026 10:57
Copy link

@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: 3

🤖 Fix all issues with AI agents
In `@docker-compose.yml`:
- Around line 83-85: When running with PUID/PGID and gosu dropping privileges,
auth.py:get_config_dir() relies on Path.home() and can miss the mounted
webui-auth; add XDG_CONFIG_HOME=/root/.config to the docker-compose.yml
environment block (or export XDG_CONFIG_HOME=/root/.config in the entrypoint
before invoking gosu) so the code resolves the config dir under
/root/.config/upload-assistant and the existing volume mapping
(/root/.config/upload-assistant) is used regardless of the container user
database state.

In `@docker-entrypoint.sh`:
- Around line 18-32: The startup fails if SESSION_SECRET_FILE is bind-mounted as
a file at /Upload-Assistant/session_secret because the existing code always runs
mkdir -p on each path; change the loop around the directories (the for ... in
/Upload-Assistant/data /Upload-Assistant/tmp /Upload-Assistant/session_secret
/root/.config/upload-assistant) to detect if a path exists and is a regular file
(e.g., test -f "$dir") and in that case skip mkdir and just chown the file
(using TARGET_UID/TARGET_GID if set), otherwise create the directory with mkdir
-p and then chown; ensure commands ignore failures when chown is unsupported
(keep the existing "|| true" behavior).

In `@docs/docker-gui-wiki-full.md`:
- Line 214: The sentence "If exposing the WebUI to your LAN/WAN, run behind a
reverse proxy with TLS is recommended." is grammatically incorrect; update it to
a correct form such as "If exposing the WebUI to your LAN/WAN, running it behind
a reverse proxy with TLS is recommended." Replace the original line in
docs/docker-gui-wiki-full.md (the WebUI LAN/WAN recommendation sentence) with
this corrected version.
🧹 Nitpick comments (4)
web_ui/auth.py (1)

153-189: Docker bind-mount directory handling is well thought out.

The auto-detection-and-recovery for the common Docker pitfall (bind-mount target created as a directory) is a solid UX improvement. The generated secret is consistent with the read-back path (hex-encoded on disk, bytes.fromhex on read).

A few minor static analysis nits from Ruff — all optional:

  1. Line 123: # noqa: PLW0603 is flagged as unused since PLW0603 isn't enabled in the Ruff config. Can be removed.
  2. Line 176: return b inside the try block — Ruff suggests moving it to an else block (TRY300).
  3. Line 178: log.error(...) in the exceptlog.exception(...) would automatically include the traceback (TRY400).
♻️ Optional: address Ruff hints
-    global _cached_session_secret  # noqa: PLW0603
+    global _cached_session_secret
                 try:
                     b = token_bytes(64)
                     p.write_text(b.hex(), encoding="utf-8")
                     with suppress(Exception):
                         os.chmod(p, 0o600)
-                    log.info("Auto-generated session secret at %s", p)
-                    return b
                 except Exception as e:
-                    log.error("failed to create session secret inside directory: %s", e)
+                    log.exception("failed to create session secret inside directory: %s", e)
                     raise OSError(
                         f"SESSION_SECRET_FILE points to a directory ({f}) and "
                         f"we could not create a session_secret file inside it; "
                         f"check permissions or mount a file instead"
                     ) from e
+                else:
+                    log.info("Auto-generated session secret at %s", p)
+                    return b
docker-compose.yml (1)

86-95: Healthcheck duplicated between Dockerfile and docker-compose.yml.

The Dockerfile (lines 86-87) already defines an identical HEALTHCHECK. The Compose healthcheck takes precedence at runtime, so there's no conflict, but having both means maintenance in two places. This is a minor nit — keeping it in both places is defensible (Compose for orchestrated deployments, Dockerfile for standalone docker run).

Dockerfile (1)

64-72: Permissions are hardcoded to UID 1000 — consider using a build arg.

The chown -R 1000:1000 bakes UID 1000 into the image. This works for the common case, but Unraid users (UID 99:100) rely on the o+rX fallback. The setup is functional, but if you ever want first-class multi-UID support without the "others" permission widening, a --build-arg for the UID would be cleaner. Low priority given the current approach works.

docs/docker-gui-wiki-full.md (1)

118-146: Unraid example omits webui-auth volume — intentional but worth a note.

The main Compose snippet (line 86) mounts a webui-auth volume to /root/.config/upload-assistant, but the Unraid snippet uses SESSION_SECRET_FILE=/Upload-Assistant/data/session_secret instead (relying on the data volume). Both are valid approaches, but Unraid users following both sections might wonder whether they need the webui-auth mount. A brief sentence noting the difference (e.g., "This example uses SESSION_SECRET_FILE inside the data mount instead of a separate webui-auth volume") would reduce confusion.

@maksii maksii closed this Feb 12, 2026
…nd WebUI

- Updated docker-entrypoint.sh to recursively fix ownership of user-placed files and ensure proper permissions for directories and files.
- Improved error handling and user feedback in server.py for loading configuration files, including warnings for missing or invalid config files.
- Added a config warning banner in the WebUI to inform users about configuration issues, enhancing user experience and clarity.
- Added functionality to reload the configuration from disk within the do_the_thing function, ensuring that changes made via the WebUI or manual edits are reflected without restarting the process.
- Included error handling to warn users if the configuration reload fails, enhancing robustness and user feedback.
@Audionut
Copy link
Owner

Did this not work out?

I'm more than happy to push any tweaks like this for this codebase.

@maksii
Copy link
Author

maksii commented Feb 13, 2026

@Audionut
Saw your message about closing\freez and decided to close, as a change would cause unnecessary load on you and potentially new bugs.
I don't know the context of that message, but if you still have the strength and motivation, I can reopen it, but than will do a bit of additional testing over the weekends
this one could be tested via maksii/utopia-upload-assistant:docker-impro-merge image(builded with almost same action that you was using, which i restored in different branch)

I was also preparing for a complete overhaul to fix shutdown issues and kill from ui and so scaffolded around 100 unit tests via AI(Some of them, of course, are not of the best quality, but at least they help to understand that nothing is broken) to make it easier to work with regression.
Given the current direction, I will probably leave this task alone, but I can do some PR on the tests themselves, which may help with maintenance.

@Audionut
Copy link
Owner

I'll message you at ath so you have a clear direction of my future plans.

@maksii
Copy link
Author

maksii commented Feb 14, 2026

Docker Image Verification — Test Results

image build ok locally on ubuntu and windows
image build ok via github action
Tested locally on unraid server with portainer on both host\mcvlan networks

# Name Portainer Stack Change Expected Outcome Result
1 Fresh start, empty volumes, all new Default stack with volumes data, tmp, webui-auth, torrents, BT_backup; UA_BROWSE_ROOTS=/data/torrents,/Upload-Assistant/tmp Container starts, WebUI on 5000, healthcheck passes. config.py auto-created. Session auth stored in webui-auth. ✅ Pass — Container started, ownership on root, able to update config, config reads in upload after change
2 Fresh start with config.py only Mount data containing only config.py (all other files cleared out) App restores missing files, config.py preserved ✅ Pass — App started ok, all files recreated, config not affected
2.1 Fresh start with config.py only, different owner Same as test 2, but config.py owned by another user with -rw-r--r-- App starts and fixes permissions as needed ✅ Pass — App started ok, all files recreated, config not affected
2.2 Fresh start with config.py as file mapping Same as test 2.1 but direct file mapping App starts ✅ Pass — App started ok, config not affected
4 Existing container — change PUID/PGID Change PUID/PGID (e.g. 1000:1000 → 99:100) Entrypoint updates ownership on data, tmp, webui-auth ✅ Pass — User switched, folders updated ownership to that user, all looks good
6-7 SESSION_SECRET_FILE + UA_BROWSE_ROOTS Add SESSION_SECRET_FILE=/Upload-Assistant/session_secret and mount on existing volumes Session persists; browse roots work ✅ Pass — Expected directory warning. After moving session file from webui-auth and restart, auto-login on refresh. Browse roots work okay
8 Port binding Use ports: "80:5000" to expose on host port 80. With macvlan (or similar), also set command: ["--webui", "0.0.0.0:80"] to change the internal port WebUI reachable on desired port ✅ Pass — All works okay. With macvlan, user should update command to change port

@maksii maksii reopened this Feb 14, 2026
Copy link

@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

🤖 Fix all issues with AI agents
In `@upload.py`:
- Around line 1286-1299: Reloading should not destructively clear the live
module-level config before validating the new value; change the block that uses
importlib.reload/_cfg_mod and config.clear()/config.update() to first reload
into a temp variable, check that the reloaded module has a valid config
attribute of the expected type (e.g., hasattr(_cfg_mod, "config") and
isinstance(_cfg_mod.config, dict)), and only then call config.clear() and
config.update(_cfg_mod.config); if validation fails or an exception occurs, keep
the existing config untouched and emit the same console.print warning (including
the error info) instead of leaving config empty.

In `@web_ui/server.py`:
- Around line 2439-2453: The current logic treats an empty dict and a load
failure the same because _load_config_from_file returns {} in both cases; update
_load_config_from_file to return None on failure (and adjust its return type
hint) and return {} for a successfully parsed but empty config, then change the
UI check that sets config_warning to use "if user_config is None" (or explicit
"is not None" where appropriate) instead of falsy checks; also update any other
callers of _load_config_from_file to handle None as the error sentinel.

@Audionut Audionut merged commit 452bcff into Audionut:master Feb 15, 2026
1 check passed
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