FROM node:24-slim@sha256:cb4e8f7c443347358b7875e717c29e27bf9befc8f5a26cf18af3c3dec80e58c5 AS base WORKDIR /app # Patch the base image's pre-installed OS packages before adding our own. # `node:20-slim` ships system libraries (e.g. libgnutls30) that `apt-get install` # never touches because they're already present, so they stay pinned to whatever # the base tag shipped. An explicit `apt-get upgrade` pulls Debian's security # point-releases (libgnutls30 -> 3.7.9-2+deb12u7), which clears the critical # GnuTLS CVEs Snyk reports against this image. # # Every network step tolerates a hard mirror outage so a transient Railway build # failure can't block deploys (2026-04-17: noble-security CDN hash-sum mismatch # blocked all Railway builds). `apt-get update` is retried so the refresh is # reliable before the security upgrade; the upgrade then carries the same `|| true` # tolerance that `update` already had and `install` keeps via `--fix-missing`, so # this step has no less download resilience than the original. The post-deploy # Snyk scan is the backstop that flags a libgnutls30 left un-upgraded by an outage. RUN (for i in 1 2 3; do apt-get update && break || sleep 5; done) \ && (apt-get upgrade -y --no-install-recommends || true) \ && apt-get install -y --fix-missing \ chromium \ fonts-liberation \ libatk-bridge2.0-0 \ libatk1.0-0 \ libcairo2 \ libcups2 \ libdbus-1-3 \ libgdk-pixbuf2.0-0 \ libnspr4 \ libnss3 \ libpango-1.0-0 \ libx11-6 \ libxcomposite1 \ libxdamage1 \ libxext6 \ libxfixes3 \ libxrandr2 \ libxrender1 \ --no-install-recommends && rm -rf /var/lib/apt/lists/* ENV PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/chromium # Install all dependencies (including devDeps for tsc) COPY package.json package-lock.json ./ RUN npm ci # Build COPY tsconfig.json ./ COPY src ./src COPY configs ./configs COPY migrations ./migrations RUN npm run build # Prune dev dependencies after build RUN npm prune --omit=dev # Runtime ENV NODE_ENV=production EXPOSE 3400 CMD ["node", "dist/api/server.js"]