Stop shipping Firefox and WebKit this server never launches
Microsoft's own Playwright image carries all three engines at 3.5 GB; this server calls chromium.launch and nothing else, ever. Node's own slim base plus Chromium installed on its own comes to 1.84 GB - most of that is the OS libraries --with-deps pulls in for a sandboxed Chromium to run at all, not the browser binary itself. One mistake made and caught while building this: installing under HOME=/tmp/browser-home, which is gone the instant the container starts, because /tmp is mounted as tmpfs. Moved to PLAYWRIGHT_BROWSERS_PATH=/ms-playwright, outside it - which is also where Microsoft's own image keeps browsers, and for the same reason. Verified under the production security constraints, not assumed to still hold on a different base: seccomp profile, cap_drop ALL plus cap_add SYS_CHROOT, read-only root, tmpfs /tmp, chromiumSandbox: true. Chromium launched sandboxed and navigated a real page under all of them together. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
44ca1c5435
commit
b06c9df73c
|
|
@ -1,14 +1,35 @@
|
||||||
FROM mcr.microsoft.com/playwright:v1.63.0-noble
|
# 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 NODE_ENV=production
|
||||||
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
|
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
|
ENV HOME=/tmp/browser-home
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY package.json package-lock.json ./
|
COPY package.json package-lock.json ./
|
||||||
RUN npm ci --omit=dev --ignore-scripts \
|
RUN npm ci --omit=dev --ignore-scripts \
|
||||||
&& npm cache clean --force \
|
&& npm cache clean --force
|
||||||
&& chown -R pwuser:pwuser /app
|
|
||||||
|
# --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
|
COPY --chown=pwuser:pwuser src ./src
|
||||||
|
|
||||||
USER pwuser
|
USER pwuser
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue