Skip to content

Commit 840b7f7

Browse files
oiokiclaude
andauthored
build(docker): Switch to Docker Hardened Images (DHI) (#212)
* build: Switch to Docker Hardened Images (DHI) Replace node:24.14.0 and node:24.14.0-slim base images with Sentry's DHI equivalents (us-docker.pkg.dev/sentryio/dhi/node:24-debian13-dev and node:24-debian13). Move canvas native compilation (yarn install + build deps) entirely into the builder stage so the runtime image no longer needs build-essential or -dev headers. The runtime stage only installs the minimal shared libraries that canvas needs at runtime (libcairo2, libpango-1.0-0, libjpeg62-turbo, libgif7, librsvg2-2), eliminating ~1,400 Trivy findings that were rooted in the Debian system package footprint. Co-Authored-By: Claude <noreply@anthropic.com> * build: Fix platform selection for DHI images The DHI manifest entries for amd64 have an unexpected variant field ("v8"), which is normally an ARM designator. Docker BuildKit fails to match linux/amd64 against these entries. Add --platform=linux/amd64 to both FROM lines so BuildKit selects by architecture directly rather than relying on variant matching. Co-Authored-By: Claude <noreply@anthropic.com> * build: Fix DHI platform specifier to linux/amd64/v8 The DHI manifest non-standardly labels the amd64 image with variant "v8" (an ARM designator), making its platform string linux/amd64/v8 rather than the expected linux/amd64. Cloud Build's BuildKit fails to match linux/amd64 against linux/amd64/v8. Specify the exact platform string from the manifest so BuildKit resolves the correct image layer. Co-Authored-By: Claude <noreply@anthropic.com> * build: Use standard node image for builder, DHI for runtime The DHI dev image has libexpat1=2.7.4 (arch:all) pre-installed as a security patch, which conflicts with the entire canvas build dep chain (libcairo2-dev → libfontconfig-dev → libexpat1-dev requires libexpat1 = 2.7.1-2 arch-specific). Both APT solver 3.0 and the classic solver fail to resolve this. Use standard node:24.14.0 for the builder stage (compiles canvas native module and TypeScript without package conflicts). The runtime stage uses DHI node:24-debian13-dev since the fully minimal node:24-debian13 image has no shell or package manager — canvas needs runtime shared libraries (libcairo2, libpango-1.0-0, libjpeg62-turbo, libgif7, librsvg2-2) which apt-get install correctly on Debian 13 in the -dev image. The --platform=linux/amd64/v8 flag is scoped to only the DHI FROM line because the DHI manifest incorrectly labels the amd64 entry with variant "v8" (an ARM designator). Standard node:24.14.0 uses linux/amd64 without a variant, so the platform flag must not apply to that stage. Compiled node_modules (including canvas.node native binary) is copied from the builder so the runtime stage never needs build tools. Co-Authored-By: Claude <noreply@anthropic.com> * build: Remove unnecessary canvas runtime library installs canvas 3.x bundles its own copies of libcairo, libpango, libjpeg, libgif, librsvg, harfbuzz, glib, etc. inside node_modules/canvas/build/Release/. No system-level canvas libraries need to be installed in the runtime image. The -dev variant is still required over the minimal node:24-debian13 image because canvas's bundled dependencies (librsvg, glib) need base system libs (libz, libexpat, libuuid, liblzma) that are present in the dev image but absent from the stripped-down minimal image. Co-Authored-By: Claude <noreply@anthropic.com> * build(docker): Switch builder stage to DHI image canvas 3.x downloads pre-built binaries via node-pre-gyp, so the build stage no longer needs system canvas libraries (libcairo2-dev, libpango1.0-dev, etc.). This removes the conflict with DHI's pre-installed libexpat1 2.7.4 package that previously blocked using the hardened image in the builder stage. Also removes the --platform=linux/amd64/v8 workaround now that the DHI image manifests have correct platform variant labels. Co-Authored-By: Claude <noreply@anthropic.com> * build(docker): Use minimal DHI runtime image with targeted lib copy Switch the runtime stage from node:24-debian13-dev to node:24-debian13 (the minimal/distroless image) to reduce the attack surface. canvas 3.x bundles its graphics libs (libcairo, libpango, etc.) so no system canvas libraries are needed. However, canvas's bundled librsvg and glib still require four basic system libs absent from the minimal image (libz, libexpat, libuuid, liblzma). These are collected in the builder stage and copied into the runtime, rather than pulling in the full -dev image. The smoke-test RUN uses exec form since the minimal image has no shell. Co-Authored-By: Claude <noreply@anthropic.com> * build(docker): Exclude devDependencies from production image Add a separate deps stage that runs yarn install --production so that jest, typescript, eslint, ts-jest, supertest, and their transitive deps are not copied into the runtime image. The builder stage keeps the full install for TypeScript compilation. node_modules layer in the runtime image shrinks from ~94 MB to ~46 MB. Co-Authored-By: Claude <noreply@anthropic.com> * build(docker): Reduce build stages from 3 to 2 Merge the separate deps and builder stages into a single builder stage. Install all dependencies, compile TypeScript, then prune to production-only deps in place before copying into the runtime image. Runtime image contents remain identical. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3ad3fda commit 840b7f7

1 file changed

Lines changed: 25 additions & 20 deletions

File tree

Dockerfile

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,43 @@
1-
FROM node:24.14.0 AS builder
1+
FROM us-docker.pkg.dev/sentryio/dhi/node:24-debian13-dev AS builder
22

3-
COPY package.json yarn.lock .
3+
WORKDIR /build
4+
5+
COPY package.json yarn.lock ./
46
RUN yarn install --frozen-lockfile
57

68
COPY tsconfig.json .
79
COPY src src
810
RUN yarn build
911

10-
FROM node:24.14.0-slim
12+
# Drop devDependencies from node_modules for the runtime image
13+
RUN yarn install --frozen-lockfile --production
1114

12-
ENV NODE_ENV=production
15+
# canvas 3.x bundles its graphics libs (libcairo, libpango, etc.) but its
16+
# bundled librsvg/glib still need a few basic system libs absent from the
17+
# minimal runtime image. Collect them here to copy in without pulling the
18+
# entire -dev system into the runtime.
19+
RUN mkdir -p /canvas-sys-libs && \
20+
find /lib /usr/lib -maxdepth 3 \( \
21+
-name "libz.so.1*" -o \
22+
-name "libexpat.so.1*" -o \
23+
-name "libuuid.so.1*" -o \
24+
-name "liblzma.so.5*" \
25+
\) -exec cp -P --parents {} /canvas-sys-libs/ \;
1326

14-
RUN npm install -g npm@latest \
15-
&& npm cache clean --force
1627

17-
RUN apt-get update && apt-get install -y --no-install-recommends \
18-
build-essential \
19-
libcairo2-dev \
20-
libpango1.0-dev \
21-
libjpeg-dev \
22-
libgif-dev \
23-
librsvg2-dev \
24-
&& rm -rf /var/lib/apt/lists/*
28+
FROM us-docker.pkg.dev/sentryio/dhi/node:24-debian13
2529

26-
WORKDIR /usr/src/app
30+
ENV NODE_ENV=production
2731

28-
COPY package.json yarn.lock ./
29-
RUN yarn install --frozen-lockfile \
30-
&& yarn cache clean
32+
WORKDIR /usr/src/app
3133

34+
COPY package.json ./
3235
COPY fonts fonts
33-
COPY --from=builder lib lib
36+
COPY --from=builder /build/node_modules node_modules
37+
COPY --from=builder /build/lib lib
38+
COPY --from=builder /canvas-sys-libs/ /
3439

35-
RUN node lib/index.js --help
40+
RUN ["node", "lib/index.js", "--help"]
3641

3742
EXPOSE 9090/tcp
3843
CMD ["node", "./lib/index.js", "server", "9090"]

0 commit comments

Comments
 (0)