Dockerfile generator

Choose a runtime and get a multi-stage Dockerfile and a .dockerignore that run as a non-root user, pin their base images and cache dependencies, with every line explained. The Dockerfile Linter checks the result right here.

A Node.js server: dependencies installed from the lockfile, then the source.
Turn it off when an orchestrator or the Compose file checks health.
Comments above each instruction, in the page's language. Turn off for a bare file.

What it does

It writes a Dockerfile and a .dockerignore for five common runtimes, following Docker's own best practices: a build stage and a lean final image, dependencies installed from the lockfile before the source is copied so the cache survives code changes, BuildKit cache mounts that speed up builds without growing the image, base images pinned to a version tag, a non-root user, a HEALTHCHECK, and CMD or ENTRYPOINT in exec form so the process gets stop signals. Every line comes with a comment that says what it does and why, and the result is checked by the Dockerfile Linter on this page.

How it works

  1. The files are written on this page from typed templates; nothing is sent anywhere and nothing is built or pulled.
  2. Each runtime offers the versions it supports, pinned to version tags read from the official registries; the defaults are the current long-term lines.
  3. The output is checked by the Dockerfile Linter's rules with the .dockerignore it comes with, and the findings, normally none, are shown under the files.
  4. Before the page was published, every runtime and option combination in the generator's tests was built and run on a hello-world app with Docker: it built, ran as a non-root user, reported healthy and stopped on SIGTERM.

What each runtime gets

Node.js
node:<version>-alpine, npm ci or pnpm install --frozen-lockfile without dev dependencies in a separate stage, NODE_ENV=production, the image's node user, and a wget health check.
Python
python:<version>-slim, a virtual environment built with pip from requirements.txt or with uv from uv.lock, a dedicated user with a fixed uid, and a health check with Python's urllib.
Go
golang:<version>-alpine builds a static binary with cached modules; it runs on distroless (nonroot, no shell) or Alpine with a dedicated user and a wget health check.
Static site
Built with npm or pnpm on Node.js 24, or copied as is, and served by nginxinc/nginx-unprivileged on port 8080 as uid 101.
Java (Maven)
Maven with a full JDK resolves dependencies into a cache and builds the jar; it runs on eclipse-temurin:<version>-jre-alpine as a dedicated user, with the heap sized to the container.

Example

A Node.js service with npm and the defaults, written by the generator with its explanations:

Dockerfile
# syntax=docker/dockerfile:1
# The line above must stay first: it makes BuildKit read this file with the
# current Dockerfile syntax, which the cache mounts below need.

# Stage 1 installs the dependencies in node:24-alpine, a version tag rather than
# latest, so a rebuild uses the same Node.js line.
FROM node:24-alpine AS deps
# Work in /app in this stage.
WORKDIR /app
# Only package.json and package-lock.json first: this layer, and the install
# after it, are rebuilt only when the dependencies change.
COPY package.json package-lock.json ./
# Installs exactly what the lockfile says, without devDependencies. The npm
# cache is a BuildKit cache mount, so it speeds up the next build without
# staying in the image.
RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev

# The final image starts again from node:24-alpine, without the build stage's
# caches and tools.
FROM node:24-alpine
# Production mode for Node.js libraries that read NODE_ENV (Express, for
# example, then caches views and hides stack traces).
ENV NODE_ENV=production
# The port the application should listen on. Many frameworks read PORT; if yours
# does not, set the port in its configuration to 3000.
ENV PORT=3000
# The application lives in /app.
WORKDIR /app
# The installed dependencies from stage 1. The package manager and its cache
# stay behind.
COPY --from=deps /app ./
# The source, after the dependencies so a code change does not reinstall them.
# The files stay owned by root, so the application cannot change its own code;
# .dockerignore keeps .git, .env and local builds out.
COPY . .
# The official Node.js image has an unprivileged user, node (uid 1000); the
# process runs as it, not as root.
USER node
# Documents that the application listens on port 3000. It publishes nothing:
# docker run -p or Compose ports: does.
EXPOSE 3000
# Docker marks the container unhealthy when http://127.0.0.1:3000/ stops
# answering. It uses BusyBox wget, which the image already has.
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
  CMD ["wget", "-q", "--spider", "http://127.0.0.1:3000/"]
# Starts server.js in exec form: the process is PID 1 and receives SIGTERM on
# docker stop.
CMD ["node", "server.js"]
.dockerignore
# Git history: large, and it can hold secrets that were removed from the code.
.git

# Environment files and keys: COPY . . would otherwise put them in the image,
# where anyone who pulls it can read them.
.env
.env.*
*.pem
*.key

# The Docker files themselves: the application does not need them, and a change
# to them would invalidate the source layer.
Dockerfile
.dockerignore
compose*.y*ml
docker-compose*.y*ml

# Logs and editor files.
*.log
.DS_Store
.idea
.vscode

# Dependencies installed on your machine: the image installs its own, for its
# own platform.
node_modules
npm-debug.log*
coverage

The Dockerfile Linter's findings on it:

Findings

Error0
High0
Medium0
Low0
Info0

0 findings: 0 error, 0 high, 0 medium, 0 low, 0 info.

No findings.

Limits

  • It writes the files; it does not build them, and it does not read your repository to detect the runtime or the framework.
  • Your project must match what the Dockerfile expects (a lockfile, the file to run, a build script); the notes under the output list it for your choices.
  • Tags such as node:24-alpine receive security updates within their version line. To freeze an exact image, add its digest and update it on purpose.
  • One service per Dockerfile: monorepos, native build dependencies, private registries and build secrets need changes the generator does not make.

Check your own Dockerfile with the linter

Methodology: how the numbers are worked out

Sources

  1. Docker Docs: Dockerfile reference
  2. Docker Docs: Building best practices
  3. Docker Docs: Build checks
  4. Docker Docs: Build secrets
  5. Docker Docs: Build context and .dockerignore
  6. Docker Docs: Multi-stage builds
  7. Docker Docs: Optimize cache usage
  8. Node.js: Release schedule
  9. Python: Status of Python versions
  10. Go: Release policy
  11. uv: Using uv in Docker
  12. Distroless container images
  13. nginx: unprivileged image
  14. Eclipse Temurin: Docker images

Updated

Next step

Dockerfile linter

A free public tool by ANAROQ, a company building compute infrastructure. What ANAROQ is building