home / skills / laurigates / claude-plugins / nodejs-containers

nodejs-containers skill

/container-plugin/skills/nodejs-containers

This skill helps you optimize Node.js container images with Alpine, multi-stage builds, and dependency patterns to reduce size and improve build times.

npx playbooks add skill laurigates/claude-plugins --skill nodejs-containers

Review the files below or copy the command above to add this skill to your agents.

Files (2)
SKILL.md
5.8 KB
---
model: haiku
created: 2026-01-15
modified: 2026-02-14
reviewed: 2026-01-15
name: nodejs-containers
description: |
  Node.js-specific container optimization patterns including Alpine variants,
  multi-stage builds, node_modules caching, production dependency separation,
  and optimization from ~900MB to ~50-100MB. Covers npm/yarn/pnpm patterns,
  BuildKit cache mounts, and non-root user configuration.
  Use when working with Node.js containers, Dockerfiles for Node apps, or optimizing Node image sizes.
allowed-tools: Bash, Read, Grep, Glob, Edit, Write, TodoWrite, WebSearch, WebFetch
---

# Node.js Container Optimization

Expert knowledge for building optimized Node.js container images using Alpine variants, multi-stage builds, and Node.js-specific dependency management patterns.

## When to Use This Skill

| Use this skill when... | Use `container-development` instead when... |
|------------------------|---------------------------------------------|
| Building Node.js-specific Dockerfiles | General multi-stage build patterns |
| Optimizing Node.js image sizes | Language-agnostic container security |
| Handling npm/yarn/pnpm in containers | Docker Compose configuration |
| Dealing with native module builds | Non-Node.js container optimization |

## Core Expertise

**Node.js Container Challenges**:
- Large node_modules directories (100-500MB)
- Full base images include build tools (~900MB)
- Separate dev and production dependencies
- Different package managers (npm, yarn, pnpm)
- Native modules requiring build tools

**Key Capabilities**:
- Alpine-based images (~100MB vs ~900MB full)
- Multi-stage builds separating build and runtime
- BuildKit cache mounts for node_modules
- Production-only dependency installation
- Non-root user configuration

## Optimized Multi-Stage Pattern (Node Servers)

The recommended pattern achieves ~100-150MB images:

```dockerfile
# Dependencies stage - production only
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

# Build stage - includes devDependencies
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Runtime stage - minimal
FROM node:20-alpine
WORKDIR /app

# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -u 1001 -S nodejs -G nodejs

# Copy dependencies and built app
COPY --from=deps --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=build --chown=nodejs:nodejs /app/dist ./dist
COPY --chown=nodejs:nodejs package.json ./

USER nodejs
EXPOSE 3000

HEALTHCHECK --interval=30s CMD node healthcheck.js || exit 1

CMD ["node", "dist/server.js"]
```

## BuildKit Cache Mounts (Fastest Builds)

```dockerfile
# syntax=docker/dockerfile:1

FROM node:20-alpine AS build
WORKDIR /app

# Cache mount for npm cache
RUN --mount=type=cache,target=/root/.npm \
    --mount=type=bind,source=package.json,target=package.json \
    --mount=type=bind,source=package-lock.json,target=package-lock.json \
    npm ci

COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
USER node
CMD ["node", "dist/server.js"]
```

**Build performance**:
- First build: ~2-3 minutes
- Subsequent builds (no package changes): ~10-20 seconds
- Subsequent builds (package changes): ~30-60 seconds

## Package Manager Patterns

### npm

```dockerfile
COPY package*.json ./
RUN npm ci --only=production
RUN npm cache clean --force
```

### yarn

```dockerfile
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile --production
RUN yarn cache clean
```

### pnpm

```dockerfile
RUN npm install -g pnpm
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --prod
# pnpm creates smaller node_modules with hard links (20-30% smaller)
```

## Performance Impact

| Metric | Full Node (900MB) | Alpine (350MB) | Multi-Stage (100MB) | Improvement |
|--------|-------------------|----------------|---------------------|-------------|
| **Image Size** | 900MB | 350MB | 100MB | 89% reduction |
| **Pull Time** | 3m 20s | 1m 10s | 25s | 87% faster |
| **Build Time** | 4m 30s | 3m 15s | 2m 30s | 44% faster |
| **Rebuild (cached)** | 2m 10s | 1m 30s | 15s | 88% faster |
| **Memory Usage** | 512MB | 256MB | 180MB | 65% reduction |

