[{"data":1,"prerenderedAt":582},["ShallowReactive",2],{"mdc--azjcl3-key":3,"mdc--fiwj4d-key":46,"mdc--rf110u-key":69,"mdc--yw5s4k-key":89,"mdc-8m5xdc-key":101,"mdc--16wdx8-key":254,"mdc--b6s6mm-key":380,"mdc-bv5dq4-key":390,"mdc-184c7h-key":448,"mdc-pc9ug3-key":460},{"data":4,"body":5},{},{"type":6,"children":7},"root",[8,33],{"type":9,"tag":10,"props":11,"children":12},"element","p",{},[13,16,23,25,31],{"type":14,"value":15},"text","An image built on an Apple Silicon laptop is ",{"type":9,"tag":17,"props":18,"children":20},"code",{"className":19},[],[21],{"type":14,"value":22},"linux/arm64",{"type":14,"value":24},". Run it on a normal cloud VM and you get ",{"type":9,"tag":17,"props":26,"children":28},{"className":27},[],[29],{"type":14,"value":30},"exec format error",{"type":14,"value":32},", or — worse, because it looks like it works — silent QEMU emulation at a fraction of the speed.",{"type":9,"tag":10,"props":34,"children":35},{},[36,38,44],{"type":14,"value":37},"A ",{"type":9,"tag":39,"props":40,"children":41},"strong",{},[42],{"type":14,"value":43},"multi-platform image",{"type":14,"value":45}," is one tag that carries several architectures, with the registry handing each puller the right one.",{"data":47,"body":48},{},{"type":6,"children":49},[50,57],{"type":9,"tag":51,"props":52,"children":54},"h2",{"id":53},"what-is-actually-stored",[55],{"type":14,"value":56},"What is actually stored",{"type":9,"tag":10,"props":58,"children":59},{},[60,62,67],{"type":14,"value":61},"A multi-platform tag points at a ",{"type":9,"tag":39,"props":63,"children":64},{},[65],{"type":14,"value":66},"manifest list",{"type":14,"value":68}," (or \"image index\"): a small document listing one manifest per platform, each pointing at its own layers.",{"data":70,"body":71},{},{"type":6,"children":72},[73,78],{"type":9,"tag":74,"props":75,"children":77},"terminal-teaser",{":lines":76},"[{\"cmd\":\"docker buildx imagetools inspect nginx:1.29-alpine\",\"out\":\"Name:      docker.io/library/nginx:1.29-alpine\\nMediaType: application/vnd.oci.image.index.v1+json\\nManifests:\\n  Platform:  linux/amd64\\n  Platform:  linux/arm64\\n  Platform:  linux/arm/v7\\n  Platform:  linux/s390x\"}]",[],{"type":9,"tag":10,"props":79,"children":80},{},[81,87],{"type":9,"tag":17,"props":82,"children":84},{"className":83},[],[85],{"type":14,"value":86},"docker pull nginx:1.29-alpine",{"type":14,"value":88}," on an ARM machine fetches the arm64 manifest and nothing else. The selection happens in the client, from the index, at pull time.",{"data":90,"body":91},{},{"type":6,"children":92},[93],{"type":9,"tag":94,"props":95,"children":100},"quiz",{":answer":96,":options":97,"explanation":98,"question":99},"0","[\"A manifest list indexing one manifest per platform, each with its own layers\",\"A single image containing binaries for every architecture\",\"The amd64 image, with others converted on demand\"]","Nothing is fat and nothing is converted. The index is a few hundred bytes; the client reads it, picks the matching platform, and pulls only those layers.","What does a multi-platform tag actually point at in the registry?",[],{"data":102,"body":103},{},{"type":6,"children":104},[105,111,124,136,141,236],{"type":9,"tag":51,"props":106,"children":108},{"id":107},"you-need-a-different-builder",[109],{"type":14,"value":110},"You need a different builder",{"type":9,"tag":10,"props":112,"children":113},{},[114,116,122],{"type":14,"value":115},"The default ",{"type":9,"tag":17,"props":117,"children":119},{"className":118},[],[120],{"type":14,"value":121},"docker",{"type":14,"value":123}," driver builds one platform at a time. Multi-platform needs a builder that can hold several results:",{"type":9,"tag":125,"props":126,"children":130},"pre",{"className":127,"code":129,"language":14},[128],"language-text","docker buildx create --name multi --driver docker-container --use\ndocker buildx inspect --bootstrap\n",[131],{"type":9,"tag":17,"props":132,"children":134},{"__ignoreMap":133},"",[135],{"type":14,"value":129},{"type":9,"tag":10,"props":137,"children":138},{},[139],{"type":14,"value":140},"The drivers worth knowing:",{"type":9,"tag":142,"props":143,"children":144},"table",{},[145,164],{"type":9,"tag":146,"props":147,"children":148},"thead",{},[149],{"type":9,"tag":150,"props":151,"children":152},"tr",{},[153,159],{"type":9,"tag":154,"props":155,"children":156},"th",{},[157],{"type":14,"value":158},"Driver",{"type":9,"tag":154,"props":160,"children":161},{},[162],{"type":14,"value":163},"What it is",{"type":9,"tag":165,"props":166,"children":167},"tbody",{},[168,185,202,219],{"type":9,"tag":150,"props":169,"children":170},{},[171,180],{"type":9,"tag":172,"props":173,"children":174},"td",{},[175],{"type":9,"tag":17,"props":176,"children":178},{"className":177},[],[179],{"type":14,"value":121},{"type":9,"tag":172,"props":181,"children":182},{},[183],{"type":14,"value":184},"the default, built into the daemon. One platform per build.",{"type":9,"tag":150,"props":186,"children":187},{},[188,197],{"type":9,"tag":172,"props":189,"children":190},{},[191],{"type":9,"tag":17,"props":192,"children":194},{"className":193},[],[195],{"type":14,"value":196},"docker-container",{"type":9,"tag":172,"props":198,"children":199},{},[200],{"type":14,"value":201},"BuildKit in a container. Multi-platform, cache export, most features.",{"type":9,"tag":150,"props":203,"children":204},{},[205,214],{"type":9,"tag":172,"props":206,"children":207},{},[208],{"type":9,"tag":17,"props":209,"children":211},{"className":210},[],[212],{"type":14,"value":213},"remote",{"type":9,"tag":172,"props":215,"children":216},{},[217],{"type":14,"value":218},"a BuildKit instance you connect to over the network.",{"type":9,"tag":150,"props":220,"children":221},{},[222,231],{"type":9,"tag":172,"props":223,"children":224},{},[225],{"type":9,"tag":17,"props":226,"children":228},{"className":227},[],[229],{"type":14,"value":230},"cloud",{"type":9,"tag":172,"props":232,"children":233},{},[234],{"type":14,"value":235},"Docker Build Cloud — managed native builders for each architecture.",{"type":9,"tag":10,"props":237,"children":238},{},[239,244,246,252],{"type":9,"tag":17,"props":240,"children":242},{"className":241},[],[243],{"type":14,"value":196},{"type":14,"value":245}," is the one to reach for locally, and the catch is that it cannot load a multi-platform result into your local image store. Push it, or build one platform at a time with ",{"type":9,"tag":17,"props":247,"children":249},{"className":248},[],[250],{"type":14,"value":251},"--load",{"type":14,"value":253},".",{"data":255,"body":256},{},{"type":6,"children":257},[258,264,274,283,288,298,309,351,356,366,375],{"type":9,"tag":51,"props":259,"children":261},{"id":260},"the-three-ways-to-build-for-another-architecture",[262],{"type":14,"value":263},"The three ways to build for another architecture",{"type":9,"tag":10,"props":265,"children":266},{},[267,272],{"type":9,"tag":39,"props":268,"children":269},{},[270],{"type":14,"value":271},"Emulation.",{"type":14,"value":273}," Easiest, slowest. QEMU translates instructions so an arm64 build runs on an amd64 machine unchanged.",{"type":9,"tag":125,"props":275,"children":278},{"className":276,"code":277,"language":14},[128],"docker run --privileged --rm tonistiigi/binfmt --install all\n\ndocker buildx build --platform linux/amd64,linux/arm64 -t acme/app:1.0 --push .\n",[279],{"type":9,"tag":17,"props":280,"children":281},{"__ignoreMap":133},[282],{"type":14,"value":277},{"type":9,"tag":10,"props":284,"children":285},{},[286],{"type":14,"value":287},"No Dockerfile changes. Docker Desktop bundles QEMU already. But anything compute-heavy — compiling, running a test suite — gets very slow, often ten times or worse.",{"type":9,"tag":10,"props":289,"children":290},{},[291,296],{"type":9,"tag":39,"props":292,"children":293},{},[294],{"type":14,"value":295},"Cross-compilation.",{"type":14,"value":297}," Fastest, and language-dependent. Build natively and target the other architecture, using the build arguments BuildKit provides:",{"type":9,"tag":125,"props":299,"children":304},{"className":300,"code":302,"language":303,"meta":133},[301],"language-dockerfile","# syntax=docker/dockerfile:1\nFROM --platform=$BUILDPLATFORM golang:1.25-alpine AS build\nARG TARGETOS TARGETARCH\nWORKDIR /src\nCOPY . .\nRUN --mount=type=cache,target=/root/.cache/go-build \\\n    GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /out/server ./cmd/server\n\nFROM alpine:3.22\nCOPY --from=build /out/server /usr/local/bin/server\nENTRYPOINT [\"server\"]\n","dockerfile",[305],{"type":9,"tag":17,"props":306,"children":307},{"__ignoreMap":133},[308],{"type":14,"value":302},{"type":9,"tag":10,"props":310,"children":311},{},[312,318,320,325,327,333,335,341,343,349],{"type":9,"tag":17,"props":313,"children":315},{"className":314},[],[316],{"type":14,"value":317},"--platform=$BUILDPLATFORM",{"type":14,"value":319}," pins the build stage to the ",{"type":9,"tag":39,"props":321,"children":322},{},[323],{"type":14,"value":324},"builder's native",{"type":14,"value":326}," architecture, so the compiler runs at full speed. ",{"type":9,"tag":17,"props":328,"children":330},{"className":329},[],[331],{"type":14,"value":332},"$TARGETOS",{"type":14,"value":334}," and ",{"type":9,"tag":17,"props":336,"children":338},{"className":337},[],[339],{"type":14,"value":340},"$TARGETARCH",{"type":14,"value":342}," say what to produce. The final stage has no ",{"type":9,"tag":17,"props":344,"children":346},{"className":345},[],[347],{"type":14,"value":348},"--platform",{"type":14,"value":350},", so it is built for each target — it only copies a binary, which is free.",{"type":9,"tag":10,"props":352,"children":353},{},[354],{"type":14,"value":355},"Go, Rust, .NET and anything else with a real cross-compiler suit this. Interpreted languages with native extensions usually do not.",{"type":9,"tag":10,"props":357,"children":358},{},[359,364],{"type":9,"tag":39,"props":360,"children":361},{},[362],{"type":14,"value":363},"Native nodes.",{"type":14,"value":365}," Best of both, most setup: a builder with a real machine per architecture.",{"type":9,"tag":125,"props":367,"children":370},{"className":368,"code":369,"language":14},[128],"docker buildx create --name multi --node amd64 --platform linux/amd64\ndocker buildx create --append --name multi --node arm64 --platform linux/arm64\n",[371],{"type":9,"tag":17,"props":372,"children":373},{"__ignoreMap":133},[374],{"type":14,"value":369},{"type":9,"tag":10,"props":376,"children":377},{},[378],{"type":14,"value":379},"Each stage runs natively on the matching node. This is what Docker Build Cloud sells as a service.",{"data":381,"body":382},{},{"type":6,"children":383},[384],{"type":9,"tag":94,"props":385,"children":389},{":answer":96,":options":386,"explanation":387,"question":388},"[\"It pins the toolchain to the builder's own architecture so the compiler runs natively instead of under emulation\",\"It skips the build stage for non-native platforms\",\"It caches the stage across platforms\"]","Without it, BuildKit builds that stage once per target platform — under QEMU for the foreign one, which is where all the time goes. With it, one native compiler run produces each target binary via `GOOS`/`GOARCH`.","Why does `FROM --platform=$BUILDPLATFORM` on the build stage speed up a cross-compiled multi-arch build?",[],{"data":391,"body":392},{},{"type":6,"children":393},[394,400,409,434,439],{"type":9,"tag":51,"props":395,"children":397},{"id":396},"building-and-shipping-it",[398],{"type":14,"value":399},"Building and shipping it",{"type":9,"tag":125,"props":401,"children":404},{"className":402,"code":403,"language":14},[128],"docker buildx build \\\n  --platform linux/amd64,linux/arm64 \\\n  -t ghcr.io/acme/app:1.0 \\\n  --push .\n",[405],{"type":9,"tag":17,"props":406,"children":407},{"__ignoreMap":133},[408],{"type":14,"value":403},{"type":9,"tag":10,"props":410,"children":411},{},[412,418,420,425,427,432],{"type":9,"tag":17,"props":413,"children":415},{"className":414},[],[416],{"type":14,"value":417},"--push",{"type":14,"value":419}," matters. A multi-platform result cannot be loaded into the local image store by the ",{"type":9,"tag":17,"props":421,"children":423},{"className":422},[],[424],{"type":14,"value":196},{"type":14,"value":426}," driver, so ",{"type":9,"tag":17,"props":428,"children":430},{"className":429},[],[431],{"type":14,"value":251},{"type":14,"value":433}," fails and building without either output flag leaves you with nothing but a warmed cache.",{"type":9,"tag":10,"props":435,"children":436},{},[437],{"type":14,"value":438},"Building only for your own machine while iterating:",{"type":9,"tag":125,"props":440,"children":443},{"className":441,"code":442,"language":14},[128],"docker buildx build --load -t app:dev .\n",[444],{"type":9,"tag":17,"props":445,"children":446},{"__ignoreMap":133},[447],{"type":14,"value":442},{"data":449,"body":450},{},{"type":6,"children":451},[452],{"type":9,"tag":453,"props":454,"children":459},"fill-blank",{":answer":455,"hint":456,"placeholder":457,"prompt":458},"[\"docker buildx build --platform linux/amd64,linux/arm64 -t ghcr.io/acme/app:1.0 --push .\",\"docker buildx build --platform linux/arm64,linux/amd64 -t ghcr.io/acme/app:1.0 --push .\",\"docker build --platform linux/amd64,linux/arm64 -t ghcr.io/acme/app:1.0 --push .\"]","Platforms comma-separated in one flag, and an output flag so the result goes somewhere.","docker buildx build ...","Build for both linux/amd64 and linux/arm64 and push the result as `ghcr.io/acme/app:1.0`.",[],{"data":461,"body":462},{},{"type":6,"children":463},[464,577],{"type":9,"tag":465,"props":466,"children":468},"deep-dive",{"title":467},"What Engine 29's image store changed here",[469,489,501,540,551],{"type":9,"tag":10,"props":470,"children":471},{},[472,474,479,481,487],{"type":14,"value":473},"Multi-platform images used to be awkward locally for one specific reason: ",{"type":9,"tag":39,"props":475,"children":476},{},[477],{"type":14,"value":478},"the old image store could hold only one architecture per tag.",{"type":14,"value":480}," So a multi-platform build had to go straight to a registry — you could not keep the result, and ",{"type":9,"tag":17,"props":482,"children":484},{"className":483},[],[485],{"type":14,"value":486},"docker image save",{"type":14,"value":488}," gave you one platform silently.",{"type":9,"tag":10,"props":490,"children":491},{},[492,494,499],{"type":14,"value":493},"Engine 29 made the ",{"type":9,"tag":39,"props":495,"children":496},{},[497],{"type":14,"value":498},"containerd image store the default on fresh installs",{"type":14,"value":500},", and it holds full indexes. Consequences:",{"type":9,"tag":502,"props":503,"children":504},"ul",{},[505,530,535],{"type":9,"tag":506,"props":507,"children":508},"li",{},[509,514,515,521,523,528],{"type":9,"tag":17,"props":510,"children":512},{"className":511},[],[513],{"type":14,"value":486},{"type":14,"value":334},{"type":9,"tag":17,"props":516,"children":518},{"className":517},[],[519],{"type":14,"value":520},"docker image load",{"type":14,"value":522}," take ",{"type":9,"tag":17,"props":524,"children":526},{"className":525},[],[527],{"type":14,"value":348},{"type":14,"value":529}," and handle multiple platforms.",{"type":9,"tag":506,"props":531,"children":532},{},[533],{"type":14,"value":534},"You can inspect and keep multi-platform images locally rather than round-tripping through a registry.",{"type":9,"tag":506,"props":536,"children":537},{},[538],{"type":14,"value":539},"Attestations attached to a build are stored properly instead of being dropped.",{"type":9,"tag":10,"props":541,"children":542},{},[543,549],{"type":9,"tag":17,"props":544,"children":546},{"className":545},[],[547],{"type":14,"value":548},"docker info",{"type":14,"value":550}," tells you which store you have; installations that upgraded rather than being freshly installed keep the old one.",{"type":9,"tag":10,"props":552,"children":553},{},[554,559,561,567,569,575],{"type":9,"tag":39,"props":555,"children":556},{},[557],{"type":14,"value":558},"Two habits regardless of store.",{"type":14,"value":560}," Check what you actually produced — ",{"type":9,"tag":17,"props":562,"children":564},{"className":563},[],[565],{"type":14,"value":566},"docker buildx imagetools inspect \u003Ctag>",{"type":14,"value":568}," lists the platforms in a published index. And check what you are running when something is inexplicably slow: ",{"type":9,"tag":17,"props":570,"children":572},{"className":571},[],[573],{"type":14,"value":574},"docker image inspect -f '{{.Architecture}}'",{"type":14,"value":576},", because an amd64 image quietly emulated on arm64 does not announce itself, it just runs at a third of the speed.",{"type":9,"tag":10,"props":578,"children":579},{},[580],{"type":14,"value":581},"Next up: Bake — the declarative file that replaces a page of buildx flags, and which Compose v5 now builds through.",1787908867687]