Files
2026-09-11 02:46:56 +02:00

70 lines
2.7 KiB
Docker

# syntax=docker/dockerfile:1
###############################################################################
# SHARP-ER web - React Router 8 (framework mode, SSR) on Node 22
#
# Adapted from the React Router template Dockerfile. Two things changed:
# * npm -> pnpm, because this repository is a pnpm workspace and there is no
# package-lock.json to `npm ci` from;
# * the build context is the REPOSITORY ROOT, where pnpm-lock.yaml,
# pnpm-workspace.yaml and .npmrc live.
#
# docker build -f apps/web/Dockerfile -t sharper-web .
# docker compose build web # does exactly that
#
# The server reads STRAPI_URL / STRAPI_TOKEN / SITE_URL at RUN time, never at
# build time, so one image is valid for every environment.
###############################################################################
# --- base: the toolchain both stages share ----------------------------------
FROM node:22-bookworm-slim AS base
ENV PNPM_VERSION=9.12.3
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/* \
&& npm install --global "pnpm@${PNPM_VERSION}"
WORKDIR /repo
# --- build: install dependencies and produce build/client + build/server -----
FROM base AS build
# Manifests first so the install layer stays cached. Both workspace manifests
# are copied: pnpm checks the lockfile against every project in the workspace
# and treats it as stale if one is missing.
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
COPY apps/web/package.json ./apps/web/
COPY apps/cms/package.json ./apps/cms/
# `--filter web...` keeps Strapi out of this image entirely.
# devDependencies are required: vite, @react-router/dev and tailwind build here.
RUN pnpm install --frozen-lockfile --filter web...
COPY apps/web ./apps/web
RUN pnpm --filter web build
# --- runtime ----------------------------------------------------------------
FROM base AS runtime
ENV NODE_ENV=production \
PORT=3000
COPY --from=build /repo/node_modules ./node_modules
COPY --from=build /repo/package.json /repo/pnpm-workspace.yaml /repo/.npmrc ./
COPY --from=build /repo/apps/web ./apps/web
RUN chown -R node:node /repo/apps/web
USER node
WORKDIR /repo/apps/web
EXPOSE 3000
# /en is a real page: it proves the SSR handler renders, not just that a socket
# is open. It stays 200 even when the CMS is unreachable, because every route
# falls back to its bundled draft copy - so this healthcheck tracks the web
# service alone.
HEALTHCHECK --interval=15s --timeout=5s --start-period=30s --retries=5 \
CMD node -e "fetch('http://127.0.0.1:'+(process.env.PORT||3000)+'/en').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
# react-router-serve, the production server that ships with the framework.
CMD ["pnpm", "start"]