Node-Quark delivers secure, distroless Node.js Docker images based on scratch
3.1K
Ultra-minimal, multi-architecture Node.js containers built from scratch
Node-Quark delivers secure, distroless Node.js Docker images based on scratch rather than full Linux distributions. By dynamically extracting only the essential libraries required by Node.js from Alpine Linux, we achieve images that are ~35MB (compared to ~180MB+ for standard Alpine Node images) while maintaining full HTTPS, crypto, and ICU (internationalization) support.
scratch (empty image), not Alpine or Debianlddlinux/amd64 and linux/arm64/v8 (Apple Silicon, AWS Graviton, Raspberry Pi)uid:1000), no shell, no package manager, minimal filesystem# Pull the latest LTS version (Node 24)
docker pull xutyxd/node-quark:24
# Run a quick test
docker run --rm xutyxd/node-quark:24 -e "console.log('Hello from scratch!')"
# Check the size (should be ~60MB vs 180MB+ for node:24-alpine)
docker images xutyxd/node-quark:24
# Multi-arch support (automatically selects correct platform)
docker run --rm xutyxd/node-quark:24 -p "process.arch"
| Tag | Node Version | Alpine Base | Size | Platform |
|---|---|---|---|---|
20, 20.15, 20.15.1, 20.15.1-alpine3.20 | 20.x | 3.20 | amd64, arm64 | |
22, 22.22, 22.22.2, 22.22.2-alpine3.22, latests | 22.x | 3.22 | amd64, arm64 | |
24, 24.1, 24.1.0, 24.1.0-alpine3.24, latests | 24.x | 3.24 | amd64, arm64 | |
edge, 20260127-edge | 20260127 (dev) | edge | amd64, arm64 |
How It Works
Traditional Node.js images include a full OS (Alpine, Debian, etc.) with shells, package managers, and hundreds of unused files. Node-Quark takes a different approach:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Stage 1: Builder (Alpine Linux) ā
ā āāā Install Node.js from Alpine repos ā
ā āāā Install UPX ā
ā āāā Run `ldd /usr/bin/node` ā
ā āāā Extract all linked libraries ā
ā āāā Copy to /rootfs structure ā
āāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāā
ā
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Stage 2: Runner (Scratch) ā
ā āāā Copy /rootfs (Node + libs only) ā
ā āāā CA certificates for HTTPS ā
ā āāā Non-root user (1000:1000) ā
ā āāā No shell, no package manager ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Dynamic Dependency Extraction
Unlike other distroless images that hardcode library paths (which break when Node.js updates), we use ldd to automatically detect the exact libraries needed:
# Capture every library Node.js links against
ldd /usr/bin/node | grep -o '/[^ ]*' | sort -u > /tmp/libs.txt
# Copy each library while preserving directory structure
while IFS= read -r lib; do
cp -L "$lib" "/rootfs$lib"
done < /tmp/libs.txt
This ensures compatibility across Alpine versions (3.20, 3.22, edge) without manual updates when dependencies change.
Security Features
node (uid 1000), not rootDockerfile integration
Node-Quark is designed to be used as runner in your Dockerfiles. It's a good idea to use a multi-stage build to reduce the size of your final image.
# Use node:alpine as base
# It has npm, bash, and other utilities
FROM alpine AS builder
# Download and install Node.js
RUN apk add --no-cache nodejs npm
WORKDIR /user/src/app
ADD . .
RUN npm install
RUN npm run openapi:bundle
RUN npm run server:build
RUN npm run clean
# --------
FROM alpine:3.24 AS tools
RUN mkdir -p /usr/src/app && \
chown -R 1000:1000 /usr/src/app
# --------
FROM xutyxd/node-quark:24 AS runner
COPY --from=tools /usr/src/app /usr/src/app
WORKDIR /usr/src/app
COPY --from=builder /user/src/app/server/cjs /user/src/app/server/cjs
COPY --from=builder /user/src/app/server/openapi /user/src/app/server/openapi
COPY --from=builder /user/src/app/package.json /user/src/app/package.json
COPY --from=builder /user/src/app/node_modules /user/src/app/node_modules
# As ENTRYPOINT is directly /bin/node, only need to point to the main file
CMD ["./server/cjs/index.js"]
EXPOSE 8080
| Image | Size | Base | Security |
|---|---|---|---|
node:24 | ~1.13GB | Debian Bookworm | Many CVEs |
node:24-alpine | ~164MB | Alpine 3.21 | Minimal CVEs |
node:24-slim | ~227MB | Debian Slim | Medium CVEs |
| xutyxd/node-quark:24 | Scratch | Minimal attack surface |
All published images are signed using Sigstore Cosignā with keyless signing via GitHub Actions OIDC. You can verify the provenance and SBOM without installing anything except Docker.
docker run --rm \
ghcr.io/sigstore/cosign/cosign:v2.4.1 \
verify-attestation \
--certificate-identity=https://github.com/xutyxd/node-quark/.github/workflows/docker.yml@refs/heads/main \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
--type spdx \
xutyxd/node-quark:24
Content type
Image
Digest
sha256:dca052255ā¦
Size
13.3 kB
Last updated
1 day ago
docker pull xutyxd/node-quark:sha256-eb67eba8a0c9172076872f37052749a49a04ca86021a0dc4aec02d8770e0d8ee.att