Fixed SQLITE_READONLY errors by implementing proper volume permission handling in Docker container. The issue occurred because Docker volumes are mounted with root ownership, preventing the non-root nuxt user from writing to the database. Solution: - Added docker-entrypoint.sh script to handle permission fixes - Container starts as root to fix /app/data permissions - Uses su-exec to drop privileges to nuxt user after fixing permissions - Maintains security by running application as non-root user This allows the SQLite database to be properly created and modified while keeping the container secure with non-root execution. Fixes: - "attempt to write a readonly database" errors - Session cleanup failures - Rate limit cleanup failures - Password reset cleanup failures - Admin credential generation on first launch 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
98 lines
2.8 KiB
Docker
98 lines
2.8 KiB
Docker
# ======================
|
|
# Stage 1: Dependencies
|
|
# ======================
|
|
FROM node:20-alpine AS deps
|
|
|
|
WORKDIR /app
|
|
|
|
# Copy package files
|
|
COPY package*.json ./
|
|
|
|
# Install dependencies (including devDependencies for build)
|
|
# Use npm ci if package-lock.json exists, otherwise use npm install
|
|
RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
|
|
|
|
# ======================
|
|
# Stage 2: Builder
|
|
# ======================
|
|
FROM node:20-alpine AS builder
|
|
|
|
WORKDIR /app
|
|
|
|
# Copy dependencies from deps stage
|
|
COPY --from=deps /app/node_modules ./node_modules
|
|
|
|
# Copy application source
|
|
COPY . .
|
|
|
|
# Accept build arguments for Nuxt build-time configuration
|
|
ARG SITE_URL=https://church.example.com
|
|
ARG EMAIL_HOST=smtp.example.com
|
|
ARG EMAIL_PORT=587
|
|
ARG EMAIL_USER=noreply@example.com
|
|
ARG EMAIL_PASSWORD=your-email-password
|
|
ARG EMAIL_FROM=New Life Christian Church <noreply@example.com>
|
|
|
|
# Set environment variables for build
|
|
ENV SITE_URL=$SITE_URL
|
|
ENV EMAIL_HOST=$EMAIL_HOST
|
|
ENV EMAIL_PORT=$EMAIL_PORT
|
|
ENV EMAIL_USER=$EMAIL_USER
|
|
ENV EMAIL_PASSWORD=$EMAIL_PASSWORD
|
|
ENV EMAIL_FROM=$EMAIL_FROM
|
|
|
|
# Build the application
|
|
RUN npm run build
|
|
|
|
# Remove development dependencies after build
|
|
RUN npm prune --production
|
|
|
|
# ======================
|
|
# Stage 3: Runtime
|
|
# ======================
|
|
FROM node:20-alpine AS runtime
|
|
|
|
WORKDIR /app
|
|
|
|
# Install dumb-init and su-exec for proper signal handling and user switching
|
|
RUN apk add --no-cache dumb-init su-exec
|
|
|
|
# Create non-root user for security
|
|
RUN addgroup -g 1001 -S nodejs && \
|
|
adduser -S nuxt -u 1001
|
|
|
|
# Copy only production dependencies and built output
|
|
COPY --from=builder --chown=nuxt:nodejs /app/.output ./.output
|
|
COPY --from=builder --chown=nuxt:nodejs /app/node_modules ./node_modules
|
|
COPY --from=builder --chown=nuxt:nodejs /app/package.json ./package.json
|
|
|
|
# Copy entrypoint script
|
|
COPY docker-entrypoint.sh /usr/local/bin/
|
|
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
|
|
|
# Create data directory for SQLite database with proper permissions
|
|
RUN mkdir -p /app/data && chown -R nuxt:nodejs /app/data
|
|
|
|
# Don't switch to non-root user yet - entrypoint will handle it
|
|
# This allows the entrypoint to fix volume permissions first
|
|
|
|
# Expose port
|
|
EXPOSE 3000
|
|
|
|
# Set runtime environment variables
|
|
ENV NODE_ENV=production
|
|
ENV NUXT_HOST=0.0.0.0
|
|
ENV NUXT_PORT=3000
|
|
|
|
# Security: AUTH_SECRET and admin credentials are auto-generated on first launch
|
|
# They are stored in the database and logged once to container logs
|
|
# Use: docker logs <container-name> | grep "ADMIN CREDENTIALS" to retrieve them
|
|
|
|
# Health check
|
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
|
|
CMD node -e "require('http').get('http://localhost:3000/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})" || exit 1
|
|
|
|
# Use custom entrypoint to handle permissions and user switching
|
|
ENTRYPOINT ["dumb-init", "--", "docker-entrypoint.sh"]
|
|
CMD ["node", ".output/server/index.mjs"]
|