## Security Impact

| Image Type | Vulnerabilities | Size | Risk |
|------------|-----------------|------|------|
| **node:20 (Debian)** | 45-60 CVEs | 900MB | High |
| **node:20-alpine** | 8-12 CVEs | 350MB | Medium |
| **Multi-stage Alpine** | 4-8 CVEs | 100MB | Low |
| **Distroless Node** | 2-4 CVEs | 120MB | Very Low |

## Agentic Optimizations

| Context | Command | Purpose |
|---------|---------|---------|
| **Fast rebuild** | `DOCKER_BUILDKIT=1 docker build --target build .` | Build only build stage |
| **Size check** | `docker images app --format "table {{.Repository}}\t{{.Size}}"` | Compare sizes |
| **Layer analysis** | `docker history app:latest --human --no-trunc \| head -20` | Find large layers |
| **Dependency audit** | `docker run --rm app npm audit --production` | Check vulnerabilities |
| **Cache clear** | `docker builder prune --filter type=exec.cachemount` | Clear BuildKit cache |
| **Test locally** | `docker run --rm -p 3000:3000 app` | Quick local test |

## Best Practices

- Use Alpine variants for smaller images
- Use `npm ci` not `npm install` (reproducible builds)
- Separate dev and production dependencies
- Run as non-root user
- Use multi-stage builds for production
- Layer package.json separately from source code
- Add .dockerignore to exclude node_modules, tests

For detailed examples, advanced patterns, and best practices, see [REFERENCE.md](REFERENCE.md).

## Related Skills

- `container-development` - General container patterns, multi-stage builds, security
- `go-containers` - Go-specific container optimizations
- `python-containers` - Python-specific container optimizations

Overview

This skill describes Node.js-specific container optimization patterns to shrink image size, speed builds, and improve security. It focuses on Alpine variants, multi-stage builds, node_modules caching, production-only dependency installation, and non-root runtime configuration. Practical patterns cover npm, yarn, and pnpm plus BuildKit cache mounts to move typical Node images from ~900MB down to ~50–150MB.

How this skill works

The approach separates dependency installation, build, and runtime into distinct Docker build stages so only production artifacts land in the final image. It leverages Alpine base images, package-manager best practices (npm ci / yarn --frozen-lockfile / pnpm), and BuildKit cache mounts to speed iterative builds. Final images run as a non-root user and copy only built assets and production node_modules to minimize size and attack surface.

When to use it

  • Building Dockerfiles for Node.js servers or API services.
  • Reducing image size and pull time for Node apps before deployment.
  • Handling native modules that require build tooling separately from runtime.
  • Improving CI/CD build speed with cache mounts and reproducible installs.
  • Enforcing production-only dependencies in runtime images.

Best practices

  • Use multi-stage builds: separate deps, build, and runtime stages.
  • Prefer Alpine or distroless runtime bases to reduce size and CVEs.
  • Install deps with npm ci / yarn --frozen-lockfile / pnpm --prod for reproducible installs.
  • Layer package.json separately from source to maximize cache hits.
  • Use BuildKit cache mounts for npm/yarn/pnpm caches to speed rebuilds.
  • Run the final container as a non-root user and include a .dockerignore excluding node_modules.

Example use cases

  • API server: build with devDependencies in a build stage, copy only dist and production node_modules to the runtime stage.
  • CI pipelines: use DOCKER_BUILDKIT and cache mounts to reduce rebuilds from minutes to seconds when packages don't change.
  • Microservices: shrink images for faster scaling and lower network egress by switching from full Debian Node images to multi-stage Alpine images.
  • Native modules: compile native code in a build stage that includes build tools, then copy binaries into a slim runtime image.
  • Monorepos: install dependencies in isolated stages per service and reuse cached layers to avoid repeated installs.

FAQ

Will switching to Alpine break native modules?

Native modules often require build tools; compile them in a build stage that includes necessary tooling, then copy the compiled artifacts to the Alpine runtime to avoid runtime build dependencies.

Which package manager yields the smallest node_modules?

pnpm typically produces smaller node_modules via hard links, often 20–30% smaller than npm/yarn, but use the manager your project supports and apply the same multi-stage/caching patterns.