Docker: Build Optimization and Enhanced Non-Root/Volumeless Support (#5024) * docker: optimize build layers and enhance permission handling - Pre-created hardcoded dirs in Dockerfile to support volumeless non-root runs. - Enhanced slightly docker-entrypoint.sh with robust volume detection and safer chown logic. - Included legacy 'backups' directory... again. - Added dos2unix to install list. - Updated .dockerignore - Updated comments - Smaller fixes * fix(docker): removed unnecessary comment, and the... *sighs* backups dir, again * Exclude DS_Store everywhere * Exclude tests and all jsconfigs from docker images * Exclude local plugins from docker builds * fix(docker): backups are back... yay xD * feat(docker): add robust healthcheck script - Added `docker/healthcheck.cjs`: A standalone, dependency-free Node.js script for verifying server status. - Updated `Dockerfile`: Added HEALTHCHECK instruction and script copy step. - Features: Auto-detects port from env/config, handles IPv4/IPv6 fallback, auto-retries HTTPS on socket hangup, and sets custom User-Agent. * Fix .dockerignore permission * Revert "feat(docker): add robust healthcheck script" This reverts commit fa634fb08884cdef9245a12271cb9a13b487365f. --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

78651bdf560fae12da1d70e32e902a6e57f694b9

Pavdig <101715456+Pavdig@users.noreply.github.com>

Signed
3 files changed, +78 -45Showing whitespace changes
.dockerignore+55 -15
@@ -1,21 +1,61 @@
1+# --- Git & CI ---
12.git
23.github
34.vscodegitignore
4-node_modules
5+
5-npm-debug.log
6+# --- Docker ---
6-readme*
7+/Dockerfile
7-Start.bat
8+/.dockerignore
8-/dist
9+/docker/docker-compose.yml
9-/backups
10-cloudflared.exe
11-access.log
12-/data
13-/cache
14-.DS_Store
15-/public/scripts/extensions/third-party
16-/colab
17-.gemini
1810/docker/config
1911/docker/extensions
2012/docker/data
2113/docker/plugins
14+/public/scripts/extensions/third-party
15+
16+# --- Plugins (keep only package files) ---
17+/plugins/*
18+!/plugins/package.json
19+!/plugins/package-lock.json
20+
21+# --- The Folders ---
22+/backups
23+/cache
24+/colab
25+/data
26+/dist
27+/node_modules
28+/tests
29+
30+# --- Sensitive Info ---
31+**/.env*
32+**/*.pem
33+**/certs
34+
35+# --- Documentation ---
36+readme*
37+*.md
38+Update-Instructions.txt
39+
40+# --- OS & System Junk ---
41+**/.DS_Store
42+*.bat
43+*.cmd
44+*.exe
45+start.sh
46+
47+# --- Dev Config ---
48+.editorconfig
49+.eslintrc.cjs
50+.eslintrc*
51+.vscode
52+**/jsconfig.json
53+.npmignore
54+.gemini
55+replit.nix
56+.replit
57+.nomedia
58+
59+# -- Logs & Temp ---
60+*.log
61+**/tmp
Dockerfile+10 -10
@@ -4,8 +4,8 @@ FROM node:lts-alpine3.23
44ARG APP_HOME=/home/node/app
55
66# Install system dependencies
7-# Added su-exec and shadow to support optional PUID/PGID user mapping
7+# "Don't rely on the base image for tools; if you call it, you install it." ;)
88RUN apk add --no-cache gcompat tini git git-lfs su-exec shadow dos2unix
99
1010# Create app directory and set ownership
1111WORKDIR ${APP_HOME}
@@ -21,28 +21,28 @@ RUN \
2121 echo "*** Install npm packages ***" && \
2222 npm ci --no-audit --no-fund --loglevel=error --no-progress --omit=dev && npm cache clean --force
2323
2424# Create config directory and link config.yaml. Added hardcoded dirs(constants.js?)
25+# that must be present for Non-Root Mode and volumeless docker runs.
2526RUN \
2627 rm -f "config.yaml" || true && \
27- ln -s "./config/config.yaml" "config.yaml" || true && \
28+ mkdir -p config data plugins public/scripts/extensions/third-party backups && \
28- mkdir "config" || true
29+ chown -R node:node config data plugins public/scripts/extensions/third-party backups && \
29-# Set ownership
30+ ln -s "./config/config.yaml" "config.yaml"
30-RUN chown -R node:node config
3131
3232# Pre-compile public libraries
3333RUN \
3434 echo "*** Run Webpack ***" && \
3535 node "./docker/build-lib.js"
3636
3737# Set the entrypoint script and cleanup
3838RUN \
3939 echo "*** Cleanup ***" && \
4040 mv "./docker/docker-entrypoint.sh" "./" && \
41- rm -rf "./docker" && \
4241 echo "*** Make docker-entrypoint.sh executable ***" && \
4342 chmod +x "./docker-entrypoint.sh" && \
4443 echo "*** Convert line endings to Unix format ***" && \
4544 dos2unix "./docker-entrypoint.sh" && \
45+ rm -rf "./docker"
4646
4747# Fix extension repos permissions
4848RUN git config --global --add safe.directory "*"
docker/docker-entrypoint.sh+13 -20
@@ -8,7 +8,7 @@ start_sillytavern() {
88 # Config Check
99 if [ ! -e "config/config.yaml" ]; then
1010 echo "Resource not found, copying from defaults: config.yaml"
1111 $PREFIX cp -r "default/config.yaml" "config/config.yaml"
1212 fi
1313
1414 # Execute postinstall to auto-populate config.yaml with missing values
@@ -20,7 +20,7 @@ start_sillytavern() {
2020
2121# Dirs that MUST be present at this point (e.g for volumeless docker runs).
2222# Please update list, if in the future a related perm issue appear.
2323CORE_DIRS="config data plugins public/scripts/extensions/third-party backups"
2424
2525# Mounted Volumes (External)
2626# Parse mounts, handling files vs directories
@@ -35,11 +35,7 @@ for mount in $RAW_MOUNTS; do
3535
3636 # Performance Safety: If the file is in the root of the app,
3737 # we do NOT add the parent (App Root), or we will recursively scan the whole app.
3838 if [ "$PARENT_DIR" != "/home/node/app" ]; then&& MOUNTED_DIRS="$MOUNTED_DIRS $PARENT_DIR" || MOUNTED_DIRS="$MOUNTED_DIRS $mount"
39- MOUNTED_DIRS="$MOUNTED_DIRS $mount"
40- else
41- MOUNTED_DIRS="$MOUNTED_DIRS $PARENT_DIR"
42- fi
4339 else
4440 # It is a directory, add it directly
4541 MOUNTED_DIRS="$MOUNTED_DIRS $mount"
@@ -53,21 +49,19 @@ CHECK_DIRS=$(echo "$CORE_DIRS $MOUNTED_DIRS" | tr ' ' '\n' | sort -u)
5349for dir in $CHECK_DIRS; do
5450 if [ ! -e "$dir" ]; then
5551 echo "Creating missing directory: $dir"
56- mkdir -p "$dir"
52+ mkdir -p "$dir" 2>/dev/null || echo "Warning: Could not create $dir" >&2
5753 fi
5854done
5955
60-# Change permissions only if started as Root(UID 0) and needed.
56+# Mode Selection
6157if [ "$(id -u)" = "0" ]; then
6258 # Check if PUID/PGID variables are provided
6359 if [ -n "$PUID" ] && [ -n "$PGID" ]; then
64- TARGET_UID=$PUID
60+ echo "Mode: PUID/PGID (UID:$PUID GID:$PGID)"
65- TARGET_GID=$PGID
66- echo "Non-root mode requested (UID:$TARGET_UID GID:$TARGET_GID)."
6761
6862 # Update the internal 'node' user to match requested IDs
6963 groupmod -o -g "$TARGET_GIDPGID" node
7064 usermod -o -u "$TARGET_UIDPUID" -g "$TARGET_GIDPGID" node
7165
7266 for dir in $CHECK_DIRS; do
7367 if [ -d "$dir" ]; then
@@ -75,11 +69,9 @@ if [ "$(id -u)" = "0" ]; then
7569 DIR_UID=$(stat -c '%u' "$dir")
7670 DIR_GID=$(stat -c '%g' "$dir")
7771
7872 if [ "$DIR_UID" != "$TARGET_UIDPUID" ] || [ "$DIR_GID" != "$TARGET_GIDPGID" ]; then
7973 echo "(Detected mismatch) Adjusting permissions for: $dir."
80- if ! chown -R node:node "$dir"; then
74+ chown -R node:node "$dir" || echo "Warning: Failed to update permissions for '$dir'." >&2
81- echo "Error: Failed to update permissions for '$dir'."
82- fi
8375 fi
8476 fi
8577 done
@@ -87,16 +79,17 @@ if [ "$(id -u)" = "0" ]; then
8779 # Fix config file specifically
8880 chown node:node "config/config.yaml" 2>/dev/null
8981
82+ # Set execution prefix to run as 'node' user
9083 EXEC_PREFIX="su-exec node:node"
9184 else
9285 # Default: Run as Root (original behavior)
9386 echo "Running inMode: defaultDefault (rootRoot) mode."
9487 EXEC_PREFIX=""
9588 fi
9689
9790else
9891 # Non-Root Mode (Docker CLI --user flag)
9992 echo "Running asMode: detectedStrict userNon-Root (UID: $(id -u))."
10093 # We CANNOT auto-fix permissions in this mode because we lack privileges.
10194 # Relying solely on the user configuring their host permissions correctly.
10295 EXEC_PREFIX=""