Repository navigation
Shrink the Docker images - #2
Merged
Merged
Conversation
Backend: run the static binary on plain Alpine instead of the Go toolchain image, strip debug symbols and run as a non-root user. Alpine keeps a shell for the compose files that start it with sh -c. Frontend: build with Next.js standalone output and ship only the standalone server, its traced dependencies and static assets, instead of the whole .next directory (mostly build cache) and node_modules with dev dependencies. Install from package-lock.json with npm ci, move to Node 22 (Node 18 is end-of-life), run as the node user, and add a .dockerignore so a host's node_modules and .next never end up in the build context. Measured on local builds of the same steps: the backend binary layer goes from 26 MB to 18 MB and the frontend app layers from about 700 MB to 22 MB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The v1.0.0 images carry a lot they never use at runtime. The backend ships the whole Go toolchain next to a single static binary. The frontend ships the full
.nextdirectory, which is mostly build cache, plusnode_moduleswith dev dependencies.Download sizes combine the base image layers reported in the CI logs (
golang:1.24.4-alpine83.1 MB,node:18-alpine44.9 MB →alpine:3.223.8 MB,node:22-alpine60.7 MB) with the app layers, measured by building the same steps locally. On-disk sizes are estimates.Changes
Backend
alpine:3.22instead ofgolang:1.24.4-alpine. Alpine keeps a shell, because both compose files start the backend withsh -c.-trimpath -ldflags="-s -w", which takes the binary from 26 MB to 18 MB.Frontend
output: 'standalone'innext.config.js. The final stage copies only.next/standalone,.next/staticandpublic, and starts withnode server.js. The app layers go from about 700 MB to 22 MB. Of that, 266 MB was.next/cachealone.npm cifrompackage-lock.json, which matchespackage.json, so image builds are reproducible.nodeuser.frontend/.dockerignore, so a host'snode_modulesand.nextnever reach the build context. Before,COPY . .could overwrite the dependencies installed in the image with the host's copies.No compose or deployment changes are needed. The frontend still listens on port 3000, and
npm startstill works outside Docker; Next.js only prints a warning thatnode server.jsis preferred with standalone output.Testing
sha-6f5c956only;latestis untouched..dockerignore) with the same steps as the Dockerfile, assembled the runtime directory the way the final stage does, and rannode server.jswithNODE_ENV=production:public/timer-worker.jsreturn 200./apirewrite reaches the backend.Not tested yet
node, so no issues are expected. Pullingsha-6f5c956on a server and starting it once before merging would confirm it.