Ctrl K
ComunidadGuías…

Dockerfile

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# --- Builder stage ---------------------------------------------------------
# Runs natively on the CI host for every target platform instead of under
# QEMU: the output is plain JS, and none of the server's production
# dependencies ships native code. Revisit if one ever does.
FROM --platform=$BUILDPLATFORM node:24-alpine AS builder
WORKDIR /app

# Copy workspace manifests first for cached deps install. The lock is
# committed and load-bearing (it pins the musl Rolldown binding this Alpine
# build needs), so copy it unconditionally and install with `npm ci`, which
# follows it exactly and fails loudly on lock/manifest drift.
COPY package.json package-lock.json ./
COPY shared/package.json shared/
COPY server/package.json server/
COPY client/package.json client/

RUN npm ci

# Copy sources and build.
COPY tsconfig.base.json ./
COPY shared/ ./shared/
COPY server/ ./server/
COPY client/ ./client/

RUN npm run build

# The client's runtime packages (CodeMirror, Shiki, Yjs, fonts...) are fully
# bundled into server/dist/public by the build, and the toolchain is only
# needed in this stage; reinstall just the server workspace's production
# dependencies so the runtime copy carries nothing else.
RUN rm -rf node_modules shared/node_modules server/node_modules client/node_modules \
  && npm ci --omit=dev -w server

# --- Runtime stage ---------------------------------------------------------
FROM node:24-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production

# Copy node_modules + workspace manifests + built artifacts from the builder.
COPY --from=builder /app/package.json ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/shared/package.json shared/
COPY --from=builder /app/shared/dist shared/dist
COPY --from=builder /app/server/package.json server/
COPY --from=builder /app/server/dist server/dist

# Drop root: the official node:alpine image ships a `node` user (uid 1000).
# Running unprivileged limits the blast radius if a future vulnerability in
# a dependency lets an attacker execute code in the container.
USER node

EXPOSE 3000
# busybox wget ships with Alpine; /healthz is served before the rate limiter
# so the probe never eats the per-IP budget.
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
  CMD wget -qO- "http://127.0.0.1:${PORT:-3000}/healthz" || exit 1
CMD ["node", "server/dist/index.js"]