The prebuilt @tailwindcss/oxide and lightningcss .node bindings expect a glibc-compat runtime. On bare Alpine musl they fail to load during the install-time probe, which cascades into `npm ci` exiting 1 in the Portainer build. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
37 lines
1.5 KiB
Docker
37 lines
1.5 KiB
Docker
# syntax=docker/dockerfile:1.7
|
|
# Multi-stage build: Node builds the Vite SPA, Nginx serves the static output.
|
|
# Pinned tags only — no :latest, no floating minors.
|
|
|
|
# ── 1. Build ───────────────────────────────────────────────────────────────
|
|
FROM node:24.15.0-alpine3.23 AS build
|
|
WORKDIR /app
|
|
|
|
# libc6-compat: the prebuilt @tailwindcss/oxide and lightningcss .node bindings
|
|
# are linked against a glibc-compatible runtime and fail to load on bare Alpine
|
|
# musl otherwise — which kills `npm ci` during its postinstall probe.
|
|
RUN apk add --no-cache libc6-compat
|
|
|
|
# Copy lockfile first so `npm ci` layer caches when only source changes.
|
|
COPY package.json package-lock.json ./
|
|
RUN npm ci --no-audit --no-fund
|
|
|
|
COPY . .
|
|
RUN npm run build
|
|
|
|
|
|
# ── 2. Serve ───────────────────────────────────────────────────────────────
|
|
FROM nginx:1.27.3-alpine AS serve
|
|
|
|
# Strip the default site; our config owns /etc/nginx/conf.d/default.conf.
|
|
RUN rm /etc/nginx/conf.d/default.conf
|
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
|
|
|
# Drop the built SPA into the document root.
|
|
COPY --from=build /app/dist /usr/share/nginx/html
|
|
|
|
EXPOSE 80
|
|
|
|
# Alpine ships busybox wget — avoids pulling curl just for healthchecks.
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
CMD wget -q -O- http://127.0.0.1/health || exit 1
|