- Use multi-stage builds to keep production images small — separate build and runtime stages.
- Always include a `.dockerignore` to exclude `node_modules`, `.git`, `.env`, and build artifacts.
- Run containers as a non-root user for security. Add `USER` directive after installing dependencies.
- Use Docker Compose for local development and end-to-end testing environments.
- Pin base image versions with specific tags or SHA digests — never use `latest` in production.
- Order Dockerfile instructions from least to most frequently changing to maximize layer caching.
- Use `COPY --link` where supported to improve cache efficiency on rebuilds.
- Define health checks in Compose (`healthcheck:`) so dependent services wait for readiness.
- Use named volumes for persistent data (databases, caches) — never store state in containers.