Dockerfile
Base & Metadata
# syntax=docker/dockerfile:1
FROM node:20-alpine
LABEL org.opencontainers.image.source="https://example.com/repo"Build Args, Env, Workdir
ARG NODE_ENV=production
ENV NODE_ENV=$NODE_ENV \
TZ=Europe/London
WORKDIR /appCopy & Dependencies
# leverage layer caching: copy only manifests first
COPY package*.json ./
RUN npm ci --only=production
# then app source
COPY . .Run Commands
# combine commands to reduce layers; use --no-cache prudently
RUN adduser -D appuser && chown -R appuser:appuser /appExpose, User, Entrypoint/Cmd
EXPOSE 3000
USER appuser
# ENTRYPOINT defines the executable; CMD provides default args
ENTRYPOINT ["node", "server.js"]
# or:
# CMD ["node", "server.js"]ENTRYPOINT vs CMD
- ENTRYPOINT: the main binary. docker run image <args> appends to ENTRYPOINT.
- CMD: default args (or default command if no ENTRYPOINT). Overridden by run-time args.
Healthcheck (optional)
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1Volumes (declare intended mount points)
VOLUME ["/data"]Multi-Stage Build (example: Node build β tiny runtime)
# build stage
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# runtime stage
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/dist ./dist
COPY package*.json ./
RUN npm ci --omit=dev
USER node
EXPOSE 3000
CMD ["node", "dist/index.js"].dockerignore (crucial for smaller, faster builds)
.git
node_modules
dist
*.log
.env
DockerfileAlpine APK & APT Best Practices
# Alpine
RUN apk add --no-cache curl
# Debian/Ubuntu
RUN apt-get update && apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*Security & Size Tips
- Run as non-root (USER).
- Pin versions where it matters (base image, packages).
- Keep layers minimal; combine RUN steps.
- Avoid copying the entire context; use .dockerignore.
- Prefer alpine/distroless when feasible.
- Donβt bake secrets into images; use build args and runtime env vars or secrets.
Build & Run (from this Dockerfile)
docker build -t myapp:latest .
docker run -d --name myapp -p 3000:3000 myapp:latestIf you want this tailored to your Raspberry Pi (ARM64) builds, I can add multi-arch notes (--platform linux/arm64) and a minimal compose file.