ワンクリックで
docker
Docker standards for Node.js/pnpm apps. Auto-load when writing or reviewing Dockerfiles, .dockerignore, or docker-compose files.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Docker standards for Node.js/pnpm apps. Auto-load when writing or reviewing Dockerfiles, .dockerignore, or docker-compose files.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | docker |
| description | Docker standards for Node.js/pnpm apps. Auto-load when writing or reviewing Dockerfiles, .dockerignore, or docker-compose files. |
| user-invocable | false |
| autoload-when-editing | ["**/Dockerfile","**/Dockerfile.*","**/.dockerignore","**/docker-compose*.yml","**/docker-compose*.yaml"] |
| autoload-dirs | ["/Users/walid/Development/nextnode","/Users/walid/Development/saas"] |
Auto-loads when writing or reviewing any Docker-related file. Every rule is mandatory — no exceptions.
builder + runtime. No single-stage production Dockerfiles.# syntax=docker/dockerfile:1 on line 1 of every Dockerfile — unlocks BuildKit features (cache mounts, secret mounts, heredocs).node:XX-slim (Debian) as base — NOT full node:XX, NOT alpine (musl breaks native modules silently). Exception: pure JS apps with zero native deps MAY use alpine if explicitly justified.corepack prepare --activate (reads packageManager from package.json). NEVER corepack prepare pnpm@X.Y.Z --activate. IMPORTANT: corepack prepare --activate requires package.json (with packageManager field) to exist in the current directory — always COPY package.json ./ BEFORE running it in any stage.--frozen-lockfile on every pnpm install — no exceptions.pnpm.onlyBuiltDependencies MUST be set in package.json — this is the allowlist of packages permitted to run install scripts (e.g. better-sqlite3, esbuild, sharp). Everything else is blocked by default in pnpm v10+. NEVER use --ignore-scripts (breaks native module builds) or CI=true.
onlyBuiltDependencies controls dependency install/postinstall scripts (packages in node_modules), NOT the root project's lifecycle scripts (like prepare). The root prepare script (e.g. "prepare": "husky") still runs after every pnpm install.--prod skips devDependencies (husky isn't installed) but pnpm still triggers the root prepare lifecycle hook → sh: husky: not found. Fix: strip the prepare script from package.json before the prod install (scripts don't affect lockfile resolution). HUSKY=0 does NOT help here — it makes the husky command a no-op, but the binary doesn't even exist in --prod so sh fails before husky can read the env var. See prod-deps stage in canonical templates.CMD and ENTRYPOINT — CMD ["node", "dist/server.js"], never CMD node server.js.USER before CMD.docker-compose.yml.Source of truth: The
nextnode-infraskill owns all deploy-time behavior — auto-generated env vars, compose transformation, port allocation, route handling, service scaffolding, and the "What to Add vs What NOT to Add" guide. Always consultnextnode-infrawhen working on NextNode Docker/Compose files. This section covers only the Dockerfile/Compose authoring rules.
When a project uses nextnode.toml, Docker config MUST derive values from it — never hardcode what the infrastructure manages.
Dockerfile ARGs MUST use the infra CLI's _{SERVICE} suffix convention to stay consistent with the compose env vars the CLI generates. For the main app service, the suffix is _APP:
ARG APP_PORT_APP=4321
ENV PORT=$APP_PORT_APP
EXPOSE $APP_PORT_APP
The default value MUST match [deploy].port from nextnode.toml. The infra CLI generates APP_PORT_APP (and HOST_PORT_APP) into the .env at deploy time — the Dockerfile ARG name matches so there's a single naming convention across Dockerfile and compose.
For multi-service projects with [[routes]], each service gets its own suffix:
APP_PORT_APP (from [deploy].port)APP_PORT_{SERVICE} (from [[routes]].port, e.g. APP_PORT_STRAPI)services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "${HOST_PORT_APP}:${APP_PORT_APP}"
restart: unless-stopped
environment:
- NODE_ENV=${NODE_ENV}
env_file — CLI injects .env at deploy timecontainer_name — let Compose auto-nameproxy-public network — Caddy runs natively on VPShealthcheck — belongs in Dockerfilerestart: unless-stopped alwaysFor multi-service builds,
[[routes]], resource limits, compose transformation at deploy time, and the full list of infra-managed env vars — seenextnode-infraskill.
Order instructions from what changes LEAST to what changes MOST. A changed layer invalidates all subsequent layers.
1. FROM base image (changes: on security patches)
2. System packages (changes: rarely)
3. WORKDIR (changes: never)
4. package.json COPY (changes: slightly more often)
5. corepack enable + prepare (changes: when packageManager changes)
6. Non-root user creation (changes: never)
7. pnpm-lock.yaml COPY (changes: when deps change)
8. pnpm install (changes: when deps change)
9. Source files COPY (changes: every build)
10. Build command (changes: every build)
11. Runtime COPY --from=build (changes: every build)
12. ENV / LABEL / HEALTHCHECK (changes: rarely)
13. USER (changes: never)
14. ENTRYPOINT / CMD (changes: rarely)
Critical mistakes:
COPY . . before pnpm install — causes full reinstall on every source changeapt-get update && apt-get install across two RUN layers — the apt cache layer goes staleCOPY package.json pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile
The BuildKit cache mount persists the pnpm store across builds — repeat installs with unchanged lockfile resolve from cache instantly.
WARNING —
pnpm fetch+--offlineis broken. The cache mount target/pnpm/storedoes NOT match pnpm's actual default store path (~/.local/share/pnpm/store).pnpm fetchwrites to the default store, the cache mount persists an empty/pnpm/store, andpnpm install --offlinesilently installs nothing from the empty mount. NEVER usepnpm fetch/--offlinewith cache mounts — use directpnpm install --frozen-lockfileinstead.
BuildKit runs independent stages concurrently:
FROM base AS prod-deps
COPY pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile --prod
FROM base AS build
COPY pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile
COPY tsconfig*.json ./
COPY src/ ./src/
RUN pnpm build
prod-deps and build execute in parallel — total build time is max(stage A, stage B), not sum.
Always use --mount=type=cache for package managers:
# pnpm store
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile
# apt (needs sharing=locked)
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y --no-install-recommends tini \
&& rm -rf /var/lib/apt/lists/*
Copy only what the build needs — prevents cache busting from unrelated file changes:
# Build stage — copy only build inputs
COPY tsconfig*.json ./
COPY src/ ./src/
COPY public/ ./public/
RUN pnpm build
For CI (ephemeral runners), use registry-backed cache:
docker buildx build \
--cache-from type=registry,ref=ghcr.io/org/app:buildcache \
--cache-to type=registry,ref=ghcr.io/org/app:buildcache,mode=max \
--tag ghcr.io/org/app:latest \
--push .
mode=max exports ALL intermediate layers — critical for multi-stage cache hits.
node:XX-slim| Base | Size | CVEs | Shell | Recommendation |
|---|---|---|---|---|
node:22 | ~1 GB | High | Yes | NEVER in production |
node:22-slim | ~220 MB | Low | Yes | Default choice |
node:22-alpine | ~55 MB | Very low | sh | Only pure JS apps |
distroless/nodejs22 | ~100 MB | Near zero | No | Maximum security |
ALWAYS use --prod in the runtime install or copy from a dedicated prod-deps stage:
COPY --from=prod-deps /app/node_modules ./node_modules
NEVER ship devDependencies to production.
# Runtime stage receives ONLY:
COPY --from=prod-deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY package.json ./
# NEVER copy: src/, tsconfig.json, test files, docs, .git
# CORRECT — single layer, no bloat
RUN apt-get update && apt-get install -y --no-install-recommends tini \
&& rm -rf /var/lib/apt/lists/*
# WRONG — rm runs in new layer, old layer still has cache
RUN apt-get install -y tini
RUN rm -rf /var/lib/apt/lists/*
--no-install-recommendsPrevents apt from pulling suggested packages — cuts installed size 30-50%.
RUN find / -type f -perm /6000 -exec chmod -s {} \; 2>/dev/null || true
Removes setuid/setgid binaries that could enable privilege escalation.
# Debian slim
RUN groupadd -g 1001 appgroup \
&& useradd -u 1001 -g appgroup --no-log-init --create-home appuser
# Alpine
RUN addgroup -g 1001 -S appgroup \
&& adduser -S -u 1001 -G appgroup appuser
Use COPY --chown=appuser:appgroup to set ownership at copy time — cheaper than a separate RUN chown.
Switch to non-root AFTER all file operations, BEFORE CMD:
USER appuser
ENTRYPOINT ["/usr/bin/tini", "-g", "--"]
CMD ["node", "dist/server.js"]
# WRONG — visible in docker history
ARG NPM_TOKEN
RUN echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
# CORRECT — ephemeral, never in any layer
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
pnpm install --frozen-lockfile
Rules:
ARG or ENV for secretsCOPY .npmrc then RUN rm .npmrc — the file exists in the intermediate layer--mount=type=secret for private registries, tokens, credentialsAlways use COPY for local files. ADD is only acceptable for auto-extracting tar archives with checksum validation.
RUN apt-get update && apt-get install -y --no-install-recommends tini \
&& rm -rf /var/lib/apt/lists/*
ENTRYPOINT ["/usr/bin/tini", "-g", "--"]
CMD ["node", "dist/server.js"]
On Alpine: RUN apk add --no-cache tini → /sbin/tini.
Use Node.js built-in HTTP — never install curl just for health checks:
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
CMD ["node", "-e", "require('http').get('http://localhost:' + (process.env.PORT || 4321) + '/health', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
Parameters:
--start-period: set to actual app startup time (grace before failures count)--interval: 30s is standard--retries: 3 before marking unhealthyEnforce in compose or docker run:
services:
app:
read_only: true
tmpfs:
- /tmp:rw,noexec,nosuid,size=64m
services:
app:
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
ARG BUILD_DATE
ARG VCS_REF
ARG APP_VERSION
LABEL org.opencontainers.image.created="${BUILD_DATE}" \
org.opencontainers.image.revision="${VCS_REF}" \
org.opencontainers.image.version="${APP_VERSION}" \
org.opencontainers.image.source="https://github.com/org/app" \
org.opencontainers.image.title="App Name" \
org.opencontainers.image.licenses="MIT"
# syntax=docker/dockerfile:1
ARG NODE_VERSION=22
ARG APP_PORT_APP=4321
# ── Base ─────────────────────────────────────────────────
FROM node:${NODE_VERSION}-slim AS base
WORKDIR /app
COPY package.json ./
RUN corepack enable && corepack prepare --activate
ENV PNPM_HOME="/pnpm" \
PATH="/pnpm:$PATH"
# ── Prod deps ────────────────────────────────────────────
FROM base AS prod-deps
COPY pnpm-lock.yaml ./
# Strip "prepare" script: runs `husky` (devDep, not in --prod).
# Scripts don't affect lockfile resolution.
RUN node -e "const p=JSON.parse(require('fs').readFileSync('package.json','utf8'));delete p.scripts.prepare;require('fs').writeFileSync('package.json',JSON.stringify(p))"
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile --prod
# ── Build ────────────────────────────────────────────────
FROM base AS build
COPY pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile
COPY tsconfig*.json ./
COPY src/ ./src/
RUN pnpm build
# ── Runtime ──────────────────────────────────────────────
FROM node:${NODE_VERSION}-slim AS runtime
RUN apt-get update && apt-get install -y --no-install-recommends tini \
&& rm -rf /var/lib/apt/lists/*
RUN groupadd -g 1001 appgroup \
&& useradd -u 1001 -g appgroup --no-log-init --create-home appuser
WORKDIR /app
COPY --chown=appuser:appgroup --from=prod-deps /app/node_modules ./node_modules
COPY --chown=appuser:appgroup --from=build /app/dist ./dist
COPY --chown=appuser:appgroup package.json ./
ARG APP_PORT_APP
ENV NODE_ENV=production \
HOST=0.0.0.0 \
PORT=${APP_PORT_APP}
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
CMD ["node", "-e", "require('http').get('http://localhost:' + (process.env.PORT || 4321) + '/health', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
EXPOSE ${APP_PORT_APP}
USER appuser
ENTRYPOINT ["/usr/bin/tini", "-g", "--"]
CMD ["node", "dist/server.js"]
Replace the CMD and add Astro-specific env:
ENV NODE_ENV=production \
HOST=0.0.0.0 \
PORT=${APP_PORT_APP} \
ASTRO_TELEMETRY_DISABLED=1
CMD ["node", "dist/server/entry.mjs"]
When PUBLIC_* vars must be inlined at build time:
FROM base AS build
COPY pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile
# PUBLIC_ vars inlined by Vite at build time
ARG PUBLIC_SUPABASE_URL
ARG PUBLIC_SUPABASE_ANON_KEY
ARG PUBLIC_SITE_URL
ENV PUBLIC_SUPABASE_URL=$PUBLIC_SUPABASE_URL \
PUBLIC_SUPABASE_ANON_KEY=$PUBLIC_SUPABASE_ANON_KEY \
PUBLIC_SITE_URL=$PUBLIC_SITE_URL
COPY tsconfig*.json ./
COPY src/ ./src/
COPY public/ ./public/
RUN pnpm build
These are NOT secrets — they are public client-side values embedded in the JS bundle.
Prerequisite: package.json MUST declare pnpm.onlyBuiltDependencies — this is the allowlist of packages permitted to run install/build scripts. Without it, pnpm v10+ blocks all lifecycle scripts by default.
{
"pnpm": {
"onlyBuiltDependencies": [
"better-sqlite3",
"esbuild",
"sharp"
]
}
}
When native addons need compilation tools, install them in the builder stage only:
FROM base AS build
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 make g++ \
&& rm -rf /var/lib/apt/lists/*
# ... rest of build stage
# These packages are NOT in the runtime stage
--ignore-scripts is NEVER needed — onlyBuiltDependencies handles script control declaratively. Using --ignore-scripts would override the allowlist and break native module compilation.
# syntax=docker/dockerfile:1
ARG NODE_VERSION=22
ARG APP_PORT_APP=3000
# ── Base ─────────────────────────────────────────────────
FROM node:${NODE_VERSION}-slim AS base
WORKDIR /app
COPY package.json ./
RUN corepack enable && corepack prepare --activate
ENV PNPM_HOME="/pnpm" \
PATH="/pnpm:$PATH"
# ── Install + Build ─────────────────────────────────────
FROM base AS build
COPY pnpm-lock.yaml pnpm-workspace.yaml turbo.json ./
COPY apps/api/package.json ./apps/api/
COPY apps/web/package.json ./apps/web/
COPY packages/shared/package.json ./packages/shared/
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile
COPY . .
RUN pnpm turbo build
# ── Prod deps ────────────────────────────────────────────
FROM base AS prod-deps
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
COPY apps/api/package.json ./apps/api/
COPY packages/shared/package.json ./packages/shared/
# Strip "prepare" script: runs `husky` (devDep, not in --prod).
RUN node -e "const p=JSON.parse(require('fs').readFileSync('package.json','utf8'));delete p.scripts.prepare;require('fs').writeFileSync('package.json',JSON.stringify(p))"
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile --prod
# ── Runtime ──────────────────────────────────────────────
FROM node:${NODE_VERSION}-slim AS runtime
RUN apt-get update && apt-get install -y --no-install-recommends tini \
&& rm -rf /var/lib/apt/lists/*
RUN groupadd -g 1001 appgroup \
&& useradd -u 1001 -g appgroup --no-log-init --create-home appuser
WORKDIR /app
COPY --chown=appuser:appgroup --from=prod-deps /app/node_modules ./node_modules
COPY --chown=appuser:appgroup --from=prod-deps /app/apps/api/node_modules ./apps/api/node_modules
COPY --chown=appuser:appgroup --from=prod-deps /app/packages/shared/node_modules ./packages/shared/node_modules
COPY --chown=appuser:appgroup --from=build /app/apps/api/dist ./apps/api/dist
COPY --chown=appuser:appgroup --from=build /app/apps/web/dist ./apps/web/dist
COPY --chown=appuser:appgroup --from=build /app/packages/shared/dist ./packages/shared/dist
COPY --chown=appuser:appgroup package.json ./
ARG APP_PORT_APP
ENV NODE_ENV=production \
HOST=0.0.0.0 \
PORT=${APP_PORT_APP}
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
CMD ["node", "-e", "require('http').get('http://localhost:' + (process.env.PORT || 3000) + '/health', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
EXPOSE ${APP_PORT_APP}
USER appuser
ENTRYPOINT ["/usr/bin/tini", "-g", "--"]
CMD ["node", "apps/api/dist/index.js"]
Monorepo alternative — pnpm deploy: For cleaner isolation, use pnpm deploy --filter=api --prod /prod/api to extract a single workspace with only its production deps into an isolated directory.
Every project with a Dockerfile MUST have a .dockerignore:
# Version control
.git
.gitignore
.gitattributes
.github/
# Dependencies (rebuilt inside container)
node_modules/
.pnpm-store/
# Build artifacts (rebuilt inside container)
dist/
build/
.astro/
.next/
.nuxt/
out/
# TypeScript cache
*.tsbuildinfo
# Test output
coverage/
.nyc_output/
# Logs
*.log
pnpm-debug.log*
# Environment (NEVER in image)
.env
.env.*
!.env.example
.envrc
# Credentials (NEVER in image)
.npmrc
*.pem
*.key
# Editor/OS
.vscode/
.idea/
.DS_Store
*.swp
# Documentation
*.md
docs/
LICENSE
CHANGELOG
# Claude / AI
.claude/
# Docker files (already in context)
Dockerfile
Dockerfile.*
.dockerignore
docker-compose*.yml
docker-compose*.yaml
| # | Anti-Pattern | Correct Approach |
|---|---|---|
| 1 | corepack prepare pnpm@X.Y.Z --activate | corepack prepare --activate — reads packageManager from package.json |
| 2 | --ignore-scripts / ENV CI=true | pnpm.onlyBuiltDependencies allowlist in package.json — blocks all dependency scripts except listed packages. For the root prepare lifecycle (husky) in prod-deps stage, strip the script before install (see canonical templates). Never use --ignore-scripts (breaks native module builds). |
| 3 | COPY . . before pnpm install | Copy pnpm-lock.yaml first, then package.json, then install, then source |
| 4 | CMD npm start or CMD pnpm start | CMD ["node", "dist/server.js"] — exec form, direct node invocation. Exception: frameworks that need their CLI for bootstrapping (e.g. Strapi needs pnpm start for migration running, TypeScript detection). When using pnpm in CMD, corepack + PNPM_HOME must also be configured in the runtime stage |
| 5 | Running as root in production | Create dedicated user, USER appuser before CMD |
| 6 | Node.js as PID 1 | ENTRYPOINT ["/usr/bin/tini", "-g", "--"] |
| 7 | Installing curl just for HEALTHCHECK | Use node -e "require('http')..." |
| 8 | ADD for local files | COPY always — ADD only for tar extraction |
| 9 | ARG NPM_TOKEN for secrets | --mount=type=secret,id=npmrc,target=/root/.npmrc |
| 10 | Separate RUN apt-get update and RUN apt-get install | Single RUN with && — prevents stale apt cache |
| 11 | Missing .dockerignore | Always create — prevents sending .git, node_modules, .env to daemon |
| 12 | EXPOSE 4321 with hardcoded value | ARG APP_PORT_APP=4321 + EXPOSE ${APP_PORT_APP} — use infra CLI's _{SERVICE} suffix convention |
| 13 | RUN chown -R user:group /app as separate layer | COPY --chown=user:group at copy time |
| 14 | Full node:22 as runtime base | node:22-slim — 1 GB vs 220 MB |
| 15 | pnpm fetch + pnpm install --offline with cache mounts | Cache mount at /pnpm/store doesn't match pnpm's default store path — --offline finds an empty store and silently installs nothing. Use pnpm install --frozen-lockfile with cache mounts directly |
| 16 | Copying devDependencies to runtime | Use --prod on install or separate prod-deps stage |
| 17 | Missing # syntax=docker/dockerfile:1 | Always first line — enables cache mounts, secret mounts |
| 18 | pnpm install without --frozen-lockfile | Always --frozen-lockfile — deterministic builds |
When reviewing any Dockerfile, verify ALL of the following:
Structure:
# syntax=docker/dockerfile:1 on line 1node:XX-slim base (or justified alpine)corepack prepare --activate (no version pinning) — package.json with packageManager must be COPYed BEFORE this commandBuild optimization:
pnpm install --frozen-lockfile with BuildKit cache mounts (--mount=type=cache) — NO pnpm fetch/--offlineCOPY . . before install)Size optimization:
node_modules in runtimedist/ + node_modules/ + package.json in runtime--no-install-recommends on apt-getRUN layer as installSecurity:
COPY --chown instead of RUN chownCOPY over ADDpnpm.onlyBuiltDependencies set in package.json (NO --ignore-scripts)Runtime:
NODE_ENV=productionARG APP_PORT_APP matches [deploy].port from nextnode.toml — uses infra CLI's _{SERVICE} suffixEXPOSE uses ${APP_PORT_APP} (not hardcoded)Files:
.dockerignore exists and excludes .git, node_modules, .env, .npmrcBuildx with docker-container driver is REQUIRED — enables --mount=type=cache, --mount=type=secret, and multi-platform builds.
docker buildx create \
--name nextnode-builder \
--driver docker-container \
--platform linux/amd64,linux/arm64 \
--bootstrap \
--use
--driver docker-container — BuildKit in a dedicated container, full feature set--platform linux/amd64,linux/arm64 — cross-compile (deploy = amd64, dev may be arm64)--bootstrap --use — pull image, start, set as defaultDo NOT pass --buildkitd-flags '--oci-worker-no-process-sandbox' unless rootless Docker — causes boot loop otherwise.
# Single-platform → local daemon
docker buildx build --load -t myapp:latest .
# Multi-platform → registry (--load only works single-arch)
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t ghcr.io/org/app:latest \
--push .
# With registry cache
docker buildx build \
--cache-from type=registry,ref=ghcr.io/org/app:buildcache \
--cache-to type=registry,ref=ghcr.io/org/app:buildcache,mode=max \
--platform linux/amd64,linux/arm64 \
-t ghcr.io/org/app:latest \
--push .
docker buildx prune # clear cache
docker buildx stop nextnode-builder # stop
docker buildx inspect --bootstrap nextnode-builder # restart
docker buildx rm nextnode-builder # remove
Deep interview for a single feature or module brief that produces a final spec or implementation plan.
@nextnode-solutions/logger standards. Reference this skill when a project uses @nextnode-solutions/logger or when logger integration is being added.
Manage n8n workflows on the NextNode automation instance with the local n8n-cli tool.
NextNode brand guidelines — color palette, typography, logo system, and per-project branding rules. Use when doing UI/frontend work on a NextNode project.
NextNode ecosystem hub. Auto-load when working on any NextNode or SaaS project — covers nextnode.toml config, CI workflows, docker-compose rules, and cross-references to package skills.
Audit a NextNode/SaaS project against all NextNode standards — produces a compliance report with pass/fail/missing status for every required item.