Merge branch 'main' into ebook-piecewise

This commit is contained in:
kikootwo
2026-02-03 01:32:03 -05:00
6 changed files with 123 additions and 23 deletions
+42 -5
View File
@@ -1,12 +1,33 @@
#!/bin/bash
# App startup wrapper for unified container
# Starts Next.js server and initializes services
# Uses gosu to ensure correct PUID:PGID for file operations
set -e
# Load environment from /etc/environment (set by entrypoint)
if [ -f /etc/environment ]; then
set -a
source /etc/environment
set +a
fi
# Get PUID/PGID (default to node user's current IDs if not set)
PUID=${PUID:-$(id -u node)}
PGID=${PGID:-$(id -g node)}
echo "[App] Starting Next.js server..."
echo "[App] Process will run as UID:GID = $PUID:$PGID"
cd /app
# Start server in background
node server.js &
# Use gosu to switch to correct UID:GID and start server
# This bypasses username resolution issues when PUID collides with existing users
if [ "$(id -u)" = "0" ]; then
# Running as root - use gosu to switch to PUID:PGID
echo "[App] Switching to UID:GID $PUID:$PGID via gosu..."
# Start server in background with gosu
gosu "$PUID:$PGID" node server.js &
SERVER_PID=$!
echo "[App] Waiting for server to be ready..."
@@ -14,9 +35,25 @@ sleep 5
# Initialize application services (creates default scheduled jobs)
echo "[App] Initializing application services..."
curl -f http://localhost:3030/api/init || echo "[App] ⚠️ Warning: Failed to initialize services"
curl -sf http://localhost:3030/api/init || echo "[App] Warning: Failed to initialize services (may already be initialized)"
echo "[App] Server ready with PID $SERVER_PID"
echo "[App] Server ready with PID $SERVER_PID (running as $PUID:$PGID)"
# Verify the process is running with correct UID:GID
if [ -f "/proc/$SERVER_PID/status" ]; then
ACTUAL_UID=$(grep '^Uid:' /proc/$SERVER_PID/status | awk '{print $2}')
ACTUAL_GID=$(grep '^Gid:' /proc/$SERVER_PID/status | awk '{print $2}')
echo "[App] Verified process credentials: UID=$ACTUAL_UID GID=$ACTUAL_GID"
if [ "$ACTUAL_UID" != "$PUID" ] || [ "$ACTUAL_GID" != "$PGID" ]; then
echo "[App] WARNING: Process UID:GID ($ACTUAL_UID:$ACTUAL_GID) does not match expected ($PUID:$PGID)"
fi
fi
# Wait for server process
wait $SERVER_PID
else
# Not running as root - just run directly (fallback)
echo "[App] Warning: Not running as root, cannot use gosu. Running as current user."
exec node server.js
fi
+19 -7
View File
@@ -4,12 +4,16 @@ set -e
echo "🚀 ReadMeABook Unified Container Starting..."
# ============================================================================
# PUID/PGID USER REMAPPING (Hybrid approach)
# PUID/PGID USER REMAPPING (Hybrid approach with gosu)
# ============================================================================
# Hybrid approach to support user file ownership while maintaining PostgreSQL compatibility:
# - postgres user: Keep UID 103 (required by PostgreSQL), remap GID → PGID
# - redis user: Remap UID → PUID, GID → PGID
# - node user: Remap UID → PUID, GID → PGID
# - redis user: Remap UID → PUID, GID → PGID (also uses gosu at runtime)
# - node user: Remap UID → PUID, GID → PGID (also uses gosu at runtime)
#
# NOTE: We use gosu in app-start.sh and redis-start.sh to ensure the process
# actually runs with the correct UID:GID. This fixes issues where PUID collides
# with existing system users (e.g., PUID=65534 collides with 'nobody').
#
# Result:
# - PostgreSQL data (103:PGID) - postgres user with shared group
@@ -309,7 +313,8 @@ export NODE_ENV="production"
export PORT="3030"
export HOSTNAME="0.0.0.0"
# Persist environment variables for supervisord
# Persist environment variables for supervisord and child processes
# PUID/PGID are critical for gosu-based user switching in app-start.sh and redis-start.sh
cat > /etc/environment <<EOF
DATABASE_URL=$DATABASE_URL
REDIS_URL=$REDIS_URL
@@ -322,6 +327,8 @@ LOG_LEVEL=$LOG_LEVEL
NODE_ENV=$NODE_ENV
PORT=$PORT
HOSTNAME=$HOSTNAME
PUID=${PUID:-}
PGID=${PGID:-}
EOF
echo "✅ Environment configured"
@@ -353,9 +360,14 @@ if [ "$POSTGRES_PASSWORD" = "$(generate_secret)" ]; then
fi
echo ""
echo "📊 Services starting:"
echo " - PostgreSQL (internal)"
echo " - Redis (internal)"
echo " - Next.js App (port 3030)"
echo " - PostgreSQL (internal, user=postgres)"
echo " - Redis (internal, UID:GID=${PUID:-102}:${PGID:-102})"
echo " - Next.js App (port 3030, UID:GID=${PUID:-1000}:${PGID:-1000})"
if [ -n "$PUID" ] && [ -n "$PGID" ]; then
echo ""
echo "🔐 Using gosu for reliable UID:GID switching"
echo " App and Redis will run as $PUID:$PGID"
fi
echo "============================================"
echo ""
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
# Redis startup wrapper for unified container
# Uses gosu to ensure correct PUID:PGID for file operations
set -e
# Load environment from /etc/environment (set by entrypoint)
if [ -f /etc/environment ]; then
set -a
source /etc/environment
set +a
fi
# Get PUID/PGID (default to redis user's current IDs if not set)
PUID=${PUID:-$(id -u redis)}
PGID=${PGID:-$(id -g redis)}
echo "[Redis] Starting Redis server..."
echo "[Redis] Process will run as UID:GID = $PUID:$PGID"
# Use gosu to switch to correct UID:GID and start redis
# This bypasses username resolution issues when PUID collides with existing users
if [ "$(id -u)" = "0" ]; then
# Running as root - use gosu to switch to PUID:PGID
echo "[Redis] Switching to UID:GID $PUID:$PGID via gosu..."
exec gosu "$PUID:$PGID" /usr/bin/redis-server --appendonly yes --dir /var/lib/redis --bind 127.0.0.1 --port 6379
else
# Not running as root - just run directly (fallback)
echo "[Redis] Warning: Not running as root, cannot use gosu. Running as current user."
exec /usr/bin/redis-server --appendonly yes --dir /var/lib/redis --bind 127.0.0.1 --port 6379
fi
+3 -3
View File
@@ -20,8 +20,8 @@ stdout_events_enabled=true
stderr_events_enabled=true
[program:redis]
command=/usr/bin/redis-server --appendonly yes --dir /var/lib/redis --bind 127.0.0.1 --port 6379
user=redis
command=/app/redis-start.sh
user=root
autostart=true
autorestart=true
priority=20
@@ -35,7 +35,7 @@ stderr_events_enabled=true
[program:app]
command=/app/app-start.sh
directory=/app
user=node
user=root
autostart=true
autorestart=true
priority=30
+7 -1
View File
@@ -15,7 +15,7 @@ RUN apt-get update && apt-get install -y curl gnupg && \
curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /etc/apt/keyrings/postgresql.gpg && \
echo "deb [signed-by=/etc/apt/keyrings/postgresql.gpg] http://apt.postgresql.org/pub/repos/apt bookworm-pgdg main" > /etc/apt/sources.list.d/postgresql.list
# Install PostgreSQL, Redis, and supervisord
# Install PostgreSQL, Redis, supervisord, and gosu (for reliable user switching)
RUN apt-get update && apt-get install -y \
postgresql-16 \
postgresql-client-16 \
@@ -25,6 +25,7 @@ RUN apt-get update && apt-get install -y \
openssl \
ffmpeg \
locales \
gosu \
&& sed -i 's/^# \(en_US.UTF-8 UTF-8\)/\1/' /etc/locale.gen \
&& locale-gen en_US.UTF-8 \
&& update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 \
@@ -106,6 +107,11 @@ COPY --chown=root:root docker/unified/app-start.sh /app/app-start.sh
# Convert line endings and make executable
RUN sed -i 's/\r$//' /app/app-start.sh && chmod +x /app/app-start.sh
# Copy redis startup wrapper
COPY --chown=root:root docker/unified/redis-start.sh /app/redis-start.sh
# Convert line endings and make executable
RUN sed -i 's/\r$//' /app/redis-start.sh && chmod +x /app/redis-start.sh
# Expose app port
EXPOSE 3030
+14
View File
@@ -691,6 +691,20 @@ docker volume rm readmeabook-pgdata readmeabook-redis readmeabook-cache
- Let Docker create directories on first run (they'll have correct ownership)
- Note: Works fine on WSL2 when using Docker volumes or letting container create directories
**19. PUID collision causes wrong GID (EACCES: permission denied, mkdir)**
- Issue: File organization fails with `EACCES: permission denied, mkdir '/media/...'` even though PUID/PGID are set correctly
- Symptoms: `docker exec -u $PUID:$PGID container mkdir /media/test` works, but the app fails
- Root cause: When PUID collides with an existing system user (e.g., PUID=65534 collides with `nobody`), the `usermod` command creates two users with the same UID. When supervisord resolves `user=node` from /etc/passwd, it may resolve to the wrong user's GID
- Example: User sets PUID=65534 (nobody), PGID=321601206 (AD group). App runs with UID=65534 but GID=65534 (nogroup) instead of GID=321601206
- Diagnosis: Run `docker exec container ps ax -o user,pid,group,gid,comm` - if GID column shows wrong value, this is the issue
- Fix: Use `gosu` for reliable UID:GID switching that bypasses username resolution
- Added `gosu` package to Dockerfile
- Created `app-start.sh` and `redis-start.sh` wrapper scripts that use `gosu $PUID:$PGID` to switch users
- Supervisord now starts these wrappers as root, and gosu switches to the exact UID:GID
- PUID/PGID are passed via /etc/environment to the wrapper scripts
- Verification: After fix, `ps ax -o user,pid,group,gid,comm` will show correct GID for app and redis processes
- Note: This primarily affects users with complex setups (NFS mounts, AD/SSSD groups, PUID=65534)
## Related
- [Multi-container deployment](docker.md)