[{"data":1,"prerenderedAt":1348},["ShallowReactive",2],{"blog-en-dockerfile-best-practices-2025":3,"blog-en-dockerfile-best-practices-2025-alt":227},{"id":4,"title":5,"author":6,"body":7,"date":1331,"description":1332,"extension":1333,"image":199,"locale":1334,"meta":1335,"navigation":227,"path":1336,"seo":1337,"stem":1338,"tags":1339,"__hash__":1347},"blog\u002Fblog\u002Fen\u002Fdockerfile-best-practices-2025.md","Dockerfile Best Practices 2025: Building Lightweight, Secure, and Fast Images","Kubo Team",{"type":8,"value":9,"toc":1304},"minimark",[10,14,25,30,39,44,185,193,258,265,308,316,320,323,327,426,439,443,502,506,515,519,567,570,574,618,621,625,702,714,720,724,727,731,792,796,799,886,890,969,973,977,1006,1016,1020,1065,1069,1138,1147,1151,1175,1179,1194,1200,1204,1207,1282,1292,1300],[11,12,13],"p",{},"The way you write your Dockerfile can result in a 10x difference in image size, 20x difference in build time, and 5x difference in vulnerability count. Evolving from \"it works\" to \"production-ready\" Dockerfiles is an essential skill for DevOps engineers in 2025.",[11,15,16,17,24],{},"At ",[18,19,23],"a",{"href":20,"rel":21},"https:\u002F\u002Fkubo.hexabase.io\u002F",[22],"nofollow","Kubo",", we manage numerous container workloads on Kubernetes where Dockerfile quality directly impacts cluster-wide performance and security. This article systematically covers Dockerfile best practices based on the latest 2025 insights.",[26,27,29],"h2",{"id":28},"_1-base-image-selection-the-foundation-of-lightweight-images","1. Base Image Selection: The Foundation of Lightweight Images",[11,31,32,33,38],{},"Base image selection has the greatest impact on final image size and security. The ",[18,34,37],{"href":35,"rel":36},"https:\u002F\u002Fdocs.docker.com\u002Fbuild\u002Fbuilding\u002Fbest-practices\u002F",[22],"Docker official documentation"," recommends images from trusted sources.",[40,41,43],"h3",{"id":42},"base-image-comparison","Base Image Comparison",[45,46,47,69],"table",{},[48,49,50],"thead",{},[51,52,53,57,60,63,66],"tr",{},[54,55,56],"th",{},"Base Image",[54,58,59],{},"Size",[54,61,62],{},"CVEs (approx.)",[54,64,65],{},"Shell",[54,67,68],{},"Use Case",[70,71,72,93,111,130,149,167],"tbody",{},[51,73,74,81,84,87,90],{},[75,76,77],"td",{},[78,79,80],"code",{},"ubuntu:24.04",[75,82,83],{},"~77MB",[75,85,86],{},"30-50",[75,88,89],{},"bash",[75,91,92],{},"General (not recommended)",[51,94,95,100,103,106,108],{},[75,96,97],{},[78,98,99],{},"debian:bookworm-slim",[75,101,102],{},"~52MB",[75,104,105],{},"20-40",[75,107,89],{},[75,109,110],{},"Lightweight Debian",[51,112,113,118,121,124,127],{},[75,114,115],{},[78,116,117],{},"alpine:3.21",[75,119,120],{},"~5MB",[75,122,123],{},"5-10",[75,125,126],{},"sh (busybox)",[75,128,129],{},"Lightweight + debuggable",[51,131,132,137,140,143,146],{},[75,133,134],{},[78,135,136],{},"distroless\u002Fstatic",[75,138,139],{},"~2MB",[75,141,142],{},"0-2",[75,144,145],{},"None",[75,147,148],{},"Go\u002FRust static binaries",[51,150,151,156,159,162,164],{},[75,152,153],{},[78,154,155],{},"distroless\u002Fbase",[75,157,158],{},"~20MB",[75,160,161],{},"2-5",[75,163,145],{},[75,165,166],{},"glibc-dependent apps",[51,168,169,174,177,180,182],{},[75,170,171],{},[78,172,173],{},"scratch",[75,175,176],{},"0MB",[75,178,179],{},"0",[75,181,145],{},[75,183,184],{},"Fully custom",[11,186,187,192],{},[18,188,191],{"href":189,"rel":190},"https:\u002F\u002Fgithub.com\u002FGoogleContainerTools\u002Fdistroless",[22],"Google's distroless images"," contain only your application and its runtime dependencies, with no shell or package manager, minimizing the attack surface.",[194,195,200],"pre",{"className":196,"code":197,"language":198,"meta":199,"style":199},"language-dockerfile shiki shiki-themes tokyo-night","# Recommended: Choose minimal images for your use case\n# Go applications\nFROM gcr.io\u002Fdistroless\u002Fstatic-debian12\n\n# Node.js applications\nFROM gcr.io\u002Fdistroless\u002Fnodejs20-debian12\n\n# Python applications\nFROM python:3.12-slim\n","dockerfile","",[78,201,202,210,216,222,229,235,241,246,252],{"__ignoreMap":199},[203,204,207],"span",{"class":205,"line":206},"line",1,[203,208,209],{},"# Recommended: Choose minimal images for your use case\n",[203,211,213],{"class":205,"line":212},2,[203,214,215],{},"# Go applications\n",[203,217,219],{"class":205,"line":218},3,[203,220,221],{},"FROM gcr.io\u002Fdistroless\u002Fstatic-debian12\n",[203,223,225],{"class":205,"line":224},4,[203,226,228],{"emptyLinePlaceholder":227},true,"\n",[203,230,232],{"class":205,"line":231},5,[203,233,234],{},"# Node.js applications\n",[203,236,238],{"class":205,"line":237},6,[203,239,240],{},"FROM gcr.io\u002Fdistroless\u002Fnodejs20-debian12\n",[203,242,244],{"class":205,"line":243},7,[203,245,228],{"emptyLinePlaceholder":227},[203,247,249],{"class":205,"line":248},8,[203,250,251],{},"# Python applications\n",[203,253,255],{"class":205,"line":254},9,[203,256,257],{},"FROM python:3.12-slim\n",[11,259,260,264],{},[261,262,263],"strong",{},"Version pinning"," is essential for reproducibility:",[194,266,268],{"className":196,"code":267,"language":198,"meta":199,"style":199},"# Bad: Tag may change\nFROM node:20\n\n# Good: Specific version\nFROM node:20.18-alpine3.21\n\n# Best: Pinned by digest\nFROM node:20.18-alpine3.21@sha256:abc123...\n",[78,269,270,275,280,284,289,294,298,303],{"__ignoreMap":199},[203,271,272],{"class":205,"line":206},[203,273,274],{},"# Bad: Tag may change\n",[203,276,277],{"class":205,"line":212},[203,278,279],{},"FROM node:20\n",[203,281,282],{"class":205,"line":218},[203,283,228],{"emptyLinePlaceholder":227},[203,285,286],{"class":205,"line":224},[203,287,288],{},"# Good: Specific version\n",[203,290,291],{"class":205,"line":231},[203,292,293],{},"FROM node:20.18-alpine3.21\n",[203,295,296],{"class":205,"line":237},[203,297,228],{"emptyLinePlaceholder":227},[203,299,300],{"class":205,"line":243},[203,301,302],{},"# Best: Pinned by digest\n",[203,304,305],{"class":205,"line":248},[203,306,307],{},"FROM node:20.18-alpine3.21@sha256:abc123...\n",[11,309,310,315],{},[18,311,314],{"href":312,"rel":313},"https:\u002F\u002Fwww.hexabase.com\u002Fproduct\u002Fcaptain-ai\u002F",[22],"Captain.AI"," analyzes your project's language and framework to automatically recommend optimal base images.",[26,317,319],{"id":318},"_2-layer-structure-optimization","2. Layer Structure Optimization",[11,321,322],{},"Docker uses layer caching to speed up builds. Optimizing instruction order maximizes cache hit rates.",[40,324,326],{"id":325},"instruction-order-least-frequently-changed-first","Instruction Order: Least Frequently Changed First",[194,328,330],{"className":196,"code":329,"language":198,"meta":199,"style":199},"# 1. Base image (rarely changes)\nFROM node:20-alpine AS builder\n\n# 2. System dependency installation (infrequent changes)\nRUN apk add --no-cache python3 make g++\n\n# 3. Copy dependency definitions (changes when packages added)\nWORKDIR \u002Fapp\nCOPY package.json package-lock.json .\u002F\n\n# 4. Install dependencies (re-runs when packages added)\nRUN npm ci\n\n# 5. Copy source code (frequent changes)\nCOPY . .\n\n# 6. Build (re-runs on source code changes)\nRUN npm run build\n",[78,331,332,337,342,346,351,356,360,365,370,375,380,386,392,397,403,409,414,420],{"__ignoreMap":199},[203,333,334],{"class":205,"line":206},[203,335,336],{},"# 1. Base image (rarely changes)\n",[203,338,339],{"class":205,"line":212},[203,340,341],{},"FROM node:20-alpine AS builder\n",[203,343,344],{"class":205,"line":218},[203,345,228],{"emptyLinePlaceholder":227},[203,347,348],{"class":205,"line":224},[203,349,350],{},"# 2. System dependency installation (infrequent changes)\n",[203,352,353],{"class":205,"line":231},[203,354,355],{},"RUN apk add --no-cache python3 make g++\n",[203,357,358],{"class":205,"line":237},[203,359,228],{"emptyLinePlaceholder":227},[203,361,362],{"class":205,"line":243},[203,363,364],{},"# 3. Copy dependency definitions (changes when packages added)\n",[203,366,367],{"class":205,"line":248},[203,368,369],{},"WORKDIR \u002Fapp\n",[203,371,372],{"class":205,"line":254},[203,373,374],{},"COPY package.json package-lock.json .\u002F\n",[203,376,378],{"class":205,"line":377},10,[203,379,228],{"emptyLinePlaceholder":227},[203,381,383],{"class":205,"line":382},11,[203,384,385],{},"# 4. Install dependencies (re-runs when packages added)\n",[203,387,389],{"class":205,"line":388},12,[203,390,391],{},"RUN npm ci\n",[203,393,395],{"class":205,"line":394},13,[203,396,228],{"emptyLinePlaceholder":227},[203,398,400],{"class":205,"line":399},14,[203,401,402],{},"# 5. Copy source code (frequent changes)\n",[203,404,406],{"class":205,"line":405},15,[203,407,408],{},"COPY . .\n",[203,410,412],{"class":205,"line":411},16,[203,413,228],{"emptyLinePlaceholder":227},[203,415,417],{"class":205,"line":416},17,[203,418,419],{},"# 6. Build (re-runs on source code changes)\n",[203,421,423],{"class":205,"line":422},18,[203,424,425],{},"RUN npm run build\n",[11,427,428,429,432,433,438],{},"With this order, source code changes trigger only ",[261,430,431],{},"steps 5-6",", while dependency installation (potentially minutes long) remains cached. ",[18,434,437],{"href":435,"rel":436},"https:\u002F\u002Fblog.bytescrum.com\u002Fdockerfile-best-practices-2025-secure-fast-and-modern",[22],"ByteScrum's best practices article"," puts it aptly: \"Wrong order = 20-minute rebuilds; correct order = 20-second rebuilds.\"",[40,440,442],{"id":441},"consolidating-run-instructions","Consolidating RUN Instructions",[194,444,446],{"className":196,"code":445,"language":198,"meta":199,"style":199},"# Bad: Creates 3 layers, intermediate files persist\nRUN apt-get update\nRUN apt-get install -y curl wget\nRUN rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n\n# Good: Single layer, cache cleaned\nRUN apt-get update && \\\n    apt-get install -y --no-install-recommends \\\n      curl \\\n      wget \\\n    && rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n",[78,447,448,453,458,463,468,472,477,482,487,492,497],{"__ignoreMap":199},[203,449,450],{"class":205,"line":206},[203,451,452],{},"# Bad: Creates 3 layers, intermediate files persist\n",[203,454,455],{"class":205,"line":212},[203,456,457],{},"RUN apt-get update\n",[203,459,460],{"class":205,"line":218},[203,461,462],{},"RUN apt-get install -y curl wget\n",[203,464,465],{"class":205,"line":224},[203,466,467],{},"RUN rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n",[203,469,470],{"class":205,"line":231},[203,471,228],{"emptyLinePlaceholder":227},[203,473,474],{"class":205,"line":237},[203,475,476],{},"# Good: Single layer, cache cleaned\n",[203,478,479],{"class":205,"line":243},[203,480,481],{},"RUN apt-get update && \\\n",[203,483,484],{"class":205,"line":248},[203,485,486],{},"    apt-get install -y --no-install-recommends \\\n",[203,488,489],{"class":205,"line":254},[203,490,491],{},"      curl \\\n",[203,493,494],{"class":205,"line":377},[203,495,496],{},"      wget \\\n",[203,498,499],{"class":205,"line":382},[203,500,501],{},"    && rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n",[26,503,505],{"id":504},"_3-essential-security-hardening","3. Essential Security Hardening",[11,507,508,509,514],{},"According to ",[18,510,513],{"href":511,"rel":512},"https:\u002F\u002Fwww.sysdig.com\u002Flearn-cloud-native\u002Fdockerfile-best-practices",[22],"Sysdig's research",", 76% of production containers run as root — a critical security risk.",[40,516,518],{"id":517},"run-as-non-root-user","Run as Non-Root User",[194,520,522],{"className":196,"code":521,"language":198,"meta":199,"style":199},"# Create application user\nRUN addgroup -g 10001 -S appgroup && \\\n    adduser -u 10001 -S appuser -G appgroup\n\n# Set ownership of required directories\nCOPY --chown=appuser:appgroup . \u002Fapp\n\n# Switch to non-root user\nUSER appuser\n",[78,523,524,529,534,539,543,548,553,557,562],{"__ignoreMap":199},[203,525,526],{"class":205,"line":206},[203,527,528],{},"# Create application user\n",[203,530,531],{"class":205,"line":212},[203,532,533],{},"RUN addgroup -g 10001 -S appgroup && \\\n",[203,535,536],{"class":205,"line":218},[203,537,538],{},"    adduser -u 10001 -S appuser -G appgroup\n",[203,540,541],{"class":205,"line":224},[203,542,228],{"emptyLinePlaceholder":227},[203,544,545],{"class":205,"line":231},[203,546,547],{},"# Set ownership of required directories\n",[203,549,550],{"class":205,"line":237},[203,551,552],{},"COPY --chown=appuser:appgroup . \u002Fapp\n",[203,554,555],{"class":205,"line":243},[203,556,228],{"emptyLinePlaceholder":227},[203,558,559],{"class":205,"line":248},[203,560,561],{},"# Switch to non-root user\n",[203,563,564],{"class":205,"line":254},[203,565,566],{},"USER appuser\n",[11,568,569],{},"Using UIDs above 10000 avoids ID collisions with host OS system users.",[40,571,573],{"id":572},"secret-management","Secret Management",[194,575,577],{"className":196,"code":576,"language":198,"meta":199,"style":199},"# NEVER do this: Secrets persist in image layers\nCOPY .env \u002Fapp\u002F.env\nENV API_KEY=sk-secret-key\n\n# Correct: BuildKit secret mounts\nRUN --mount=type=secret,id=api_key \\\n    export API_KEY=$(cat \u002Frun\u002Fsecrets\u002Fapi_key) && \\\n    .\u002Fsetup.sh\n",[78,578,579,584,589,594,598,603,608,613],{"__ignoreMap":199},[203,580,581],{"class":205,"line":206},[203,582,583],{},"# NEVER do this: Secrets persist in image layers\n",[203,585,586],{"class":205,"line":212},[203,587,588],{},"COPY .env \u002Fapp\u002F.env\n",[203,590,591],{"class":205,"line":218},[203,592,593],{},"ENV API_KEY=sk-secret-key\n",[203,595,596],{"class":205,"line":224},[203,597,228],{"emptyLinePlaceholder":227},[203,599,600],{"class":205,"line":231},[203,601,602],{},"# Correct: BuildKit secret mounts\n",[203,604,605],{"class":205,"line":237},[203,606,607],{},"RUN --mount=type=secret,id=api_key \\\n",[203,609,610],{"class":205,"line":243},[203,611,612],{},"    export API_KEY=$(cat \u002Frun\u002Fsecrets\u002Fapi_key) && \\\n",[203,614,615],{"class":205,"line":248},[203,616,617],{},"    .\u002Fsetup.sh\n",[11,619,620],{},"BuildKit secret mounts make secrets available only during build without persisting them in image layers.",[40,622,624],{"id":623},"leveraging-dockerignore","Leveraging .dockerignore",[194,626,630],{"className":627,"code":628,"language":629,"meta":199,"style":199},"language-gitignore shiki shiki-themes tokyo-night","# .dockerignore\n.git\n.gitignore\nnode_modules\n.env\n.env.*\n*.md\ndocker-compose*.yml\nDockerfile*\n.DS_Store\n__pycache__\n*.pyc\ncoverage\u002F\n.pytest_cache\u002F\n","gitignore",[78,631,632,637,642,647,652,657,662,667,672,677,682,687,692,697],{"__ignoreMap":199},[203,633,634],{"class":205,"line":206},[203,635,636],{},"# .dockerignore\n",[203,638,639],{"class":205,"line":212},[203,640,641],{},".git\n",[203,643,644],{"class":205,"line":218},[203,645,646],{},".gitignore\n",[203,648,649],{"class":205,"line":224},[203,650,651],{},"node_modules\n",[203,653,654],{"class":205,"line":231},[203,655,656],{},".env\n",[203,658,659],{"class":205,"line":237},[203,660,661],{},".env.*\n",[203,663,664],{"class":205,"line":243},[203,665,666],{},"*.md\n",[203,668,669],{"class":205,"line":248},[203,670,671],{},"docker-compose*.yml\n",[203,673,674],{"class":205,"line":254},[203,675,676],{},"Dockerfile*\n",[203,678,679],{"class":205,"line":377},[203,680,681],{},".DS_Store\n",[203,683,684],{"class":205,"line":382},[203,685,686],{},"__pycache__\n",[203,688,689],{"class":205,"line":388},[203,690,691],{},"*.pyc\n",[203,693,694],{"class":205,"line":394},[203,695,696],{},"coverage\u002F\n",[203,698,699],{"class":205,"line":399},[203,700,701],{},".pytest_cache\u002F\n",[11,703,704,707,708,713],{},[78,705,706],{},".dockerignore"," reduces build context size and prevents unintended files — especially secrets — from being included in images. ",[18,709,712],{"href":710,"rel":711},"https:\u002F\u002Fwww.geeksforgeeks.org\u002Fdevops\u002Fhow-to-use-dockerfile-best-practices-for-efficient-image-building\u002F",[22],"GeeksforGeeks' Dockerfile guide"," highlights this as an essential practice.",[11,715,716,719],{},[18,717,23],{"href":20,"rel":718},[22],"'s security policies recommend non-root execution and .dockerignore usage as standards.",[26,721,723],{"id":722},"_4-leveraging-buildkit","4. Leveraging BuildKit",[11,725,726],{},"BuildKit is Docker's next-generation build engine, offering parallel builds, improved caching, and security features.",[40,728,730],{"id":729},"enabling-buildkit","Enabling BuildKit",[194,732,735],{"className":733,"code":734,"language":89,"meta":199,"style":199},"language-bash shiki shiki-themes tokyo-night","# Enable via environment variable\nexport DOCKER_BUILDKIT=1\n\n# Use docker buildx (recommended)\ndocker buildx build -t myapp:latest .\n",[78,736,737,743,761,765,770],{"__ignoreMap":199},[203,738,739],{"class":205,"line":206},[203,740,742],{"class":741},"sbD-w","# Enable via environment variable\n",[203,744,745,749,753,757],{"class":205,"line":212},[203,746,748],{"class":747},"sN7LL","export",[203,750,752],{"class":751},"sE3pS"," DOCKER_BUILDKIT",[203,754,756],{"class":755},"sAklC","=",[203,758,760],{"class":759},"sOJ5S","1\n",[203,762,763],{"class":205,"line":218},[203,764,228],{"emptyLinePlaceholder":227},[203,766,767],{"class":205,"line":224},[203,768,769],{"class":741},"# Use docker buildx (recommended)\n",[203,771,772,775,779,782,786,789],{"class":205,"line":231},[203,773,774],{"class":751},"docker",[203,776,778],{"class":777},"sPY7s"," buildx",[203,780,781],{"class":777}," build",[203,783,785],{"class":784},"sT800"," -t",[203,787,788],{"class":777}," myapp:latest",[203,790,791],{"class":777}," .\n",[40,793,795],{"id":794},"cache-mounts","Cache Mounts",[11,797,798],{},"Share dependency package caches across builds to avoid reinstallation:",[194,800,802],{"className":196,"code":801,"language":198,"meta":199,"style":199},"# Persist pip cache\nRUN --mount=type=cache,target=\u002Froot\u002F.cache\u002Fpip \\\n    pip install -r requirements.txt\n\n# Persist npm cache\nRUN --mount=type=cache,target=\u002Froot\u002F.npm \\\n    npm ci\n\n# Persist Go module cache\nRUN --mount=type=cache,target=\u002Fgo\u002Fpkg\u002Fmod \\\n    --mount=type=cache,target=\u002Froot\u002F.cache\u002Fgo-build \\\n    go build -o \u002Fapp\u002Fserver .\n\n# Persist apt cache\nRUN --mount=type=cache,target=\u002Fvar\u002Fcache\u002Fapt \\\n    --mount=type=cache,target=\u002Fvar\u002Flib\u002Fapt \\\n    apt-get update && apt-get install -y curl\n",[78,803,804,809,814,819,823,828,833,838,842,847,852,857,862,866,871,876,881],{"__ignoreMap":199},[203,805,806],{"class":205,"line":206},[203,807,808],{},"# Persist pip cache\n",[203,810,811],{"class":205,"line":212},[203,812,813],{},"RUN --mount=type=cache,target=\u002Froot\u002F.cache\u002Fpip \\\n",[203,815,816],{"class":205,"line":218},[203,817,818],{},"    pip install -r requirements.txt\n",[203,820,821],{"class":205,"line":224},[203,822,228],{"emptyLinePlaceholder":227},[203,824,825],{"class":205,"line":231},[203,826,827],{},"# Persist npm cache\n",[203,829,830],{"class":205,"line":237},[203,831,832],{},"RUN --mount=type=cache,target=\u002Froot\u002F.npm \\\n",[203,834,835],{"class":205,"line":243},[203,836,837],{},"    npm ci\n",[203,839,840],{"class":205,"line":248},[203,841,228],{"emptyLinePlaceholder":227},[203,843,844],{"class":205,"line":254},[203,845,846],{},"# Persist Go module cache\n",[203,848,849],{"class":205,"line":377},[203,850,851],{},"RUN --mount=type=cache,target=\u002Fgo\u002Fpkg\u002Fmod \\\n",[203,853,854],{"class":205,"line":382},[203,855,856],{},"    --mount=type=cache,target=\u002Froot\u002F.cache\u002Fgo-build \\\n",[203,858,859],{"class":205,"line":388},[203,860,861],{},"    go build -o \u002Fapp\u002Fserver .\n",[203,863,864],{"class":205,"line":394},[203,865,228],{"emptyLinePlaceholder":227},[203,867,868],{"class":205,"line":399},[203,869,870],{},"# Persist apt cache\n",[203,872,873],{"class":205,"line":405},[203,874,875],{},"RUN --mount=type=cache,target=\u002Fvar\u002Fcache\u002Fapt \\\n",[203,877,878],{"class":205,"line":411},[203,879,880],{},"    --mount=type=cache,target=\u002Fvar\u002Flib\u002Fapt \\\n",[203,882,883],{"class":205,"line":416},[203,884,885],{},"    apt-get update && apt-get install -y curl\n",[40,887,889],{"id":888},"heredoc-syntax","Heredoc Syntax",[194,891,893],{"className":196,"code":892,"language":198,"meta":199,"style":199},"# Traditional approach (cumbersome escaping)\nRUN echo \"server {\" > \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf && \\\n    echo \"  listen 80;\" >> \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf && \\\n    echo \"  location \u002F { proxy_pass http:\u002F\u002Fapp:8080; }\" >> \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf && \\\n    echo \"}\" >> \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf\n\n# BuildKit heredoc (readable)\nCOPY \u003C\u003CEOF \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf\nserver {\n    listen 80;\n    location \u002F {\n        proxy_pass http:\u002F\u002Fapp:8080;\n    }\n}\nEOF\n",[78,894,895,900,905,910,915,920,924,929,934,939,944,949,954,959,964],{"__ignoreMap":199},[203,896,897],{"class":205,"line":206},[203,898,899],{},"# Traditional approach (cumbersome escaping)\n",[203,901,902],{"class":205,"line":212},[203,903,904],{},"RUN echo \"server {\" > \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf && \\\n",[203,906,907],{"class":205,"line":218},[203,908,909],{},"    echo \"  listen 80;\" >> \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf && \\\n",[203,911,912],{"class":205,"line":224},[203,913,914],{},"    echo \"  location \u002F { proxy_pass http:\u002F\u002Fapp:8080; }\" >> \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf && \\\n",[203,916,917],{"class":205,"line":231},[203,918,919],{},"    echo \"}\" >> \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf\n",[203,921,922],{"class":205,"line":237},[203,923,228],{"emptyLinePlaceholder":227},[203,925,926],{"class":205,"line":243},[203,927,928],{},"# BuildKit heredoc (readable)\n",[203,930,931],{"class":205,"line":248},[203,932,933],{},"COPY \u003C\u003CEOF \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf\n",[203,935,936],{"class":205,"line":254},[203,937,938],{},"server {\n",[203,940,941],{"class":205,"line":377},[203,942,943],{},"    listen 80;\n",[203,945,946],{"class":205,"line":382},[203,947,948],{},"    location \u002F {\n",[203,950,951],{"class":205,"line":388},[203,952,953],{},"        proxy_pass http:\u002F\u002Fapp:8080;\n",[203,955,956],{"class":205,"line":394},[203,957,958],{},"    }\n",[203,960,961],{"class":205,"line":399},[203,962,963],{},"}\n",[203,965,966],{"class":205,"line":405},[203,967,968],{},"EOF\n",[26,970,972],{"id":971},"_5-image-size-reduction-techniques","5. Image Size Reduction Techniques",[40,974,976],{"id":975},"prefer-copy-over-add","Prefer COPY Over ADD",[194,978,980],{"className":196,"code":979,"language":198,"meta":199,"style":199},"# Avoid: ADD downloads remote URLs and auto-extracts tarballs\nADD https:\u002F\u002Fexample.com\u002Fapp.tar.gz \u002Fapp\u002F\n\n# Preferred: COPY is explicit local file copy only\nCOPY app\u002F \u002Fapp\u002F\n",[78,981,982,987,992,996,1001],{"__ignoreMap":199},[203,983,984],{"class":205,"line":206},[203,985,986],{},"# Avoid: ADD downloads remote URLs and auto-extracts tarballs\n",[203,988,989],{"class":205,"line":212},[203,990,991],{},"ADD https:\u002F\u002Fexample.com\u002Fapp.tar.gz \u002Fapp\u002F\n",[203,993,994],{"class":205,"line":218},[203,995,228],{"emptyLinePlaceholder":227},[203,997,998],{"class":205,"line":224},[203,999,1000],{},"# Preferred: COPY is explicit local file copy only\n",[203,1002,1003],{"class":205,"line":231},[203,1004,1005],{},"COPY app\u002F \u002Fapp\u002F\n",[11,1007,1008,1011,1012,1015],{},[78,1009,1010],{},"ADD"," can cause unexpected behavior — always use ",[78,1013,1014],{},"COPY"," for local files.",[40,1017,1019],{"id":1018},"eliminate-unnecessary-packages","Eliminate Unnecessary Packages",[194,1021,1023],{"className":196,"code":1022,"language":198,"meta":199,"style":199},"# Exclude recommended packages with --no-install-recommends\nRUN apt-get update && \\\n    apt-get install -y --no-install-recommends \\\n      ca-certificates \\\n      curl \\\n    && rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n\n# On Alpine, disable apk cache with --no-cache\nRUN apk add --no-cache curl\n",[78,1024,1025,1030,1034,1038,1043,1047,1051,1055,1060],{"__ignoreMap":199},[203,1026,1027],{"class":205,"line":206},[203,1028,1029],{},"# Exclude recommended packages with --no-install-recommends\n",[203,1031,1032],{"class":205,"line":212},[203,1033,481],{},[203,1035,1036],{"class":205,"line":218},[203,1037,486],{},[203,1039,1040],{"class":205,"line":224},[203,1041,1042],{},"      ca-certificates \\\n",[203,1044,1045],{"class":205,"line":231},[203,1046,491],{},[203,1048,1049],{"class":205,"line":237},[203,1050,501],{},[203,1052,1053],{"class":205,"line":243},[203,1054,228],{"emptyLinePlaceholder":227},[203,1056,1057],{"class":205,"line":248},[203,1058,1059],{},"# On Alpine, disable apk cache with --no-cache\n",[203,1061,1062],{"class":205,"line":254},[203,1063,1064],{},"RUN apk add --no-cache curl\n",[40,1066,1068],{"id":1067},"multi-stage-build-patterns","Multi-Stage Build Patterns",[194,1070,1072],{"className":196,"code":1071,"language":198,"meta":199,"style":199},"FROM node:20-alpine AS builder\nWORKDIR \u002Fapp\nCOPY package*.json .\u002F\nRUN npm ci\nCOPY . .\nRUN npm run build && npm prune --production\n\nFROM node:20-alpine\nWORKDIR \u002Fapp\nCOPY --from=builder \u002Fapp\u002Fdist .\u002Fdist\nCOPY --from=builder \u002Fapp\u002Fnode_modules .\u002Fnode_modules\nCOPY --from=builder \u002Fapp\u002Fpackage.json .\u002F\nUSER node\nCMD [\"node\", \"dist\u002Findex.js\"]\n",[78,1073,1074,1078,1082,1087,1091,1095,1100,1104,1109,1113,1118,1123,1128,1133],{"__ignoreMap":199},[203,1075,1076],{"class":205,"line":206},[203,1077,341],{},[203,1079,1080],{"class":205,"line":212},[203,1081,369],{},[203,1083,1084],{"class":205,"line":218},[203,1085,1086],{},"COPY package*.json .\u002F\n",[203,1088,1089],{"class":205,"line":224},[203,1090,391],{},[203,1092,1093],{"class":205,"line":231},[203,1094,408],{},[203,1096,1097],{"class":205,"line":237},[203,1098,1099],{},"RUN npm run build && npm prune --production\n",[203,1101,1102],{"class":205,"line":243},[203,1103,228],{"emptyLinePlaceholder":227},[203,1105,1106],{"class":205,"line":248},[203,1107,1108],{},"FROM node:20-alpine\n",[203,1110,1111],{"class":205,"line":254},[203,1112,369],{},[203,1114,1115],{"class":205,"line":377},[203,1116,1117],{},"COPY --from=builder \u002Fapp\u002Fdist .\u002Fdist\n",[203,1119,1120],{"class":205,"line":382},[203,1121,1122],{},"COPY --from=builder \u002Fapp\u002Fnode_modules .\u002Fnode_modules\n",[203,1124,1125],{"class":205,"line":388},[203,1126,1127],{},"COPY --from=builder \u002Fapp\u002Fpackage.json .\u002F\n",[203,1129,1130],{"class":205,"line":394},[203,1131,1132],{},"USER node\n",[203,1134,1135],{"class":205,"line":399},[203,1136,1137],{},"CMD [\"node\", \"dist\u002Findex.js\"]\n",[11,1139,1140,1141,1146],{},"The ",[18,1142,1145],{"href":1143,"rel":1144},"https:\u002F\u002Fwww.devopstraininginstitute.com\u002Fblog\u002F10-best-practices-for-docker-image-optimization",[22],"DevOps Training Institute optimization guide"," reports that combining these techniques achieves over 80% size reduction.",[40,1148,1150],{"id":1149},"regular-image-rebuilds","Regular Image Rebuilds",[11,1152,1153,1154,1157,1158,1163,1164,1169,1170,1174],{},"Rebuild production images ",[261,1155,1156],{},"at least monthly"," to apply the latest base image security patches. Automate this with ",[18,1159,1162],{"href":1160,"rel":1161},"https:\u002F\u002Fgithub.com\u002Fdependabot",[22],"Dependabot"," or ",[18,1165,1168],{"href":1166,"rel":1167},"https:\u002F\u002Fwww.mend.io\u002Frenovate\u002F",[22],"Renovate"," as ",[18,1171,1173],{"href":35,"rel":1172},[22],"Docker officially recommends",".",[26,1176,1178],{"id":1177},"_6-defining-health-checks","6. Defining Health Checks",[194,1180,1182],{"className":196,"code":1181,"language":198,"meta":199,"style":199},"HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n  CMD curl -f http:\u002F\u002Flocalhost:8080\u002Fhealthz || exit 1\n",[78,1183,1184,1189],{"__ignoreMap":199},[203,1185,1186],{"class":205,"line":206},[203,1187,1188],{},"HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n",[203,1190,1191],{"class":205,"line":212},[203,1192,1193],{},"  CMD curl -f http:\u002F\u002Flocalhost:8080\u002Fhealthz || exit 1\n",[11,1195,1140,1196,1199],{},[78,1197,1198],{},"HEALTHCHECK"," instruction allows Docker to determine container health. While Kubernetes replaces this with liveness\u002Freadiness Probes, it remains valuable in Docker Compose environments.",[26,1201,1203],{"id":1202},"summary-2025-dockerfile-checklist","Summary: 2025 Dockerfile Checklist",[11,1205,1206],{},"The definitive checklist for production-quality Dockerfiles:",[1208,1209,1212,1222,1228,1234,1240,1246,1252,1258,1264,1270,1276],"ul",{"className":1210},[1211],"contains-task-list",[1213,1214,1217,1221],"li",{"className":1215},[1216],"task-list-item",[1218,1219],"input",{"disabled":227,"type":1220},"checkbox"," select minimal base images (alpine - distroless - scratch)",[1213,1223,1225,1227],{"className":1224},[1216],[1218,1226],{"disabled":227,"type":1220}," Pin versions for reproducibility",[1213,1229,1231,1233],{"className":1230},[1216],[1218,1232],{"disabled":227,"type":1220}," Place infrequently changing instructions first (cache optimization)",[1213,1235,1237,1239],{"className":1236},[1216],[1218,1238],{"disabled":227,"type":1220}," Consolidate RUN instructions and clean caches",[1213,1241,1243,1245],{"className":1242},[1216],[1218,1244],{"disabled":227,"type":1220}," Run as non-root user (UID 10000+)",[1213,1247,1249,1251],{"className":1248},[1216],[1218,1250],{"disabled":227,"type":1220}," Minimize build context with .dockerignore",[1213,1253,1255,1257],{"className":1254},[1216],[1218,1256],{"disabled":227,"type":1220}," Manage secrets via BuildKit mounts",[1213,1259,1261,1263],{"className":1260},[1216],[1218,1262],{"disabled":227,"type":1220}," Separate production and development with multi-stage builds",[1213,1265,1267,1269],{"className":1266},[1216],[1218,1268],{"disabled":227,"type":1220}," Prefer COPY over ADD",[1213,1271,1273,1275],{"className":1272},[1216],[1218,1274],{"disabled":227,"type":1220}," Define HEALTHCHECK",[1213,1277,1279,1281],{"className":1278},[1216],[1218,1280],{"disabled":227,"type":1220}," Schedule regular rebuild cycles",[11,1283,1284,1287,1288,1291],{},[18,1285,23],{"href":20,"rel":1286},[22]," efficiently runs container images that follow these best practices on Kubernetes clusters. With ",[18,1289,314],{"href":312,"rel":1290},[22],", automated Dockerfile reviews and optimization suggestions elevate your entire team's Dockerfile quality.",[11,1293,1294,1295,1174],{},"For Dockerfile optimization and container operations consulting, please ",[18,1296,1299],{"href":1297,"rel":1298},"https:\u002F\u002Fwww.hexabase.com\u002Fcontact-us\u002F",[22],"contact us",[1301,1302,1303],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sbD-w, html code.shiki .sbD-w{--shiki-default:#51597D;--shiki-default-font-style:italic}html pre.shiki code .sN7LL, html code.shiki .sN7LL{--shiki-default:#9D7CD8;--shiki-default-font-style:italic}html pre.shiki code .sE3pS, html code.shiki .sE3pS{--shiki-default:#C0CAF5}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .sOJ5S, html code.shiki .sOJ5S{--shiki-default:#FF9E64}html pre.shiki code .sPY7s, html code.shiki .sPY7s{--shiki-default:#9ECE6A}html pre.shiki code .sT800, html code.shiki .sT800{--shiki-default:#E0AF68}",{"title":199,"searchDepth":212,"depth":212,"links":1305},[1306,1309,1313,1318,1323,1329,1330],{"id":28,"depth":212,"text":29,"children":1307},[1308],{"id":42,"depth":218,"text":43},{"id":318,"depth":212,"text":319,"children":1310},[1311,1312],{"id":325,"depth":218,"text":326},{"id":441,"depth":218,"text":442},{"id":504,"depth":212,"text":505,"children":1314},[1315,1316,1317],{"id":517,"depth":218,"text":518},{"id":572,"depth":218,"text":573},{"id":623,"depth":218,"text":624},{"id":722,"depth":212,"text":723,"children":1319},[1320,1321,1322],{"id":729,"depth":218,"text":730},{"id":794,"depth":218,"text":795},{"id":888,"depth":218,"text":889},{"id":971,"depth":212,"text":972,"children":1324},[1325,1326,1327,1328],{"id":975,"depth":218,"text":976},{"id":1018,"depth":218,"text":1019},{"id":1067,"depth":218,"text":1068},{"id":1149,"depth":218,"text":1150},{"id":1177,"depth":212,"text":1178},{"id":1202,"depth":212,"text":1203},"2026-05-27","Comprehensive 2025 Dockerfile best practices guide covering minimal base images, layer optimization, security hardening, BuildKit features, and production-ready container image construction.","md","en",{},"\u002Fblog\u002Fen\u002Fdockerfile-best-practices-2025",{"title":5,"description":1332},"blog\u002Fen\u002Fdockerfile-best-practices-2025",[1340,1341,1342,1343,1344,1345,1346],"Dockerfile","Docker","Best Practices","Containers","Security","BuildKit","Optimization","B86_N9DS0ELhqK1B2l92t38-32-5-NVScnyhe4O78M0",1779964618819]