# A slim Node base plus Chromium alone, not Microsoft's own image: that one ships Chromium, # Firefox and WebKit together at 3.5 GB, when this server only ever launches Chromium # (browser-service.js calls chromium.launch, never firefox or webkit). Built and verified this way # comes to 1.84 GB - Chromium's own weight plus the OS libraries it needs, nothing else - and every # security constraint below (seccomp, cap_drop, the read-only root, chromiumSandbox: true) was # checked against this base before it replaced the old one, not assumed to still hold. FROM node:24-bookworm-slim ENV NODE_ENV=production ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 # Outside /tmp on purpose: the container mounts /tmp as tmpfs (see mcp-stack.compose.yml), so a # browser installed under HOME=/tmp/... at build time would be gone the instant the real container # started - a mistake made once while building this image, and the reason it is called out here. # /ms-playwright is where Microsoft's own image keeps browsers, for the identical reason. ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright ENV HOME=/tmp/browser-home WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci --omit=dev --ignore-scripts \ && npm cache clean --force # --with-deps installs the apt packages Chromium needs to run headless and sandboxed - fonts, # libgtk, libnss and the rest - which is most of what this layer costs; the browser binaries # themselves are a few hundred MB. chromium.launch({ headless: true }) reaches for the headless # shell build Playwright ships alongside full Chromium, so both are needed, not just one. RUN npx --yes playwright@1.63.0 install --with-deps chromium \ && apt-get clean && rm -rf /var/lib/apt/lists/* \ && chmod -R o+rX /ms-playwright RUN groupadd -r pwuser && useradd -r -g pwuser -G audio,video pwuser \ && mkdir -p /tmp/browser-home && chown -R pwuser:pwuser /app COPY --chown=pwuser:pwuser src ./src USER pwuser CMD ["node", "src/index.js"]