Dockerfile Generator
Multi-stage Dockerfiles for Node, Python, Go and Java, running as a non-root user.
● Runs 100% in your browser — nothing you type leaves this page.
What you’d run on a server, e.g. node server.js or node dist/index.js.
An HTTP path that answers 200 when the app is OK. Empty = no HEALTHCHECK.
# syntax=docker/dockerfile:1# Generated by OhSudo — https://ohsudo.com/tools/dockerfile-generator/# Build: docker build -t myapp .# Production dependencies only. package.json and the lock file are copied first, so this layer stays cached until they changeFROM node:24-alpine AS depsWORKDIR /appCOPY package.json package-lock.json ./RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev# Final image: only what the app needs to runFROM node:24-alpineENV NODE_ENV=production PORT=3000WORKDIR /appCOPY --from=deps /app/node_modules ./node_modulesCOPY . .# Run as an unprivileged user, not root. A numeric UID lets Kubernetes runAsNonRoot verify itUSER 1000EXPOSE 3000# Docker marks the container unhealthy if this fails (uses the runtime, as slim images have no curl)HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \CMD ["node", "-e", "fetch('http://127.0.0.1:3000/health').then((r) => process.exit(r.ok ? 0 : 1), () => process.exit(1))"]# Exec form (JSON array): node gets SIGTERM on docker stop. As PID 1 it ignores it unless# your app handles it (process.on("SIGTERM", …)), so run with docker run --init, or init: true in ComposeCMD ["node", "server.js"]
Build and run it
$ docker build -t myapp .$ docker run --rm --init -p 3000:3000 myapp$ docker ps # STATUS shows (healthy) after the first checkAdd a .dockerignore first, so node_modules, .venv, .git and .env never reach the image. --init (Node.js) adds a tiny init process that passes signals on, so docker stop is instant. .dockerignore →
The address bar updates as you go, so you can bookmark or share the result.
How to use it
- Pick the language and version. For Node.js, choose Alpine (smallest) or Debian slim (when native modules need glibc).
- Enter the command that starts your app (Node.js, Python), or the package to build (Go). Java builds the jar with Maven.
- Set the port, and a health check path if your app has one.
- Save the result as
Dockerfilenext to your code, add a .dockerignore, and rundocker build -t myapp .. If aCOPYline then fails with “not found”, see failed to compute cache key.
What you get
- Multi-stage builds. Compilers, dev dependencies and build caches stay in the build stage; the final image gets only what runs. Go ends up on a distroless image with just the binary.
- A non-root user. If the app is ever compromised, the attacker isn’t root in the container — and much further from root on the host.
- Layers in the right order. The dependency files are copied and installed before your code, so a code change doesn’t reinstall every package. BuildKit cache mounts keep downloads between builds.
- Exec-form
CMD(["node", "server.js"]), so your app — not a shell — receivesSIGTERMand can shut down cleanly. A shell in between is the classic reason for exit code 137 after docker stop. - A health check that works on slim images, using Node.js or Python itself instead of
curl, which those images don’t have. - A JVM sized for the container:
-XX:MaxRAMPercentage=75sets the heap from the container’s memory limit.
Every combination of options is checked automatically: linted with hadolint, then built and run with a small test app, which must answer on its health path, run as a non-root user, report healthy and stop within a few seconds of docker stop.
Common commands
docker build -t myapp . # build (BuildKit is the default since Docker 23)docker run --rm --init -p 3000:3000 myapp # run; --init passes signals on (needed for Node.js)docker ps # STATUS shows (healthy) / (unhealthy)docker image history myapp # which layer is how bigdocker image inspect -f '{{.Config.User}}' myapp # which user it runs asdocker build --no-cache -t myapp . # rebuild everything, ignoring the cacheWarning
Never put secrets in a Dockerfile — not in ENV, ARG or a copied .env. They stay in the image layers, and anyone who can pull the image can read them. Pass them at runtime (docker run -e, Compose env_file, Kubernetes Secrets), or use RUN --mount=type=secret when the build itself needs one.
FAQ
Why does docker stop take 10 seconds with my Node.js app?
As PID 1, a process gets no default signal handling from the kernel, and Node.js doesn’t install a SIGTERM handler of its own. docker stop sends SIGTERM, nothing happens, and after 10 seconds Docker kills it (exit code 137). Handle the signal in your app (process.on('SIGTERM', …): close the server, then exit), or run the container with --init (init: true in Compose). Gunicorn, Go programs and the JVM handle SIGTERM on their own.
Alpine or slim?
Alpine is smaller but uses musl instead of glibc. Pure JavaScript apps don’t care; npm packages with native code (image processing, some database drivers) may need to compile or may not work. If in doubt, use slim. For Python the generator always uses slim: packages with native code are published for glibc first; on Alpine (musl) some have no ready-made wheel and must be compiled, which needs a compiler and makes builds slow.
Why is npm start a bad CMD?
It starts npm, which starts your app as a child process; SIGTERM reaches npm, which may not pass it on, and npm adds memory and startup time. Run node directly.
My app can’t write files any more. Why?
The code is copied as root and the app runs as a non-root user, so it can read the code but not change it — which is the point. Write to a volume, or create a folder for the app user in the Dockerfile (RUN mkdir /app/data && chown node /app/data before USER).
How do I build it automatically?
The CI/CD Pipeline Generator writes a GitHub Actions or GitLab CI pipeline that tests your code, then builds this Dockerfile and pushes the image on every change.
Can I use this with Docker Compose?
Yes. Use build: . instead of image: for your service in the Docker Compose Generator output, and Compose builds this Dockerfile. If your app can’t reach the database from inside the container, see Docker: connection refused to localhost.
npm fails with EACCES during the build
Usually the Dockerfile switches to a non-root user too early. See npm “EACCES: permission denied”.
The container says “exec format error”
Usually an image built for another CPU architecture, or a script with no #! line. See exec format error. On Alpine, see also not found for a binary that exists.
The container says “executable file not found in $PATH”
The program isn’t in the image, or CMD was written as one string, ["npm start"]. The generator writes the exec form, one string per argument. See executable file not found in $PATH.
Articles
Learn more
yum "Cannot find a valid baseurl for repo": CentOS 7 reached end of life, so use the vault
Fix yum "Cannot find a valid baseurl for repo: base/7/x86_64" and the CentOS 8 mirrorlist error: point the repositories at vault.centos.org, then plan a migration.
Beginner · 3 min read
Docker "pull access denied": the repository doesn't exist, or you're not logged in
Fix Docker "pull access denied, repository does not exist or may require docker login": check the image name and tag, log in to the right registry, handle rate limits.
Beginner · 4 min read
Alpine "not found" for a binary that's right there: glibc vs musl
A binary exists on Alpine, yet running it says "not found" or "no such file or directory": it was built for glibc. Check with ldd, then rebuild it or use gcompat.
Intermediate · 4 min read
Docker "exec format error": the wrong CPU architecture, or a script with no shebang
Fix Docker "exec /app: exec format error": an image built for another architecture (arm64 vs amd64) or a script with no shebang. Check it, then build with buildx.
Intermediate · 4 min read
"error while loading shared libraries" and "GLIBC_2.xx not found": what the loader is telling you
Fix Linux "error while loading shared libraries: cannot open shared object file" and "version GLIBC_2.xx not found": use ldd, install the library, run ldconfig, rebuild.
Intermediate · 5 min read
Nginx for Next.js: a production config with HTTPS, caching and standalone builds
Run Next.js behind Nginx: a complete config with HTTPS and long caching for _next/static, the standalone build, a systemd service, and the errors people run into.
Intermediate · 3 min read
npm "EACCES: permission denied": fix it without sudo
Fix npm EACCES permission denied on global installs and in projects: use a version manager or your own prefix, repair root-owned files, and avoid sudo.
Beginner · 3 min read
Docker build "failed to compute cache key: … not found": why COPY can't see your file
Fix "failed to compute cache key … not found" and "COPY failed": the file is outside the build context, excluded by .dockerignore, or missing in CI.
Beginner · 4 min read
Kubernetes "container has runAsNonRoot and image will run as root": fix the image or the pod
CreateContainerConfigError with runAsNonRoot: the image runs as root or has a non-numeric USER. Fix the Dockerfile, set runAsUser, and keep restricted security.
Intermediate · 4 min read
Docker: "connection refused" to localhost from a container — how to reach the host
Fix "connection refused" when a container connects to localhost: use host.docker.internal or a Compose service name, make the host service listen, and check the firewall.
Beginner · 4 min read
Docker "exited with code 137": OOM kill or a stop that took too long
Exit code 137 means the container was killed with SIGKILL. Find out if it ran out of memory or ignored docker stop, and fix each case.
Intermediate · 6 min read