build: compress linux binary with UPX in builder-linux stage#2934
Closed
zampani-docker wants to merge 1 commit into
Closed
build: compress linux binary with UPX in builder-linux stage#2934zampani-docker wants to merge 1 commit into
zampani-docker wants to merge 1 commit into
Conversation
Adds UPX compression to the production Docker build, reducing the binary from ~110 MB to ~28.7 MB (73.8% reduction). Uses default compression level (7) rather than --best, which takes 9+ minutes on this binary size with negligible additional compression gain. UPX is installed in builder-base alongside clang and zig (all host-arch build tools), so it is cached once in the base layer and inherited by builder-linux without a per-variant network fetch. Compression runs in its own RUN step after the Go build layer so the two are cached independently. upx -t validates the packed binary integrity before the image is finalized. Operational tradeoffs (accepted): - Runtime RSS is ~110 MB regardless of the 28.7 MB on-disk size - ~300-800 ms startup decompression on cold invocations - UPX-packed binaries may trigger AV/EDR heuristics in some environments Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟡 NEEDS ATTENTION
One medium-severity finding related to UPX version compatibility for targets.
| xx-go build -trimpath -tags no_audio -ldflags "-s -w -linkmode=external -X 'github.com/docker/docker-agent/pkg/version.Version=$GIT_TAG' -X 'github.com/docker/docker-agent/pkg/version.Commit=$GIT_COMMIT'" -o /binaries/docker-agent-$TARGETOS-$TARGETARCH . | ||
| xx-verify --static /binaries/docker-agent-$TARGETOS-$TARGETARCH | ||
| EOT | ||
| RUN upx /binaries/docker-agent-$TARGETOS-$TARGETARCH && \ |
There was a problem hiding this comment.
[MEDIUM] UPX arm64 compatibility depends on version — build may fail for linux/arm64
UPX support for linux/arm64 ELF binaries was only added in UPX 4.0 (2023). Alpine's apk add upx installs whatever version the Alpine release ships:
- Alpine 3.17 and earlier → UPX 3.x → no arm64 support →
upxwill fail at build time withCantPackException: bad ELF e_machinewhenTARGETARCH=arm64 - Alpine 3.18+ → UPX 4.x → arm64 supported
The Dockerfile pins ALPINE_VERSION="3.22", so today this is fine. However:
- There is no floor on the UPX version, so a future Alpine base image re-tag that ships an older UPX could silently break
linux/arm64builds. - The PR description notes the binary was tested at
~114 MB → ~30 MBbut does not explicitly confirm arm64 (TARGETARCH=arm64) was part of the test run.
Suggested mitigation: Add an explicit upx --version check or pin the upx version (e.g., apk add upx~=4) to ensure arm64 support is guaranteed, and confirm the test plan covers both linux/amd64 and linux/arm64.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
builder-linuxstage, reducing the production binary from ~110 MB to ~28.7 MB (73.8% reduction)builder-basealongsideclangandzig(all host-arch build tools), so it is cached once and inherited — no per-variant re-fetchRUNlayer after the Go build layer for correct BuildKit cache separationupx -tvalidates packed binary integrity at build time before the image is finalizedbuilder-cross(macOS/Windows release binaries) intentionally unchanged — UPX on macOS requires re-signing and triggers AV on WindowsTest plan
versionand--helpboth work correctly after UPX decompressionupx -tintegrity check passes on the packed binaryDeferred findings (accepted tradeoffs)
mmapandmprotect(PROT_EXEC)on anonymous mappings. Docker's default seccomp profile allows both; custom hardened profiles may need to be updated.