Anything that runs in Docker runs here. You can start from a prebuilt image for a common stack, or supply your own. This page explains what each choice actually changes.
Prebuilt images
Ready-made images cover Node.js, Python, Java, Go, Rust and PHP, plus Redis and MongoDB. There is also a base image for shipping your own Dockerfile.
A prebuilt image means the runtime, package manager and common build tools are already installed. For a service that is a single script or a small API, that is genuinely all you need — and it removes an entire class of setup problems, because there is no toolchain to get wrong.
Choosing a version deliberately
Use a specific version rather than latest. latest moves underneath you: a restart can pick up a new major version with breaking changes, which produces an outage that correlates with nothing you changed. Pin the version, and upgrade on a schedule you control.
Prefer alpine-based images where they exist. They are substantially smaller, which speeds up cold starts and leaves more of your storage limit for your own files.
When you need your own image
Reach for a custom Dockerfile when any of these is true:
- You need a native dependency or system package that the prebuilt image lacks.
- You want a multi-stage build so compilers and headers never ship to production.
- You need a specific base — Debian rather than Alpine, or a pinned digest.
- You want the build reproducible and reviewable in version control.
A minimal multi-stage Node build is enough to illustrate the idea:
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]
The first stage installs dependencies. The second copies only the finished node_modules and your source — no compiler, no devDependencies, nothing that only mattered at build time. EXPOSE is documentation rather than a network request in this environment, but declaring it keeps the intent clear and matches the port you configure on the service.
Keep secrets out of the image
Anything baked into a layer is permanent and readable by anyone who pulls the image. Tokens, database passwords and API keys belong in environment variables, set on the service, not in a Dockerfile or a committed .env file. See Environment variables and secrets.
Multi-stage builds are the main win
If you only adopt one practice from custom images, make it this one. A typical Java or Go toolchain is several hundred megabytes of compilers and headers that serve no purpose at runtime. Multi-stage builds drop that from the shipped image, and on a 3 GB Starter container the difference decides whether you fit comfortably or run close to the storage limit.