# ── Stage 1: install production deps ───────────────────────────────────── FROM node:22-alpine AS deps WORKDIR /app/backend COPY backend/package.json backend/package-lock.json ./ RUN npm ci --omit=dev # ── Stage 2: runtime ───────────────────────────────────────────────────── FROM node:22-alpine LABEL maintainer="LearnSpace" RUN apk add --no-cache sqlite tini WORKDIR /app COPY --from=deps /app/backend/node_modules ./backend/node_modules COPY backend/package.json ./backend/ COPY backend/src ./backend/src COPY backend/seed.js ./backend/seed.js COPY frontend ./frontend COPY js ./js COPY docker-entrypoint.sh ./docker-entrypoint.sh # Ensure data & uploads dirs exist (volumes mount here); normalize entrypoint EOL + make executable RUN mkdir -p /app/backend/data /app/backend/uploads /app/backups \ && sed -i 's/\r$//' /app/docker-entrypoint.sh \ && chmod +x /app/docker-entrypoint.sh ENV NODE_ENV=production ENV PORT=3000 EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD wget -qO- http://localhost:3000/api/health || exit 1 # Entrypoint применяет миграции + засев прав, затем запускает сервер (tini — PID 1, сигналы/зомби) ENTRYPOINT ["tini", "--", "/app/docker-entrypoint.sh"]