---
name: kunanonj/create-dockerfile
source: https://app.decimal.ai/s/kunanonj-create-dockerfile@1/SKILL.md
source_sha256: b187f0f90782
---

# Create Dockerfile

Write production-ready Dockerfile for general-purpose application projects.

## When Use

- Containerizing Node.js, Python, Go, Rust, or Java app
- Making consistent build/runtime environment
- Preparing app for cloud deploy or Docker Compose
- No existing Dockerfile in project

## Inputs

- **Required**: Project language and entry point (e.g., `npm start`, `python app.py`)
- **Required**: Dependency manifest (package.json, requirements.txt, go.mod, Cargo.toml, pom.xml)
- **Optional**: Target environment (development or production)
- **Optional**: Exposed ports

## Steps

### Step 1: Choose Base Image

| Language | Dev Image | Prod Image | Size |
|---|---|---|---|
| Node.js | `node:22-bookworm` | `node:22-bookworm-slim` | ~200MB |
| Python | `python:3.12-bookworm` | `python:3.12-slim-bookworm` | ~150MB |
| Go | `golang:1.23-bookworm` | `gcr.io/distroless/static` | ~2MB |
| Rust | `rust:1.82-bookworm` | `debian:bookworm-slim` | ~80MB |
| Java | `eclipse-temurin:21-jdk` | `eclipse-temurin:21-jre` | ~200MB |

**Got:** Pick slim/distroless variant for production images.

### Step 2: Write Dockerfile (by language)

#### Node.js

```dockerfile
FROM node:22-bookworm-slim

RUN groupadd -r appuser && useradd -r -g appuser -m appuser

WORKDIR /app

COPY package.json package-lock.json ./
RUN npm ci --omit=dev

COPY . .

USER appuser
EXPOSE 3000
CMD ["node", "src/index.js"]
```

#### Python

```dockerfile
FROM python:3.12-slim-bookworm

RUN groupadd -r appuser && useradd -r -g appuser -m appuser

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

USER appuser
EXPOSE 8000
CMD ["python", "app.py"]
```

#### Go

```dockerfile
FROM golang:1.23-bookworm AS builder

WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/server ./cmd/server

FROM gcr.io/distroless/static
COPY --from=builder /app/server /server
EXPOSE 8080
ENTRYPOINT ["/server"]
```

#### Rust

```dockerfile
FROM rust:1.82-bookworm AS builder

WORKDIR /src
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo "fn main() {}" > src/main.rs && cargo build --release && rm -rf src

COPY . .
RUN touch src/main.rs && cargo build --release

FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /src/target/release/myapp /usr/local/bin/myapp
EXPOSE 8080
ENTRYPOINT ["myapp"]
```

#### Java (Maven)

```dockerfile
FROM eclipse-temurin:21-jdk AS builder

WORKDIR /src
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn package -DskipTests

FROM eclipse-temurin:21-jre
COPY --from=builder /src/target/*.jar /app/app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
```

**Got:** `docker build -t myapp .` finishes without errors.

**If fail:** Check base image availability and dependency install commands.

### Step 3: ENTRYPOINT vs CMD

| Directive | Purpose | Override |
|---|---|---|
| `ENTRYPOINT` | Fixed executable | Override with `--entrypoint` |
| `CMD` | Default arguments | Override with trailing args |
| Both | `ENTRYPOINT` + default args via `CMD` | Args override CMD only |

Use `ENTRYPOINT` for compiled binaries with single purpose. Use `CMD` for interpreted languages where might want `docker run myapp bash`.

### Step 4: Create .dockerignore

```text
.git
.gitignore
node_modules
__pycache__
*.pyc
target/
.env
.env.*
*.md
!README.md
.vscode
.idea
Dockerfile
docker-compose*.yml
```

**Got:** Build context drops dev artifacts.

### Step 5: Add Non-Root User

Always run as non-root in production:

```dockerfile
RUN groupadd -r appuser && useradd -r -g appuser -m appuser
USER appuser
```

For distroless images, use built-in nonroot user:

```dockerfile
FROM gcr.io/distroless/static:nonroot
USER nonroot
```

### Step 6: Build and Verify

```bash
docker build -t myapp:latest .
docker run --rm myapp:latest
docker image inspect myapp:latest --format '{{.Size}}'
```

**Got:** Container starts, responds on expected port, runs as non-root.

**If fail:** Check logs with `docker logs`. Verify WORKDIR, COPY paths, exposed ports.

## Checks

- [ ] `docker build` finishes without errors
- [ ] Container starts and app responds
- [ ] `.dockerignore` drops unneeded files
- [ ] App runs as non-root user
- [ ] Dependencies copied before source code (cache efficiency)
- [ ] No secrets or `.env` files baked into image

## Pitfalls

- **COPY before dependency install**: Invalidates dependency cache on every code change. Always copy manifest file first.
- **Running as root**: Default Docker user is root. Always add non-root user for production.
- **Missing .dockerignore**: Sending `node_modules` or `.git` into build context wastes time and disk.
- **Using `latest` tag for base images**: Pin to specific versions (e.g., `node:22.11.0`) for reproducibility.
- **Forgetting `--no-cache-dir`**: Python `pip` caches packages by default, bloating image.
- **ADD vs COPY**: Use `COPY` unless need URL download or tar extraction (`ADD` auto-extracts).

## See Also

- `create-r-dockerfile` - R-specific Dockerfile using rocker images
- `create-multistage-dockerfile` - multi-stage patterns for minimal production images
- `optimize-docker-build-cache` - advanced caching strategies
- `setup-compose-stack` - orchestrate containerized app with other services