Skip to content

Add UPX Binary Compression to Reduce Release Sizes#85

Open
remmody wants to merge 2 commits intoDanielLavrushin:mainfrom
remmody:main
Open

Add UPX Binary Compression to Reduce Release Sizes#85
remmody wants to merge 2 commits intoDanielLavrushin:mainfrom
remmody:main

Conversation

@remmody
Copy link
Contributor

@remmody remmody commented Mar 3, 2026

Overview

This PR adds UPX compression to both Docker builds and the GitHub Actions release workflow, significantly reducing binary sizes by ~60–70% without runtime performance impact.


Changes

Dockerfile (Stage 2.5: UPX Compression)

  • Added a dedicated compression stage using alpine:3.23.3 with UPX
  • Compression flags: --ultra-brute --lzma for maximum size reduction
  • Validation: Tests compressed binary with upx -t before deployment
  • Final binary copied to runtime image as /usr/local/bin/b4

GitHub Actions Release Workflow

  • Installed upx-ucl package in Ubuntu runner
  • Automated compression for all Linux architectures:
    • 386
    • amd64
    • arm64
    • armv5-7
    • mips*
    • ppc64*
    • riscv64
    • s390x
    • loong64
  • Compression applied after build but before tar.gz packaging
  • Safety fallback: Keeps original binary if UPX test fails
  • Regenerates SHA256 checksums after compression
  • Repackages tar.gz archives with compressed binaries

Benefits

  • Smaller Docker images: ~60–70% reduction in image size
  • Faster downloads: Smaller release artifacts for all architectures
  • No runtime overhead: Decompression happens once at startup
  • Bandwidth savings: Reduced storage and transfer costs
  • Validated compression: Automatic testing ensures binary integrity

Technical Details

  • UPX version: Latest from Alpine 3.23.3 repository
  • Compression method: LZMA with ultra-brute optimization
  • Applied to: All Linux release binaries and Docker images
  • Platforms:
    • linux/amd64
    • linux/arm64
    • linux/arm/v7
    • linux/arm/v6

Testing

  • ✅ Docker build successful with compressed binary
  • ✅ Compressed binary passes upx -t validation
  • ✅ Application runs correctly after decompression
  • ✅ All architectures supported in release workflow

Commit

05e7226 — feat(build): add UPX compression to reduce binary size

remmody added 2 commits March 3, 2026 19:17
- Added UPX compression stage to Dockerfile (Stage 2.5)
- Integrated UPX compression into GitHub Actions release workflow
- Using --ultra-brute --lzma flags for maximum compression
- Added compression validation with upx -t before replacing binary
- Repackaging tar.gz archives with compressed binaries
- Regenerating SHA256 checksums after compression
- Installed upx-ucl package in Ubuntu runner
- Compression applied to all Linux architectures in release builds
- Fallback to original binary if UPX test fails
- Reduces Docker image and release artifact sizes significantly
@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 3, 2026

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces UPX compression in both the Docker build pipeline and the GitHub Actions release workflow to reduce the size of shipped Linux binaries and resulting artifacts.

Changes:

  • Added a dedicated Docker build stage to compress the built b4 binary with UPX before copying it into the runtime image.
  • Updated the release workflow to install UPX, compress per-arch Linux binaries, and regenerate tar.gz assets + SHA256 checksums after compression.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
Dockerfile Adds an UPX compression stage and swaps the runtime image to use the compressed binary.
.github/workflows/release.yml Installs UPX and compresses per-arch outputs before repackaging release tarballs and checksums.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

-o /b4

# Stage 2.5: UPX compression
FROM --platform=$TARGETPLATFORM alpine:3.23.3 AS upx-compressor
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UPX stage is pinned to --platform=$TARGETPLATFORM, which means during cross-platform builds (e.g., building linux/arm64 on an amd64 runner) the upx binary itself will run under QEMU emulation. That typically slows builds significantly and can fail if emulation isn’t available. Consider running the compressor stage on $BUILDPLATFORM (tooling runs native) while still compressing the target-arch /b4 output.

Suggested change
FROM --platform=$TARGETPLATFORM alpine:3.23.3 AS upx-compressor
FROM --platform=$BUILDPLATFORM alpine:3.23.3 AS upx-compressor

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +46
RUN apk add --no-cache upx

COPY --from=go-builder /b4 /b4
RUN upx --ultra-brute --lzma /b4 -o /b4.upx && upx -t /b4.upx
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

upx --ultra-brute --lzma is extremely CPU-intensive and can add a large amount of time to Docker builds (especially under QEMU when cross-building). Consider switching to a less expensive preset (e.g., --best) or making the compression level configurable via a build arg so release builds can opt into ultra-brute when needed.

Suggested change
RUN apk add --no-cache upx
COPY --from=go-builder /b4 /b4
RUN upx --ultra-brute --lzma /b4 -o /b4.upx && upx -t /b4.upx
ARG UPX_FLAGS="--best"
RUN apk add --no-cache upx
COPY --from=go-builder /b4 /b4
RUN upx ${UPX_FLAGS} /b4 -o /b4.upx && upx -t /b4.upx

Copilot uses AI. Check for mistakes.
Comment on lines +123 to +130
upx --ultra-brute --lzma "$BINARY_PATH" -o "${BINARY_PATH}.upx"

# Test compressed binary
if upx -t "${BINARY_PATH}.upx"; then
echo "UPX compression successful, replacing original binary"
mv "${BINARY_PATH}.upx" "$BINARY_PATH"
else
echo "UPX test failed, keeping original binary"
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This step intends to keep the original binary if UPX fails, but with GitHub Actions’ default bash -e behavior the command upx --ultra-brute --lzma ... will fail the step immediately on any non-zero exit code (e.g., unsupported arch/format), and the fallback logic below won’t run. Wrap the compression command in an if upx ...; then ... else ... block (or otherwise handle the exit status) so the fallback actually works.

Suggested change
upx --ultra-brute --lzma "$BINARY_PATH" -o "${BINARY_PATH}.upx"
# Test compressed binary
if upx -t "${BINARY_PATH}.upx"; then
echo "UPX compression successful, replacing original binary"
mv "${BINARY_PATH}.upx" "$BINARY_PATH"
else
echo "UPX test failed, keeping original binary"
if upx --ultra-brute --lzma "$BINARY_PATH" -o "${BINARY_PATH}.upx"; then
# Test compressed binary
if upx -t "${BINARY_PATH}.upx"; then
echo "UPX compression successful, replacing original binary"
mv "${BINARY_PATH}.upx" "$BINARY_PATH"
else
echo "UPX test failed, keeping original binary"
rm -f "${BINARY_PATH}.upx"
fi
else
echo "UPX compression failed (possibly unsupported arch/format), keeping original binary"

Copilot uses AI. Check for mistakes.
Comment on lines +123 to +124
upx --ultra-brute --lzma "$BINARY_PATH" -o "${BINARY_PATH}.upx"

Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--ultra-brute --lzma can make the release workflow substantially slower across the full arch matrix. Unless maximum compression is required, consider a faster default (e.g., --best) and/or make the compression level configurable (workflow input/env) so you can trade off size vs. build time.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants