Dockerfile Generator — Build Dockerfiles and Compose Files
Draft a Dockerfile or a docker-compose.yaml for thirteen common stacks, then review and adapt it before you build. 100% client-side — your config stays private.
What is a Dockerfile? A Dockerfile is a plain-text script of build instructions that Docker reads from top to bottom to assemble a container image. Each instruction — FROM, WORKDIR, COPY, RUN, ENV, EXPOSE, CMD — adds a cached layer, and the build halts at the first one that fails. The file has no extension and normally sits at the root of the directory you pass to docker build.
How to Use the Dockerfile Generator
- Pick a language or framework — The dropdown covers eight application runtimes (Node.js, Python, Go, Rust, Java, .NET, Ruby, PHP) and five service images (Nginx, Apache, Redis, PostgreSQL, MongoDB). The choice decides the base image, the working directory and which dependency manifest gets copied.
-
Choose a base image variant — Alpine, Slim, Distroless and Standard change only the tag on the
FROMline — the rest of the file is identical. Not every stack publishes all four tags, so confirm the image exists on your registry before building. -
List the ports to expose — Enter them comma-separated, as in
3000,8080. Each becomes its ownEXPOSEline.EXPOSEis documentation for humans and fordocker run -P; it does not publish anything by itself. -
Add environment variables — Use
KEY=valuepairs separated by commas, such asNODE_ENV=production,PORT=3000. Only pairs containing exactly one=are kept, so a value that itself contains an equals sign is silently dropped. - Toggle multi-stage if you compile — Multi-stage applies to the compiled stacks — Go, Rust, Java and .NET. Go and Rust always build in two stages regardless of the checkbox, because their configuration is marked multi-stage on its own.
-
Generate, then fill in the gaps — Copy or Download the result — the file is saved as
Dockerfile, ordocker-compose.yamlwhen the output starts with a composeversion:key. Read the checklist below before you build: the draft is a skeleton, not a finished image.
What the Generator Produces
The output is assembled from a fixed template per stack rather than from anything it learns about your project. Every stack maps to a base image, a working directory, a dependency manifest to copy and an install command; your ports and environment variables are appended as extra lines. Knowing which instruction comes from where makes the draft much easier to edit.
| Instruction | What it does | What the generator puts there |
|---|---|---|
FROM | Names the base image the build starts from | The language image plus the variant tag, for example python:alpine or golang:1.22 |
WORKDIR | Sets the directory for every later instruction | /app for application runtimes, the document root for Nginx and Apache, the data directory for Redis, PostgreSQL and MongoDB |
COPY | Copies files from the build context into the image | The dependency manifest only — package*.json, requirements.txt, go.mod go.sum, Cargo.toml, pom.xml, *.csproj, Gemfile or composer.json |
RUN | Executes a command and commits the result as a layer | The install step: npm ci, pip install, go mod download, bundle install, composer install, dotnet restore |
ENV | Sets an environment variable for build and runtime | One line for each valid KEY=value pair you entered |
EXPOSE | Records which port the process listens on | One line per port from the ports field |
CMD | The default command when a container starts | The conventional entry point for interpreted stacks and service images — but nothing at all for Go, Rust, Java and .NET |
Two deliberate omissions to fix before building. The draft copies the dependency manifest but never the rest of your source, so add a COPY . . after the install step. And the four compiled stacks arrive without a CMD or ENTRYPOINT, so add the line that starts your binary or jar.
Choosing a Base Image Variant
The variant dropdown is the single biggest lever on image size, and the trade-off is always the same: the smaller the base, the fewer tools you have when something goes wrong inside the container.
| Variant | What you get | What it costs you |
|---|---|---|
| Alpine | A base of a few megabytes built on musl libc and BusyBox, with the apk package manager and a shell | musl is not glibc. Native extensions, prebuilt Python wheels and anything expecting glibc behaviour may need compiling from source or may not work at all |
| Slim | Debian with the documentation, headers and optional packages stripped out; glibc, apt and a full shell | Tens of megabytes larger than Alpine, and you still ship a package manager inside the runtime image |
| Distroless | The language runtime and its libraries, nothing else — no shell, no package manager, no ls | You cannot docker exec into a shell to debug, and anything your app shells out to must be added explicitly |
| Standard | The full distribution image with compilers and build tools already present | The largest by a wide margin. Sensible as a build stage, wasteful as a runtime stage |
A practical rule: use Standard or Slim for the stage that compiles, and Alpine or Distroless for the stage that runs. If a dependency fails to install on Alpine, moving to Slim usually costs less time than fighting musl.
Layer Caching and Instruction Order
Every instruction in a Dockerfile creates a layer, and Docker caches each one against a key derived from the
instruction text and, for COPY and ADD, the contents of the files involved. On a rebuild
the daemon walks the file from the top reusing cached layers until it reaches the first instruction whose key has
changed — from that point on, every remaining layer is rebuilt.
That single rule explains the shape of almost every well-written Dockerfile. Dependencies change rarely; source code changes constantly. So the manifest is copied and installed first, in its own pair of instructions, and the source is copied afterwards:
COPY package*.json ./ → RUN npm ci → COPY . . → CMD [...]
Edit a source file with that ordering and only the last two layers rebuild; the install layer is reused untouched.
Reverse it — copy everything first, install second — and a one-character change to a comment reinstalls every
dependency from scratch. A .dockerignore file matters here too: without one, COPY . . pulls
node_modules, .git and build output into the context, which both bloats the image and busts
the cache on files you never meant to ship. The
.gitignore Generator produces a good starting list to adapt.
Multi-stage builds
A multi-stage Dockerfile uses more than one FROM. The first stage is named with AS builder
and does the compiling; the second starts from a clean base and pulls in only the finished artifact with
COPY --from=builder. Everything left behind in the builder — the compiler, the source, the package cache
— never reaches the final image. For a compiled language this routinely takes a runtime image from several hundred
megabytes down to tens.
The generator writes the Go runtime stage on gcr.io/distroless/static-debian12, which is the right
destination for a statically linked CGO_ENABLED=0 binary. For Rust, Java and .NET it reuses the Alpine
build image as the runtime stage, so you are still shipping a compiler unless you change that line yourself — swap in
a JRE-only image for Java or the ASP.NET runtime image for .NET and the second stage does its job properly.
Review Checklist Before You Build
The draft is a skeleton meant to be edited. These are the gaps worth closing on the way to something you would run in production.
- Copy your source. Add
COPY . .after the install step; only the manifest is copied for you. - Add a start command. Go, Rust, Java and .NET drafts contain no
CMDorENTRYPOINT. - Drop root. Nothing in the output adds a
USERinstruction, so the container runs as root. Create an unprivileged user and switch to it before theCMD. - Pin the tag.
node:alpinefollows whatever the latest release is. Pin a full version, or a digest, so a rebuild in six months produces the same image. - Write a .dockerignore. Keep
.git,node_modules, local env files and build output out of the build context. - Check npm flags. The Node template uses
npm ci --only=production;--onlyis deprecated on npm 7 and later, where--omit=devis the current spelling. - Delete the compose version key. The generated
version: "3.8"line is obsolete under the Compose Specification, and recent Docker Compose releases warn about it.
Frequently Asked Questions
A Dockerfile with more than one FROM. The first stage compiles the application using a full toolchain; the second starts from a minimal base and copies in only the built artifact with COPY --from=builder. The compiler, the source tree and the package cache stay in the discarded stage, so the shipped image is far smaller and has a much smaller attack surface.
Alpine is the smallest but uses musl instead of glibc, so native modules and prebuilt Python wheels sometimes fail on it. Slim is Debian with the extras removed — bigger, but far fewer surprises. Distroless drops the shell and package manager entirely, which is excellent for production and awkward for debugging. Standard is the full image and belongs in a build stage rather than a runtime stage.
No, and this trips up almost everyone once. EXPOSE 3000 records the port as metadata: it documents intent for anyone reading the file and it tells docker run -P which ports to map to random host ports. To reach the container you still need docker run -p 3000:3000, or a ports: entry in compose — which the compose generator writes for you as "3000:3000".
A Dockerfile describes how to build one image. A docker-compose.yaml describes how to run one or more containers together: which image or build context each uses, the ports and environment they get, and how they restart. You usually need both — the Dockerfile to build your application, the compose file to wire it to a database and start everything with one command.
Because the template deliberately copies only the dependency manifest, so that the install layer stays cached when your source changes. Add COPY . . after the install step and before the CMD. If you skip it the image builds successfully and then fails at runtime with a missing entry point, which is a confusing way to discover the problem.
The template omits the final command for those four stacks, so the draft ends after the build instructions. Add the line yourself — CMD ["/app/main"] for a Go binary, CMD ["java", "-jar", "app.jar"] for a jar, CMD ["dotnet", "app.dll"] for .NET. Without it the container exits immediately, or runs whatever command the base image inherited.
No. The templates and the generation logic are JavaScript running in your tab, and no request carries your input. One thing to know: generating a Dockerfile rewrites the page URL with your language, base variant and multi-stage choice so the setup can be bookmarked or shared. The ports and environment variable fields are never written to the URL.
Treat it as a first draft. It gives you a correct instruction order and sensible defaults, but it does not add a non-root USER, a HEALTHCHECK, or pinned image versions, and it does not know your build steps. Work through the review checklist above, confirm the base image tag exists on your registry, then build locally before wiring it into a pipeline.
Use Cases
Containerising a New Service
Start a Node.js or Python service that has never been containerised with a correct instruction order — manifest copied first, install second — instead of writing the file from memory and rediscovering the caching rule the hard way.
Shrinking a Go or Rust Image
See the two-stage shape a compiled binary needs — build with the full toolchain, then copy the single executable onto a distroless base — before spending an afternoon working out the COPY --from paths yourself.
Standing Up a Test Database
Generate a compose file for PostgreSQL, Redis or MongoDB with the port mapped and a restart policy set, so an integration test suite has a real dependency to talk to instead of a mock.
Comparing Base Image Variants
Produce the same Dockerfile against Alpine, Slim and Distroless, build all three, and measure what each variant costs in megabytes and in debuggability before committing your team to one of them.
Serving a Static Build Behind Nginx
Take the output of a front-end build step and wrap it in an Nginx image that copies the folder to the document root and exposes port 80 — the standard way to ship a compiled single-page app.
Teaching Docker Fundamentals
Show a workshop group how each field changes the file — flip the variant and watch only FROM move, add a port and watch an EXPOSE line appear — so the instruction set becomes concrete rather than memorised.