-
Notifications
You must be signed in to change notification settings - Fork 1
Optimize Docker image size with multi-stage build and enable PM2 cluster mode #239
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,38 +1,39 @@ | ||||||||||
| FROM node:23-alpine | ||||||||||
| FROM node:23-alpine AS build | ||||||||||
| # Set build arguments with defaults | ||||||||||
| ARG NODE_ENV=production | ||||||||||
| ARG NPM_INSTALL_FLAGS= | ||||||||||
|
Comment on lines
+3
to
+4
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
🔧 Suggested change FROM node:23-alpine
+ARG NODE_ENV=production
+ENV NODE_ENV=$NODE_ENVAlso applies to: 18-22 🤖 Prompt for AI Agents |
||||||||||
|
|
||||||||||
| # Create app directory and set permissions | ||||||||||
| # Set working directory | ||||||||||
| WORKDIR /app | ||||||||||
|
|
||||||||||
| # Install dependencies needed for health checks with pinned version | ||||||||||
| RUN apk add --no-cache wget=1.25.0-r1 | ||||||||||
| # Create a non-root user and group | ||||||||||
| RUN addgroup -S appgroup && adduser -S appuser -G appgroup | ||||||||||
| # Copy package files | ||||||||||
| COPY website/package*.json ./ | ||||||||||
|
|
||||||||||
| # Install dependencies based on environment and flags | ||||||||||
| RUN npm ci $NPM_INSTALL_FLAGS | ||||||||||
|
Comment on lines
+12
to
+13
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: fd -t f Dockerfile | head -20Repository: speedandfunction/website Length of output: 101 🏁 Script executed: cat -n DockerfileRepository: speedandfunction/website Length of output: 1157 🏁 Script executed: cat -n website/package.json | head -30Repository: speedandfunction/website Length of output: 1350 🏁 Script executed: cat -n website/package.json | tail -50Repository: speedandfunction/website Length of output: 1865 Remove devDependencies from runtime image. Multi-stage build copies entire 🔧 Suggested change-RUN npm ci $NPM_INSTALL_FLAGS
+RUN npm ci $NPM_INSTALL_FLAGS \
+ && npm prune --omit=devAlso applies to: 30-30 🤖 Prompt for AI Agents |
||||||||||
|
|
||||||||||
| # Copy package files for efficient caching | ||||||||||
| # Copy website files | ||||||||||
| COPY website/ ./ | ||||||||||
|
|
||||||||||
| # Set build arguments with defaults | ||||||||||
| ARG NODE_ENV=production | ||||||||||
| ARG NPM_INSTALL_FLAGS= | ||||||||||
| FROM node:23-alpine | ||||||||||
|
|
||||||||||
| # Install dependencies based on environment and flags | ||||||||||
| RUN npm ci $NPM_INSTALL_FLAGS && \ | ||||||||||
| # Clean npm cache to reduce image size | ||||||||||
| npm cache clean --force | ||||||||||
| # Set working directory | ||||||||||
| WORKDIR /app | ||||||||||
|
|
||||||||||
| # Create a non-root user and group | ||||||||||
| RUN addgroup -S appgroup && adduser -S appuser -G appgroup | ||||||||||
|
|
||||||||||
| # Install dependencies needed for health checks with pinned version | ||||||||||
| RUN apk add --no-cache wget=1.25.0-r1 | ||||||||||
|
Comment on lines
+26
to
+27
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cd /dev/null && find . -name "Dockerfile" -type f 2>/dev/null | head -20Repository: speedandfunction/website Length of output: 117 🏁 Script executed: cat -n Dockerfile | head -40Repository: speedandfunction/website Length of output: 1157 🏁 Script executed: # Check the Alpine base image being used
grep -n "FROM" Dockerfile | head -5Repository: speedandfunction/website Length of output: 121 🏁 Script executed: # Check Alpine package versioning practices and wget availability
# Let's also verify if this pattern is indeed problematic by researching Alpine versioning
web_search
Alpine Linux package version pinning node:alpine floating tagRepository: speedandfunction/website Length of output: 163 🏁 Script executed: # Check if there are any other similar patterns in the Dockerfile
rg "apk add.*=" DockerfileRepository: speedandfunction/website Length of output: 105 🏁 Script executed: # Verify the node:23-alpine tag specifics
web_search
node:23-alpine base image tag floating Alpine versionRepository: speedandfunction/website Length of output: 171 🌐 Web query:
💡 Result:
Practical compatibility rule: Fixed base image release (or digest) + 🌐 Web query:
💡 Result:
If you want it explicitly pinned, use Sources: [1], [2] 🌐 Web query:
💡 Result: You can check what versions of an Alpine package exist in each Alpine release branch by querying each branch’s repository index ( Option A: Query versions across releases using
|
||||||||||
| # Install dependencies needed for health checks with pinned version | |
| RUN apk add --no-cache wget=1.25.0-r1 | |
| # Install dependencies needed for health checks with pinned version | |
| RUN apk add --no-cache wget |
🤖 Prompt for AI Agents
In `@Dockerfile` around lines 26 - 27, The Dockerfile pins wget to an invalid
Alpine revision (RUN apk add --no-cache wget=1.25.0-r1) which fails on
node:23-alpine (Alpine 3.21); fix by either changing the base image to a fixed
Alpine release tag (e.g., node:23-alpine3.21) or removing the package revision
pin (use RUN apk add --no-cache wget or pin to wget=1.25.0-r0), and update the
RUN line accordingly so it matches the available package in the chosen base
image.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -4,7 +4,7 @@ | |||||
| "description": "Apostrophe CMS website", | ||||||
| "main": "app.js", | ||||||
| "scripts": { | ||||||
| "start": "node scripts/generate_constants.js && node app @apostrophecms/asset:build && node app.js", | ||||||
| "start": "node scripts/generate_constants.js && node app @apostrophecms/asset:build && pm2-runtime app.js -i max", | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # Check if website/package.json exists and read the relevant lines
if [ -f website/package.json ]; then
echo "=== website/package.json (lines 1-70) ==="
head -70 website/package.json | cat -n
else
echo "website/package.json not found"
fiRepository: speedandfunction/website Length of output: 2802 🌐 Web query:
💡 Result:
What to do in containers
Rule of thumb: size PM2 instances to roughly the container CPU limit (often Sources: PM2 🏁 Script executed: # Search for Dockerfile, docker-compose, k8s config, or deployment hints
find . -maxdepth 3 -type f \( -name "Dockerfile*" -o -name "docker-compose*" -o -name "*.yaml" -o -name "*.yml" -o -name ".dockerignore" \) 2>/dev/null | head -20Repository: speedandfunction/website Length of output: 517 🏁 Script executed: # Check for any .env or configuration files that mention deployment/container
find . -maxdepth 2 -type f \( -name ".env*" -o -name "Procfile" -o -name "ecosystem.config.js" \) 2>/dev/nullRepository: speedandfunction/website Length of output: 50 🏁 Script executed: # Check the Dockerfile to understand container configuration
head -50 DockerfileRepository: speedandfunction/website Length of output: 884 🏁 Script executed: # Check deployment workflows for CPU limit configuration
grep -A 10 -B 5 "cpus\|CPU\|limits" deploy_to_aws.yml deploy_to_railway.yml 2>/dev/null | head -40Repository: speedandfunction/website Length of output: 50 Avoid
🔧 Suggested change- "start": "node scripts/generate_constants.js && node app `@apostrophecms/asset`:build && pm2-runtime app.js -i max",
+ "start": "node scripts/generate_constants.js && node app `@apostrophecms/asset`:build && pm2-runtime app.js -i ${PM2_INSTANCES:-1}",📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| "dev": "node scripts/generate_constants.js && nodemon", | ||||||
| "build": "node scripts/generate_constants.js && NODE_ENV=production node app.js apostrophe:generation", | ||||||
| "serve": "NODE_ENV=production node app.js", | ||||||
|
|
@@ -55,6 +55,7 @@ | |||||
| "mongodb": "^6.17.0", | ||||||
| "node-fetch": "^2.6.7", | ||||||
| "normalize.css": "^8.0.1", | ||||||
| "pm2-runtime": "^5.4.1", | ||||||
| "postmark": "^4.0.5", | ||||||
| "swiper": "^11.2.6", | ||||||
| "yargs": "^17.7.2", | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: speedandfunction/website
Length of output: 105
🌐 Web query:
Node.js LTS versions 2025 2026 current supported versions💡 Result:
Currently supported Node.js major versions (as of Jan 29, 2026)
LTS status across 2025–2026 (key transitions)
Sources: Node.js official releases list [1] and the Node.js Release WG schedule (authoritative lifecycle dates) [2].
🏁 Script executed:
Repository: speedandfunction/website
Length of output: 950
Switch from Node 23 to a supported LTS version.
Node 23 is out of security support. Use Node 24 (Active LTS until Apr 2028) or Node 22 (Maintenance LTS until Apr 2027). Avoid Node 20 as it EOLs Apr 30, 2026.
🔧 Suggested change
Also applies to: 18-18
🧰 Tools
🪛 Checkov (3.2.334)
[low] 1-39: Ensure that HEALTHCHECK instructions have been added to container images
(CKV_DOCKER_2)
🤖 Prompt for AI Agents