# antisocial # # Only headless Chromium is ever launched, so the image installs just that # browser onto a slim Node base rather than using the Playwright image, which # also carries Firefox and WebKit — roughly two thirds of its size, for # browsers this never opens. # # Built natively; the tag carries the architecture of the machine that built # it. Multi-arch would need buildx and a manifest list. FROM node:22-bookworm-slim AS build WORKDIR /app # The browser is installed in the runtime stage, not here. ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 COPY package.json package-lock.json ./ RUN npm ci COPY tsconfig.json tsconfig.build.json ./ COPY src ./src RUN npm run build && npm prune --omit=dev FROM node:22-bookworm-slim AS runtime WORKDIR /app ENV NODE_ENV=production \ HOST=0.0.0.0 \ PORT=8080 \ PROFILE_DIR=/data/profile \ PLAYWRIGHT_BROWSERS_PATH=/ms-playwright # `chromium-headless-shell` and not `chromium`: a headless launch resolves to # the shell either way, so installing the full browser alongside it would ship # a binary nothing ever executes. `src/browser/pool.ts` names the same channel # so the two cannot drift apart. # # Fonts matter more than they look: without them any rendered text — including # a verification puzzle we have to show a person — comes out as empty boxes. RUN npx --yes playwright@1.62.1 install --with-deps chromium-headless-shell \ && apt-get update \ && apt-get install -y --no-install-recommends fonts-liberation fonts-noto-color-emoji \ && rm -rf /var/lib/apt/lists/* /root/.npm COPY --from=build /app/node_modules ./node_modules COPY --from=build /app/dist ./dist COPY package.json ./ COPY public ./public # Chromium will not start as root without its sandbox disabled outright, and # there is no reason to run as root anyway. RUN useradd --create-home --uid 10001 antisocial \ && mkdir -p /data/profile \ && chown -R antisocial:antisocial /data /app \ && chmod -R a+rx /ms-playwright USER antisocial EXPOSE 8080 HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \ CMD node -e "fetch('http://127.0.0.1:'+(process.env.PORT||8080)+'/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))" CMD ["node", "dist/server.js"]