How to Prompt AI to Write a Dockerfile That Builds Fast
A prompt that gets AI to write a lean, cache-friendly Dockerfile: multi-stage builds, pinned tags, a non-root user, and a checklist to review the result.
To get a good Dockerfile from AI, give it the facts it cannot guess, which are the runtime version, how the app builds, what it needs at runtime, and the port, and then state the constraints: a multi-stage build, a pinned base image, a non-root user, and dependency installs ordered for caching. Review the result against five checks before you trust it.
Without that, the usual output is a single-stage file on the latest tag, running as root, that copies the whole project before installing anything. It builds, it runs, and it is slow, large, and a little unsafe.
What to put in the prompt
AI writes a plausible Dockerfile for the average project. Yours is not the average project, so the prompt has to carry what is specific to it.
Write a production Dockerfile and a .dockerignore for this app.
Facts:
- Node.js 22, npm, built with "npm run build" into ./dist
- The runtime needs only dist/ and production dependencies
- Listens on port 3000, reads DATABASE_URL and PORT from the environment
- Entry point after build: dist/server.js
Constraints:
- Multi-stage build: one stage to install and build, one slim final stage
- Pin the base image to a specific version tag, never latest
- Copy package.json and package-lock.json first and run npm ci before copying the source, so dependency installs are cached
- Run as a non-root user
- Use exec form for CMD
- No secrets or .env files in the image
After the files, explain each layer in one line and say which layers rebuild after a typical code edit.The last sentence forces the model to reason about the build cache, which is the part most people never check.
What a good result looks like
FROM node:22-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .FROM node:22-slim WORKDIR /app ENV NODE_ENV=production COPY --from=build --chown=node:node /app/node_modules ./node_modules COPY --from=build --chown=node:node /app/dist ./dist COPY --from=build --chown=node:node /app/package.json ./ USER node EXPOSE 3000 CMD ["node", "dist/server.js"] ```
And the matching .dockerignore:
node_modules
.git
.env
dist
*.logThe order of the first four lines is the whole performance story. Docker caches each layer and reuses it until something above it changes. Because the dependency manifests are copied and installed before the source, editing a source file only rebuilds from the copy step down, and the slow install is reused. Put COPY . . before npm ci and every code change reinstalls every dependency.
Five checks before you accept it
Cache order. Find the install step and confirm only the dependency manifests are copied before it.
Pinned tag. Any FROM line ending in latest, or with no tag, will change under you. Pin to the version your app runs on locally.
Non-root user. Confirm a USER line appears before CMD. After building, you can verify with docker run --rm your-image id and read the user it reports.
No secrets. Search for ENV or ARG lines holding tokens or passwords. Build arguments are recorded in the image metadata, so they are the wrong place for secrets. Docker's own guidance is to avoid build-time variables for them.
Final stage contents. The slim stage should hold the runtime files and nothing else. If it contains compilers, test folders, or the git history, the multi-stage build is not doing its job.
Run docker history on the result to see the size each layer adds. A single layer that is far bigger than the rest is usually where the problem is.
The mistakes AI makes most often
Using ADD where COPY would do. ADD can fetch URLs and unpack archives, which is rarely what you want.
Shell form CMD, such as a bare command string, which wraps your process in a shell. The shell often does not forward stop signals, so containers take the full timeout to shut down. Exec form, the JSON array, avoids it.
Running apt-get update and apt-get install in separate RUN lines. Docker's best practices guide recommends combining them in one instruction, adding the no-install-recommends flag, and removing the package lists afterward.
Leaving out the ignore file, so the build context includes node_modules, the git folder, and local .env files.
Choosing a tiny Alpine base for size and then hitting errors from native modules, because Alpine uses a different C library. The slim Debian variants are a safer default.
The full Dockerfile reference is the place to settle any disagreement about an instruction.
Keep the facts in one place
You will paste the same facts into this prompt every time you change something. Put them in a file your tools read automatically; the guide to writing an AGENTS.md file shows how. For the rest of your toolchain, the AI coding tools overview maps what each category does. When the container is running in production, a runbook tells the next person how to restart it, and the prompt engineering guide covers the general method of supplying facts before constraints.
FAQ
Can AI write a Dockerfile for any language?
For common stacks such as Node, Python, Go, and Java, yes. The structure is similar across them: install dependencies in one layer, build, and copy only what runs into a slim final image. For unusual stacks, paste your build commands and let the model translate them.
How do I test an AI-written Dockerfile?
Build it, run it, and check three things: the app responds on the expected port, the container runs as a non-root user, and a second build after a small code change finishes quickly because the dependency layer is cached.
Should I use Alpine or slim images?
Slim Debian images are the safer default. Alpine images are smaller, but they use musl instead of glibc, and some native dependencies fail or behave differently on it. Choose Alpine when you have tested your dependencies on it.
Why is my Docker image so large?
The usual causes are a single-stage build that keeps compilers and dev dependencies, a missing ignore file that copies large folders in, and package manager caches left in the layer. Multi-stage builds and an ignore file fix most of it.
How did this land?
About the author

Developer Advocate
Steve builds something with Swarmz every week and writes up what worked, what broke, and what he'd do differently. Tutorials and hands-on guides are his lane.


