Files
my-protfolio/Dockerfile

94 lines
3.9 KiB
Docker

# syntax=docker/dockerfile:1.7
FROM node:22-alpine AS image-runtime
# Distribution libraries support baseline CPUs and dispatch optional SIMD at runtime.
RUN apk add --no-cache glib expat libjpeg-turbo libpng libwebp tiff lcms2 librsvg libstdc++
FROM image-runtime AS image-build
RUN apk add --no-cache python3 make g++ meson ninja pkgconf xz \
glib-dev expat-dev libjpeg-turbo-dev libpng-dev libwebp-dev tiff-dev lcms2-dev librsvg-dev
ADD --checksum=sha256:3c41e1d5458081bfa4a5bc54e116c46259c75c6760a18027764555632b9dda3e https://github.com/libvips/libvips/releases/download/v8.18.6/vips-8.18.6.tar.xz /tmp/vips.tar.xz
RUN if [ "$(uname -m)" = x86_64 ]; then export CFLAGS='-O2 -march=x86-64 -mtune=generic' CXXFLAGS='-O2 -march=x86-64 -mtune=generic'; fi \
&& mkdir /tmp/vips && tar -xJf /tmp/vips.tar.xz -C /tmp/vips --strip-components=1 \
&& meson setup /tmp/vips/build /tmp/vips --prefix=/opt/vips --libdir=lib \
--buildtype=release --auto-features=disabled \
-Dhighway=disabled -Dorc=disabled -Dcplusplus=true -Dexamples=false \
-Djpeg=enabled -Dpng=enabled -Dwebp=enabled -Dtiff=enabled -Dlcms=enabled -Drsvg=enabled -Dzlib=enabled \
&& meson compile -C /tmp/vips/build -j 2 \
&& meson install -C /tmp/vips/build
FROM image-build AS dependencies
WORKDIR /app
ARG NPM_REGISTRY=https://registry.npmjs.org/
ENV NEXT_TELEMETRY_DISABLED=1 \
PKG_CONFIG_PATH=/opt/vips/lib/pkgconfig \
LD_LIBRARY_PATH=/opt/vips/lib \
npm_config_nodedir=/usr/local
COPY package.json package-lock.json ./
RUN --mount=type=cache,id=portfolio-npm-node22,target=/root/.npm,sharing=locked \
npm ci \
--registry="${NPM_REGISTRY}" \
--replace-registry-host=registry.npmjs.org \
--include=optional
# Compile Sharp against our baseline libvips, not its x86-64-v2 prebuilt binary.
RUN if [ "$(uname -m)" = x86_64 ]; then export CFLAGS='-O2 -march=x86-64 -mtune=generic' CXXFLAGS='-O2 -march=x86-64 -mtune=generic'; fi \
&& npm explore sharp -- npm run build
FROM dependencies AS build
# Only application inputs invalidate compilation. Tests and docs stay outside.
COPY next.config.mjs tsconfig.json next-env.d.ts ./
COPY src ./src
COPY public ./public
COPY migrations ./migrations
COPY scripts/maintenance.ts scripts/build-maintenance.mjs scripts/prepare-standalone.mjs ./scripts/
RUN --mount=type=cache,id=portfolio-next-node22,target=/app/.next/cache,sharing=locked \
npm run build
# Carry the exact shared-library dependency closure from the working build stage.
# Development packages can provide libraries that are absent from image-runtime.
FROM dependencies AS native-runtime-libraries
RUN mkdir -p /runtime-libs \
&& (ldd /app/node_modules/sharp/src/build/Release/*.node /opt/vips/lib/libvips*.so* 2>/dev/null || true) \
| awk '/=> \// { print $3 } /^[[:space:]]*\// { print $1 }' \
| sort -u \
| while IFS= read -r library; do \
mkdir -p "/runtime-libs$(dirname "$library")"; \
cp -L "$library" "/runtime-libs$library"; \
done
FROM image-runtime AS runtime
WORKDIR /app
ENV NODE_ENV=production \
NEXT_TELEMETRY_DISABLED=1 \
HOSTNAME=0.0.0.0 \
PORT=3000 \
DATA_DIR=/data \
LD_LIBRARY_PATH=/opt/vips/lib
RUN addgroup --system --gid 1001 nodejs \
&& adduser --system --uid 1001 nextjs
COPY --from=build --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=image-build /opt/vips/lib /opt/vips/lib
# Preserve the package and its native dependency closure beyond Next's tracing.
COPY --from=dependencies --chown=nextjs:nodejs /app/node_modules/sharp/ ./node_modules/sharp/
COPY --from=native-runtime-libraries /runtime-libs/ /
RUN mkdir -p .next/cache /data && chown -R nextjs:nodejs .next/cache /data
USER nextjs
EXPOSE 3000
HEALTHCHECK --interval=2s --timeout=2s --start-period=3s --retries=30 \
CMD ["node", "-e", "fetch('http://127.0.0.1:3000/api/health').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"]
CMD ["node", "server.js"]