PHANTOM
🇮🇳 IN
Skip to content

fix: reduce docs Docker image size by cleaning npm cache#1129

Open
haosenwang1018 wants to merge 1 commit intoMemMachine:mainfrom
haosenwang1018:fix/docs-docker-image-size
Open

fix: reduce docs Docker image size by cleaning npm cache#1129
haosenwang1018 wants to merge 1 commit intoMemMachine:mainfrom
haosenwang1018:fix/docs-docker-image-size

Conversation

@haosenwang1018
Copy link

Addresses #1110

The memmachine-docs Docker image is ~1.12GB, which is excessive for a documentation server. The main contributor is the npm cache and temp files left behind after npm install -g mint.

This fix cleans up npm cache, /tmp, and ~/.npm in the same RUN layer as the install, which should significantly reduce the final image size.

@sscargal
Copy link
Contributor

Thanks @haosenwang1018 . Please sign your commit(s). See CONTRIBUTING.md for instructions.

As noted by Christian, starting with a Node image rather than a vanilla Alpine image helps reduce the final image, as does a multi-stage approach. Combining all suggestions should give us the smallest possible image. Is that something you want to tackle? This PR is a good start in that direction.

…se image

Signed-off-by: haosenwang1018 <haosenwang1018@users.noreply.github.com>
@haosenwang1018 haosenwang1018 force-pushed the fix/docs-docker-image-size branch from 99622c4 to ce5e8a6 Compare February 21, 2026 00:05
@haosenwang1018
Copy link
Author

Thanks for the feedback! I've updated the Dockerfile to use a multi-stage build with node:22-alpine as the base image (incorporating Christian's suggestion), and added Signed-off-by to the commit.

Note: I don't have a GPG key configured yet for the -S flag, so the commit is signed-off but not GPG-signed. I can set that up if needed.

Changes in this update:

  • Switched from alpine:latest + apk add nodejs npmnode:22-alpine base
  • Added multi-stage build: install mint in builder stage, copy only the installed packages to final image
  • npm cache + temp cleanup in builder stage (won't carry to final image anyway)

@sscargal
Copy link
Contributor

Thanks for the fast response. I'll test the new Docker to see what the result is. Should be able to get back to you next week.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses issue #1110 by reducing the memmachine-docs Docker image size from ~1.12GB through implementing a multi-stage build pattern and cleaning up npm artifacts.

Changes:

  • Migrated from alpine:latest with manual Node.js installation to official node:22-alpine base image
  • Implemented multi-stage Docker build with a builder stage for installing Mintlify and a minimal final stage
  • Added cleanup of npm cache and temporary files in the build stage to reduce layer size

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1 to +12
FROM node:22-alpine AS builder

# Install Mintlify globally
RUN npm install -g mint
RUN npm install -g mint && \
npm cache clean --force && \
rm -rf /tmp/* /root/.npm

FROM node:22-alpine

# Copy only the global Mintlify installation from builder
COPY --from=builder /usr/local/lib/node_modules /usr/local/lib/node_modules
COPY --from=builder /usr/local/bin/mint /usr/local/bin/mint
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The multi-stage build approach should significantly reduce the image size, which addresses the issue. However, there doesn't appear to be any automated testing for the docs Docker container (the docs service isn't included in CI workflows).

It's important to manually verify that the built image works correctly before merging:

  1. Build the image: docker build -t memmachine-docs:test docs/
  2. Run the container: docker run -v $(pwd)/docs:/docs:ro -p 3000:3000 memmachine-docs:test
  3. Verify that mint dev starts successfully and the docs are accessible at http://localhost:3000
  4. Check the image size: docker images memmachine-docs:test (should be significantly smaller than 1.12GB)

This manual verification is critical because the mint binary needs access to its node_modules dependencies, and we need to ensure the COPY operations preserve all necessary files and symlinks correctly.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will address automations after manual verification.

@jgong
Copy link
Contributor

jgong commented Feb 24, 2026

Hi @haosenwang1018 , Below is the manual verification with steps provided in above comment

  • Build the image: docker build -t memmachine-docs:test docs/
jinggong@Jings-MBP MemMachine % docker build -t memmachine-docs:test docs/
[+] Building 69.6s (10/10) FINISHED                                                             docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                            0.0s
 => => transferring dockerfile: 581B                                                                            0.0s
 => [internal] load metadata for docker.io/library/node:22-alpine                                               1.3s
 => [auth] library/node:pull token for registry-1.docker.io                                                     0.0s
 => [internal] load .dockerignore                                                                               0.0s
 => => transferring context: 2B                                                                                 0.0s
 => [builder 1/2] FROM docker.io/library/node:22-alpine@sha256:e4bf2a82ad0a4037d28035ae71529873c069b13eb045546  4.8s
 => => resolve docker.io/library/node:22-alpine@sha256:e4bf2a82ad0a4037d28035ae71529873c069b13eb0455466ae0bc13  0.0s
 => => sha256:6558f33bb3b6e9c80d4c77bec5c801380ee995c9c4a2ac331712725f5e7b5509 445B / 445B                      0.2s
 => => sha256:3691184ba0a570957c383fb80dd573d665b8d320f9e4a8168e6dd18c5e702c0c 1.26MB / 1.26MB                  0.4s
 => => sha256:2088de1f599b117d4fd7d0109d786f2f3c2a8e74b063a11730c106ea3c795cb6 52.24MB / 52.24MB                3.4s
 => => extracting sha256:2088de1f599b117d4fd7d0109d786f2f3c2a8e74b063a11730c106ea3c795cb6                       1.3s
 => => extracting sha256:3691184ba0a570957c383fb80dd573d665b8d320f9e4a8168e6dd18c5e702c0c                       0.1s
 => => extracting sha256:6558f33bb3b6e9c80d4c77bec5c801380ee995c9c4a2ac331712725f5e7b5509                       0.0s
 => [builder 2/2] RUN npm install -g mint &&     npm cache clean --force &&     rm -rf /tmp/* /root/.npm       44.9s
 => [stage-1 2/4] COPY --from=builder /usr/local/lib/node_modules /usr/local/lib/node_modules                   3.8s
 => [stage-1 3/4] COPY --from=builder /usr/local/bin/mint /usr/local/bin/mint                                   0.2s
 => [stage-1 4/4] WORKDIR /docs                                                                                 0.0s
 => exporting to image                                                                                          9.3s
 => => exporting layers                                                                                         6.1s
 => => exporting manifest sha256:8f1a0736e5a3673d1088967ee38a09b49f9a07d2ef3b037dccf242972edf1de8               0.0s
 => => exporting config sha256:fe842176acc41763d57703245e42795ff127ae51201146531b62da7824b76854                 0.0s
 => => exporting attestation manifest sha256:f62bb24d3001b11be2b4e8901f7564a8a3bb7a31b7253115c363c996652d0f64   0.0s
 => => exporting manifest list sha256:19d813d06d079527f0e0cf185d25c25241da8deb446f81b6770c40735a9a0211          0.0s
 => => naming to docker.io/library/memmachine-docs:test                                                         0.0s
 => => unpacking to docker.io/library/memmachine-docs:test                                                      3.1s
  • Run the container: docker run -v $(pwd)/docs:/docs:ro -p 3000:3000 memmachine-docs:test --- it exits with error
jinggong@Jings-MBP MemMachine % docker run -v $(pwd)/docs:/docs:ro -p 3000:3000 memmachine-docs:test
node:internal/modules/package_json_reader:314
  throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base), null);
        ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@mintlify/cli' imported from /usr/local/bin/mint
    at Object.getPackageJSONURL (node:internal/modules/package_json_reader:314:9)
    at packageResolve (node:internal/modules/esm/resolve:767:81)
    at moduleResolve (node:internal/modules/esm/resolve:853:18)
    at defaultResolve (node:internal/modules/esm/resolve:983:11)
    at #cachedDefaultResolve (node:internal/modules/esm/loader:731:20)
    at ModuleLoader.resolve (node:internal/modules/esm/loader:708:38)
    at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:310:38)
    at ModuleJob._link (node:internal/modules/esm/module_job:182:49) {
  code: 'ERR_MODULE_NOT_FOUND'
}

Node.js v22.22.0

The error is then explained by claude code:

In the builder stage, npm install -g mint installs Mintlify and all its
  dependencies into /usr/local/lib/node_modules/. The final stage then copies two things:

  1. /usr/local/lib/node_modules — the installed packages
  2. /usr/local/bin/mint — the CLI entry point script

  The problem is that /usr/local/bin/mint is a Node.js ESM script that has a direct import '@mintlify/cli' statement.
   When Node.js resolves that import, it searches for @mintlify/cli starting from the script's own directory
  (/usr/local/bin/), walking up the filesystem:

  /usr/local/bin/node_modules/   ← doesn't exist
  /usr/local/node_modules/       ← doesn't exist
  /usr/node_modules/             ← doesn't exist
  /node_modules/                 ← doesn't exist

I tried several solutions but it seems this one works, the diff is

diff --git a/docs/Dockerfile b/docs/Dockerfile
index 4c07fb6..3acf558 100644
--- a/docs/Dockerfile
+++ b/docs/Dockerfile
@@ -1,21 +1,18 @@
 FROM node:22-alpine AS builder

-# Install Mintlify globally
-RUN npm install -g mint && \
-    npm cache clean --force && \
-    rm -rf /tmp/* /root/.npm
+# Local install so mint and @mintlify/cli share one node_modules (no ESM resolution issues)
+WORKDIR /opt/mint
+RUN npm init -y && npm install mint && npm cache clean --force

 FROM node:22-alpine

-# Copy only the global Mintlify installation from builder
-COPY --from=builder /usr/local/lib/node_modules /usr/local/lib/node_modules
-COPY --from=builder /usr/local/bin/mint /usr/local/bin/mint
+# Use the local install; mint and deps are under /opt/mint/node_modules
+COPY --from=builder /opt/mint/node_modules /opt/mint/node_modules
+COPY --from=builder /opt/mint/package.json /opt/mint/package.json

-# Create working directory
-WORKDIR /docs
+ENV PATH="/opt/mint/node_modules/.bin:${PATH}"
+ENV NODE_PATH=/opt/mint/node_modules

-# Expose port (Mintlify dev server default)
+WORKDIR /docs
 EXPOSE 3000
-
-# Default command
 CMD ["mint", "dev"]

Would you please help to check the suggested change and see if these could be valid fix? Many thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants