diff --git a/docker/init/01-user-setup.sh b/docker/init/01-user-setup.sh index 43f0c514..522fcd8d 100644 --- a/docker/init/01-user-setup.sh +++ b/docker/init/01-user-setup.sh @@ -6,24 +6,11 @@ # runtime behavior since all postgres operations run as $POSTGRES_USER # rather than the postgres system user. -# Auto-detect PUID/PGID from existing data when not explicitly set. -# Avoids a cross-UID chown on upgrade, which would fail on restricted -# filesystems (NFS root_squash, CIFS). UID/GID 0 is excluded — PostgreSQL -# refuses to run as root. Falls through to default 1000 for new installs. -if [ -z "${PUID+x}" ] && [ -f "${POSTGRES_DIR}/PG_VERSION" ]; then - _data_uid=$(stat -c '%u' "${POSTGRES_DIR}/PG_VERSION") - if [ "$_data_uid" -ne 0 ] 2>/dev/null; then - export PUID=$_data_uid - echo "PUID not set — defaulting to existing data owner UID: $PUID" - fi -fi -if [ -z "${PGID+x}" ] && [ -f "${POSTGRES_DIR}/PG_VERSION" ]; then - _data_gid=$(stat -c '%g' "${POSTGRES_DIR}/PG_VERSION") - if [ "$_data_gid" -ne 0 ] 2>/dev/null; then - export PGID=$_data_gid - echo "PGID not set — defaulting to existing data owner GID: $PGID" - fi -fi +# Default PUID/PGID to 1000 when not explicitly set. +# The old image ran Django as UID 1000 and PostgreSQL as UID 102. Since +# DATA_DIRS and host-side files are owned by 1000, defaulting to 1000 +# preserves access for upgrading users without requiring configuration. +# The DB ownership migration (102 → 1000) is handled by 02-postgres.sh. export PUID=${PUID:-1000} export PGID=${PGID:-1000} diff --git a/docker/init/03-init-dispatcharr.sh b/docker/init/03-init-dispatcharr.sh index b580fe1e..05bcb192 100644 --- a/docker/init/03-init-dispatcharr.sh +++ b/docker/init/03-init-dispatcharr.sh @@ -1,6 +1,9 @@ #!/bin/bash -# Define directories that need to exist and be owned by PUID:PGID +# Define directories that need to exist and be owned by PUID:PGID. +# DATA_DIRS may reside on external mounts (NFS, SMB/CIFS, FUSE) where +# mkdir and chown can fail. Failures are collected and reported as a +# single consolidated warning so the container still starts. DATA_DIRS=( "/data/backups" "/data/logos" @@ -14,17 +17,25 @@ DATA_DIRS=( "/data/scripts" ) +# APP_DIRS live on the image layer and are always locally writable. APP_DIRS=( "/app/logo_cache" "/app/media" "/app/static" ) -# Create all directories -for dir in "${DATA_DIRS[@]}" "${APP_DIRS[@]}"; do +# Create app directories (image layer — always writable) +for dir in "${APP_DIRS[@]}"; do mkdir -p "$dir" done +# Create data directories, tolerating failures on external mounts +_failed_mkdir=() +_failed_chown=() +for dir in "${DATA_DIRS[@]}"; do + _mkdir_err=$(mkdir -p "$dir" 2>&1) || _failed_mkdir+=("$dir ($_mkdir_err)") +done + # Ensure /app itself is owned by PUID:PGID (needed for uwsgi socket creation) if [ "$(id -u)" = "0" ] && [ -d "/app" ]; then if [ "$(stat -c '%u:%g' /app)" != "$PUID:$PGID" ]; then @@ -50,11 +61,15 @@ fi # NOTE: mac doesn't run as root, so only manage permissions # if this script is running as root if [ "$(id -u)" = "0" ]; then - # Fix data directories (non-recursive to avoid touching user files) + # Fix data directories (non-recursive to avoid touching user files). + # Failures are collected rather than fatal — directories may be on + # external mounts (NFS, SMB/CIFS, FUSE) that reject chown. for dir in "${DATA_DIRS[@]}"; do - if [ -d "$dir" ] && [ "$(stat -c '%u:%g' "$dir")" != "$PUID:$PGID" ]; then - echo "Fixing ownership for $dir" - chown "$PUID:$PGID" "$dir" + if [ -d "$dir" ] && [ "$(stat -c '%u:%g' "$dir" 2>/dev/null)" != "$PUID:$PGID" ]; then + _chown_err=$(chown "$PUID:$PGID" "$dir" 2>&1) || { + _current_owner=$(stat -c '%u:%g' "$dir" 2>/dev/null || echo "unknown") + _failed_chown+=("$dir (current: $_current_owner, error: $_chown_err)") + } fi done @@ -70,11 +85,46 @@ if [ "$(id -u)" = "0" ]; then # No secondary check needed here — duplicating it could chown without updating # the sentinel, creating inconsistent state. - # Fix /data directory ownership (non-recursive) - if [ -d "/data" ] && [ "$(stat -c '%u:%g' /data)" != "$PUID:$PGID" ]; then - echo "Fixing ownership for /data (non-recursive)" - chown "$PUID:$PGID" /data + # Fix /data directory ownership (non-recursive). + # Tolerates failure for the same external-mount reasons as DATA_DIRS. + if [ -d "/data" ] && [ "$(stat -c '%u:%g' /data 2>/dev/null)" != "$PUID:$PGID" ]; then + _chown_err=$(chown "$PUID:$PGID" /data 2>&1) || { + _current_owner=$(stat -c '%u:%g' /data 2>/dev/null || echo "unknown") + _failed_chown+=("/data (current: $_current_owner, error: $_chown_err)") + } fi - chmod +x /data + chmod +x /data 2>/dev/null || true +fi + +# Consolidated warning for all mkdir/chown failures. +# Emitted outside the root guard so non-root mkdir failures are also reported. +if [ ${#_failed_mkdir[@]} -gt 0 ] || [ ${#_failed_chown[@]} -gt 0 ]; then + echo "" + echo "================================================================" + echo "WARNING: Some data directories could not be created or updated." + echo " This typically occurs with NFS, SMB/CIFS, or other external" + echo " mounts that restrict ownership changes." + echo "" + if [ ${#_failed_mkdir[@]} -gt 0 ]; then + echo " Could not create:" + for entry in "${_failed_mkdir[@]}"; do + echo " - $entry" + done + echo "" + fi + if [ ${#_failed_chown[@]} -gt 0 ]; then + echo " Could not set ownership to $PUID:$PGID:" + for entry in "${_failed_chown[@]}"; do + echo " - $entry" + done + echo "" + fi + echo " To fix, either:" + echo " 1. Set PUID/PGID to match your mount's owner" + echo " 2. Fix ownership on the host/NAS:" + echo " sudo chown $PUID:$PGID " + echo " 3. For SMB/CIFS: set uid=$PUID,gid=$PGID in mount options" + echo "================================================================" + echo "" fi