mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-03 12:50:09 +00:00
Merge branch 'main' into ebook-piecewise
This commit is contained in:
+49
-12
@@ -1,22 +1,59 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# App startup wrapper for unified container
|
# 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] Starting Next.js server..."
|
||||||
|
echo "[App] Process will run as UID:GID = $PUID:$PGID"
|
||||||
|
|
||||||
cd /app
|
cd /app
|
||||||
|
|
||||||
# Start server in background
|
# Use gosu to switch to correct UID:GID and start server
|
||||||
node server.js &
|
# This bypasses username resolution issues when PUID collides with existing users
|
||||||
SERVER_PID=$!
|
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..."
|
||||||
|
|
||||||
echo "[App] Waiting for server to be ready..."
|
# Start server in background with gosu
|
||||||
sleep 5
|
gosu "$PUID:$PGID" node server.js &
|
||||||
|
SERVER_PID=$!
|
||||||
|
|
||||||
# Initialize application services (creates default scheduled jobs)
|
echo "[App] Waiting for server to be ready..."
|
||||||
echo "[App] Initializing application services..."
|
sleep 5
|
||||||
curl -f http://localhost:3030/api/init || echo "[App] ⚠️ Warning: Failed to initialize services"
|
|
||||||
|
|
||||||
echo "[App] Server ready with PID $SERVER_PID"
|
# Initialize application services (creates default scheduled jobs)
|
||||||
|
echo "[App] Initializing application services..."
|
||||||
|
curl -sf http://localhost:3030/api/init || echo "[App] Warning: Failed to initialize services (may already be initialized)"
|
||||||
|
|
||||||
# Wait for server process
|
echo "[App] Server ready with PID $SERVER_PID (running as $PUID:$PGID)"
|
||||||
wait $SERVER_PID
|
|
||||||
|
# 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
|
||||||
|
|||||||
@@ -4,12 +4,16 @@ set -e
|
|||||||
echo "🚀 ReadMeABook Unified Container Starting..."
|
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:
|
# Hybrid approach to support user file ownership while maintaining PostgreSQL compatibility:
|
||||||
# - postgres user: Keep UID 103 (required by PostgreSQL), remap GID → PGID
|
# - postgres user: Keep UID 103 (required by PostgreSQL), remap GID → PGID
|
||||||
# - redis 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
|
# - 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:
|
# Result:
|
||||||
# - PostgreSQL data (103:PGID) - postgres user with shared group
|
# - PostgreSQL data (103:PGID) - postgres user with shared group
|
||||||
@@ -309,7 +313,8 @@ export NODE_ENV="production"
|
|||||||
export PORT="3030"
|
export PORT="3030"
|
||||||
export HOSTNAME="0.0.0.0"
|
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
|
cat > /etc/environment <<EOF
|
||||||
DATABASE_URL=$DATABASE_URL
|
DATABASE_URL=$DATABASE_URL
|
||||||
REDIS_URL=$REDIS_URL
|
REDIS_URL=$REDIS_URL
|
||||||
@@ -322,6 +327,8 @@ LOG_LEVEL=$LOG_LEVEL
|
|||||||
NODE_ENV=$NODE_ENV
|
NODE_ENV=$NODE_ENV
|
||||||
PORT=$PORT
|
PORT=$PORT
|
||||||
HOSTNAME=$HOSTNAME
|
HOSTNAME=$HOSTNAME
|
||||||
|
PUID=${PUID:-}
|
||||||
|
PGID=${PGID:-}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "✅ Environment configured"
|
echo "✅ Environment configured"
|
||||||
@@ -353,9 +360,14 @@ if [ "$POSTGRES_PASSWORD" = "$(generate_secret)" ]; then
|
|||||||
fi
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
echo "📊 Services starting:"
|
echo "📊 Services starting:"
|
||||||
echo " - PostgreSQL (internal)"
|
echo " - PostgreSQL (internal, user=postgres)"
|
||||||
echo " - Redis (internal)"
|
echo " - Redis (internal, UID:GID=${PUID:-102}:${PGID:-102})"
|
||||||
echo " - Next.js App (port 3030)"
|
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 "============================================"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -20,8 +20,8 @@ stdout_events_enabled=true
|
|||||||
stderr_events_enabled=true
|
stderr_events_enabled=true
|
||||||
|
|
||||||
[program:redis]
|
[program:redis]
|
||||||
command=/usr/bin/redis-server --appendonly yes --dir /var/lib/redis --bind 127.0.0.1 --port 6379
|
command=/app/redis-start.sh
|
||||||
user=redis
|
user=root
|
||||||
autostart=true
|
autostart=true
|
||||||
autorestart=true
|
autorestart=true
|
||||||
priority=20
|
priority=20
|
||||||
@@ -35,7 +35,7 @@ stderr_events_enabled=true
|
|||||||
[program:app]
|
[program:app]
|
||||||
command=/app/app-start.sh
|
command=/app/app-start.sh
|
||||||
directory=/app
|
directory=/app
|
||||||
user=node
|
user=root
|
||||||
autostart=true
|
autostart=true
|
||||||
autorestart=true
|
autorestart=true
|
||||||
priority=30
|
priority=30
|
||||||
|
|||||||
+7
-1
@@ -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 && \
|
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
|
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 \
|
RUN apt-get update && apt-get install -y \
|
||||||
postgresql-16 \
|
postgresql-16 \
|
||||||
postgresql-client-16 \
|
postgresql-client-16 \
|
||||||
@@ -25,6 +25,7 @@ RUN apt-get update && apt-get install -y \
|
|||||||
openssl \
|
openssl \
|
||||||
ffmpeg \
|
ffmpeg \
|
||||||
locales \
|
locales \
|
||||||
|
gosu \
|
||||||
&& sed -i 's/^# \(en_US.UTF-8 UTF-8\)/\1/' /etc/locale.gen \
|
&& sed -i 's/^# \(en_US.UTF-8 UTF-8\)/\1/' /etc/locale.gen \
|
||||||
&& locale-gen en_US.UTF-8 \
|
&& locale-gen en_US.UTF-8 \
|
||||||
&& update-locale LANG=en_US.UTF-8 LC_ALL=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
|
# Convert line endings and make executable
|
||||||
RUN sed -i 's/\r$//' /app/app-start.sh && chmod +x /app/app-start.sh
|
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 app port
|
||||||
EXPOSE 3030
|
EXPOSE 3030
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
- 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
|
- 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
|
## Related
|
||||||
|
|
||||||
- [Multi-container deployment](docker.md)
|
- [Multi-container deployment](docker.md)
|
||||||
|
|||||||
Reference in New Issue
Block a user