Merge pull request #519 from the-luap/fix/install-permissions-484
fix(install): self-chowning entrypoint kills fresh-install restart loop (#484)
This commit is contained in:
+9
-4
@@ -35,12 +35,15 @@ RUN apk upgrade --no-cache
|
||||
RUN npm install -g npm@10
|
||||
|
||||
# Install dumb-init for proper signal handling, postgresql-client for database
|
||||
# checks, and ffmpeg for video upload support. Alpine's ffmpeg package ships
|
||||
# both `ffmpeg` and `ffprobe` built natively against musl libc — the npm
|
||||
# checks, ffmpeg for video upload support, and su-exec for the root → nodejs
|
||||
# privilege drop in wait-for-db.sh (see #484: container starts as root so it
|
||||
# can chown bind-mounted host volumes to UID 1001, then re-execs as nodejs
|
||||
# before running the app). Alpine's ffmpeg package ships both `ffmpeg` and
|
||||
# `ffprobe` built natively against musl libc — the npm
|
||||
# `@ffmpeg-installer/ffmpeg` binary is glibc-built and (a) doesn't reliably
|
||||
# run on Alpine and (b) only includes ffmpeg, not ffprobe (which the video
|
||||
# pipeline calls via fluent-ffmpeg.ffprobe()).
|
||||
RUN apk add --no-cache dumb-init postgresql-client ffmpeg
|
||||
RUN apk add --no-cache dumb-init postgresql-client ffmpeg su-exec
|
||||
|
||||
# Create non-root user
|
||||
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
|
||||
@@ -56,7 +59,9 @@ RUN chmod -R a+r /app && chmod +x wait-for-db.sh
|
||||
RUN mkdir -p storage/events/active storage/events/archived storage/thumbnails data logs && \
|
||||
chown -R nodejs:nodejs storage data logs
|
||||
|
||||
USER nodejs
|
||||
# No USER directive — the container starts as root so wait-for-db.sh can
|
||||
# chown bind-mounted host directories to UID 1001 before dropping privs
|
||||
# via su-exec. See #484 for the fresh-install restart loop this avoids.
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/bin/sh
|
||||
# init-production.sh - Production initialization script
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 Initializing PicPeak Production Environment..."
|
||||
|
||||
# Wait for services to be ready
|
||||
echo "⏳ Waiting for database to be fully ready..."
|
||||
sleep 3
|
||||
|
||||
# Fix permissions if running as root (shouldn't happen with proper Dockerfile)
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
echo "🔧 Fixing file permissions..."
|
||||
chown -R nodejs:nodejs /app/storage /app/data /app/logs 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Create required directories
|
||||
echo "📁 Creating required directories..."
|
||||
mkdir -p /app/storage/events/active \
|
||||
/app/storage/events/archived \
|
||||
/app/storage/thumbnails \
|
||||
/app/storage/uploads/logos \
|
||||
/app/storage/uploads/favicons \
|
||||
/app/data \
|
||||
/app/logs
|
||||
|
||||
# Run migrations with safe runner
|
||||
echo "🗄️ Running database migrations (safe mode)..."
|
||||
NODE_ENV=production npm run migrate:safe
|
||||
|
||||
# Create admin user if environment variables are set
|
||||
if [ -n "$ADMIN_EMAIL" ] && [ -n "$ADMIN_PASSWORD" ]; then
|
||||
echo "👤 Creating admin user..."
|
||||
node scripts/create-admin.js \
|
||||
--email "$ADMIN_EMAIL" \
|
||||
--username "${ADMIN_USERNAME:-admin}" \
|
||||
--password "$ADMIN_PASSWORD" || echo "Admin user might already exist"
|
||||
fi
|
||||
|
||||
# Initialize email configuration if variables are set
|
||||
if [ -n "$SMTP_HOST" ]; then
|
||||
echo "📧 Email configuration detected via environment variables"
|
||||
fi
|
||||
|
||||
echo "✅ Production initialization complete!"
|
||||
echo "🌐 Starting application server..."
|
||||
|
||||
# Start the application
|
||||
exec node server.js
|
||||
@@ -3,6 +3,43 @@
|
||||
|
||||
set -e
|
||||
|
||||
# Permission handling (#484): the image starts as root so this script can
|
||||
# chown bind-mounted host volumes to UID 1001 (nodejs) before dropping
|
||||
# privileges via su-exec. This avoids the fresh-install restart loop where
|
||||
# the host directory's UID (commonly 1000) didn't match the container's
|
||||
# hard-coded nodejs user. Compose deployments that pin `user:` to something
|
||||
# other than root skip this branch — they own permissions themselves and hit
|
||||
# the preflight check below instead.
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
if ! chown -R nodejs:nodejs /app/storage /app/data /app/logs 2>/dev/null; then
|
||||
echo "ERROR: failed to chown /app/storage, /app/data, /app/logs to nodejs (UID 1001)." >&2
|
||||
echo " This usually means the host filesystem rejects chown (e.g. NFS without root squash" >&2
|
||||
echo " disabled, or a SELinux/AppArmor policy blocking the operation)." >&2
|
||||
echo " Workaround: pre-chown the host directories to 1001:1001 and pin 'user: \"1001:1001\"'" >&2
|
||||
echo " in your compose file so this script never tries to chown them itself." >&2
|
||||
echo " See https://docs.picpeak.app/deployment/docker#permissions" >&2
|
||||
exit 1
|
||||
fi
|
||||
exec su-exec nodejs:nodejs "$0" "$@"
|
||||
fi
|
||||
|
||||
# Belt-and-suspenders: if we got here as non-root (compose `user:` override),
|
||||
# verify the bind mounts are actually writable before proceeding. Failing
|
||||
# loud here beats the previous behavior — silent mkdir-||-true at line 69
|
||||
# followed by a confusing migration error and a restart loop.
|
||||
_uid="$(id -u)"
|
||||
_gid="$(id -g)"
|
||||
for _dir in /app/storage /app/data /app/logs; do
|
||||
if [ ! -w "$_dir" ]; then
|
||||
echo "ERROR: $_dir is not writable by UID $_uid." >&2
|
||||
echo " Either drop the 'user:' override from your compose file so the container starts as" >&2
|
||||
echo " root and can self-fix permissions, or run on the host:" >&2
|
||||
echo " chown -R $_uid:$_gid <host-mount-for-$_dir>" >&2
|
||||
echo " See https://docs.picpeak.app/deployment/docker#permissions" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
host="${DB_HOST:-postgres}"
|
||||
port="${DB_PORT:-5432}"
|
||||
user="${DB_USER:-picpeak}"
|
||||
|
||||
Reference in New Issue
Block a user