</>DevOps101
LearnTemplatesToolsBlog
Back

Production Dockerfile Templates

Starter Dockerfiles for Node, Python, Go, Java, Rust, and a static site.

This file is the pack. It is markdown, not a live apply.

# Production Dockerfile Templates

Starter Dockerfiles for Node, Python, Go, Java, Rust, and a static site. Python is single-stage because the interpreter is the runtime. The others leave the compiler in the build stage. Read the comments and rename paths that do not match your repo.

## How to use

1. Copy the file for the language you actually ship.
2. Replace dummy paths: dist/, app.py, the Rust binary name, the jar.
3. Build it on a clean machine before you trust it in CI.

## Node.js

```dockerfile
# Node.js. Commit package-lock.json. The build should write ./dist.
FROM node:20-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup -g 1001 -S app && adduser -S app -u 1001 -G app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=build /app/dist ./dist
USER app
EXPOSE 3000
CMD ["node", "dist/index.js"]
```
## Python

```dockerfile
# Python. Single stage: the interpreter is the runtime.
# Expects requirements.txt and app.py.
FROM python:3.12-slim
WORKDIR /app
RUN useradd --create-home --uid 1001 app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
USER app
EXPOSE 3000
CMD ["python", "app.py"]
```
## Go

```dockerfile
# Go. Commit go.sum. The binary is /out/server, not the workdir.
FROM golang:1.23-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /out/server .

FROM alpine:3.20
RUN addgroup -g 1001 -S app && adduser -S app -u 1001 -G app
COPY --from=builder /out/server /usr/local/bin/server
USER app
EXPOSE 3000
CMD ["server"]
```
## Rust

```dockerfile
# Rust. Rename "app" to the binary name in Cargo.toml.
FROM rust:1.80-slim-bookworm AS builder
WORKDIR /src
COPY . .
RUN cargo build --release

FROM debian:bookworm-slim
RUN useradd --create-home --uid 1001 app
COPY --from=builder /src/target/release/app /usr/local/bin/app
USER app
EXPOSE 3000
CMD ["app"]
```
## Java (Spring Boot)

```dockerfile
# Java, Spring Boot, Gradle wrapper.
# Copies the boot jar and skips the -plain.jar Gradle also emits.
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /src
COPY . .
RUN ./gradlew bootJar --no-daemon \
  && mkdir -p /out \
  && find build/libs -name '*.jar' ! -name '*-plain.jar' -exec cp {} /out/app.jar \;

FROM eclipse-temurin:21-jre-alpine
RUN addgroup -g 1001 -S app && adduser -S app -u 1001 -G app
WORKDIR /app
COPY --from=builder /out/app.jar /app/app.jar
USER app
EXPOSE 3000
CMD ["java", "-jar", "/app/app.jar"]
```
## Static site (Nginx)

```dockerfile
# Static site. Nginx listens on 80. Change dist/ if your bundler uses another directory.
FROM node:20-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:1.27-alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```