|
| 1 | +# Docker Best Practices Rules |
| 2 | + |
| 3 | +When creating or modifying Dockerfiles, Docker Compose files, or Docker-related configurations, follow these rules: |
| 4 | + |
| 5 | +## Image Version Pinning |
| 6 | + |
| 7 | +- **ALWAYS** pin exact versions of base images. Never use `latest` or floating tags. |
| 8 | +- Use ARG variables for versions to allow override while maintaining defaults. |
| 9 | +- Example: `ARG NODE_VERSION=24.11.1-alpine` (not `node:latest` or `node:24-alpine`) |
| 10 | + |
| 11 | +## Base Image Selection |
| 12 | + |
| 13 | +- **ALWAYS** use Alpine (`-alpine`) or Slim (`-slim`) variants for smaller images and reduced attack surface. |
| 14 | +- Prefer Alpine for Node.js applications unless glibc compatibility is required. |
| 15 | +- Example: `FROM node:24.11.1-alpine` (not `FROM node:24.11.1`) |
| 16 | + |
| 17 | +## Multi-Stage Builds |
| 18 | + |
| 19 | +- **ALWAYS** use multi-stage builds for production Dockerfiles to separate build and runtime dependencies. |
| 20 | +- Name stages descriptively: `builder`, `runner`, `dev`, `test`, `final`. |
| 21 | +- Copy only necessary artifacts between stages using `--from=<stage>`. |
| 22 | +- Development Dockerfiles may use single-stage, but production must use multi-stage. |
| 23 | + |
| 24 | +## Non-Root User |
| 25 | + |
| 26 | +- **ALWAYS** run containers as a non-root user for security. |
| 27 | +- Use built-in users when available (`node`, `nginx`). |
| 28 | +- When creating custom users, use fixed UIDs/GIDs: `addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001` |
| 29 | +- Use `--chown` flag when copying files: `COPY --chown=node:node` |
| 30 | +- Set `USER` directive before `CMD`/`ENTRYPOINT`. |
| 31 | + |
| 32 | +## Layer Caching Optimization |
| 33 | + |
| 34 | +- **ALWAYS** copy dependency manifests (`package.json`, `package-lock.json`) before source code. |
| 35 | +- Use `npm ci` instead of `npm install` for reproducible installs in production. |
| 36 | +- Use BuildKit cache mounts: `RUN --mount=type=cache,target=/root/.npm npm ci` |
| 37 | +- Order instructions from least to most frequently changing. |
| 38 | + |
| 39 | +## Security Best Practices |
| 40 | + |
| 41 | +- Never hardcode secrets in Dockerfiles. Use Docker secrets, environment variables, or secret management. |
| 42 | +- Remove build dependencies after installation when possible. |
| 43 | +- Use specific `COPY` commands instead of wildcards. |
| 44 | +- Set `ENV NODE_ENV=production` in production images. |
| 45 | +- Scan images regularly for vulnerabilities. |
| 46 | + |
| 47 | +## Docker Compose Best Practices |
| 48 | + |
| 49 | +- Use unique, descriptive container names. Never reuse container names across services. |
| 50 | +- Pin image versions in production environments. |
| 51 | +- Add healthchecks for critical services. |
| 52 | +- Use named networks for service isolation. |
| 53 | +- Set appropriate restart policies (`unless-stopped` for production). |
| 54 | +- Avoid using `latest` tag in production. |
| 55 | + |
| 56 | +## Package Installation |
| 57 | + |
| 58 | +- Use `npm ci` for production builds (faster, more reliable, respects lock file). |
| 59 | +- Use `npm install` only in development Dockerfiles. |
| 60 | +- For global packages, pin exact versions: `npm install -g [email protected]` |
| 61 | +- Use `--only=production` flag when installing production dependencies: `npm ci --only=production` |
| 62 | + |
| 63 | +## File Ownership |
| 64 | + |
| 65 | +- Always set proper ownership when copying files: `COPY --chown=user:group` |
| 66 | +- Change ownership before switching users: `RUN chown -R user:group /app` then `USER user` |
| 67 | +- Ensure non-root users can access necessary directories. |
| 68 | + |
| 69 | +## .dockerignore |
| 70 | + |
| 71 | +- Maintain a comprehensive `.dockerignore` file to exclude: |
| 72 | + - `node_modules`, build outputs (`dist`, `build`), test files, IDE files |
| 73 | + - Docker files themselves, CI/CD configs, documentation |
| 74 | + - Environment files, logs, cache directories |
| 75 | + |
| 76 | +## Code Style |
| 77 | + |
| 78 | +- Use clear section comments with separators: `# =========================================` |
| 79 | +- Add descriptive comments explaining why, not just what. |
| 80 | +- Keep ARG declarations at the top of the Dockerfile. |
| 81 | +- Group related instructions together. |
| 82 | + |
| 83 | +## Common Mistakes to Avoid |
| 84 | + |
| 85 | +- ❌ Never use `latest` tag |
| 86 | +- ❌ Never run as root user |
| 87 | +- ❌ Never include devDependencies in production images |
| 88 | +- ❌ Never copy everything with `COPY . .` before installing dependencies |
| 89 | +- ❌ Never hardcode secrets |
| 90 | +- ❌ Never skip multi-stage builds for production |
| 91 | +- ❌ Never use `ADD` instead of `COPY` |
| 92 | +- ❌ Never forget to set `NODE_ENV=production` in production images |
| 93 | + |
| 94 | +## Project-Specific Standards |
| 95 | + |
| 96 | +- Node.js version: Use `24.11.1-alpine` as the default. |
| 97 | +- Nginx version: Use `nginxinc/nginx-unprivileged:alpine3.22` for unprivileged nginx. |
| 98 | +- Serve package: Pin to ` [email protected]` when using Vercel serve. |
| 99 | +- Ports: Use `8080` for production, `5173` for Vite dev server. |
| 100 | +- Working directory: Use `/app` as the standard working directory. |
| 101 | + |
| 102 | +## When Creating New Dockerfiles |
| 103 | + |
| 104 | +1. Start with ARG declarations for versions |
| 105 | +2. Use multi-stage build structure |
| 106 | +3. Copy dependency files first |
| 107 | +4. Install dependencies with cache mounts |
| 108 | +5. Copy source code |
| 109 | +6. Build application |
| 110 | +7. Create/use non-root user |
| 111 | +8. Set proper ownership |
| 112 | +9. Switch to non-root user |
| 113 | +10. Expose ports |
| 114 | +11. Set CMD/ENTRYPOINT |
| 115 | + |
| 116 | +## Validation Checklist |
| 117 | + |
| 118 | +Before finalizing any Dockerfile, ensure: |
| 119 | + |
| 120 | +- [ ] Base image version is pinned (no `latest`) |
| 121 | +- [ ] Using Alpine or Slim variant |
| 122 | +- [ ] Multi-stage build for production images |
| 123 | +- [ ] Running as non-root user |
| 124 | +- [ ] Layer ordering optimized for caching |
| 125 | +- [ ] Using `npm ci` instead of `npm install` (production) |
| 126 | +- [ ] BuildKit cache mounts for package managers |
| 127 | +- [ ] `.dockerignore` is comprehensive |
| 128 | +- [ ] No secrets hardcoded |
| 129 | +- [ ] Proper file ownership set |
| 130 | +- [ ] `NODE_ENV=production` set (production images) |
| 131 | + |
| 132 | +## References |
| 133 | + |
| 134 | +- See `AGENTS.md` for detailed Docker best practices documentation |
| 135 | +- Official Docker best practices: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/ |
| 136 | +- OWASP Docker Security: https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html |
0 commit comments