The Linux standalone binaries are about 70 MiB as a single file and unpack to 152 MiB (the single-file variant does that in a temp dir at every start). The FreeBSD and macOS binaries are 40-42 MiB. Most of the difference is avoidable.
Measured on the borg-linux-glibc239-x86_64-gh artifact of CI run 34374266883 (PR #10344); the borg-linux-glibc243-x86_64-gh binary of 2.0.0b24 has the same size (70.2 MiB), so this is not specific to the new job.
Where the size goes
| item |
unpacked |
in the compressed binary |
note |
| debug info + symbol tables in the 97 bundled shared libraries |
~74 MiB |
~26 MiB |
Python's default CFLAGS contain -g, and scripts/borg.exe.spec has strip=False. libpython is 30.5 MiB (5.7 MiB stripped), borg's own Cython modules 22 MiB, the stdlib extension modules 21 MiB, then pyfuse3, yaml, msgpack, ... |
botocore AWS service models (botocore/data) |
23 MiB |
~14.5 MiB |
435 entries, already gzip-compressed JSON, so they hardly shrink. borgstore's S3 backend only creates an s3 client; it needs data/s3, endpoints.json, partitions.json, sdk-default-configuration.json, _retry.json: about 0.3 MiB compressed |
pure-Python code archive inside borg.exe (PYZ) |
14 MiB |
14 MiB |
textual, rich, paramiko, botocore's python code, borg itself |
cryptography's Rust extension (_rust.abi3.so) |
13.7 MiB |
4.4 MiB |
pulled in by paramiko (sftp extra); the manylinux wheel statically links its own copy of OpenSSL, so the binary contains two OpenSSLs |
| libcrypto |
6.9 MiB |
~2.5 MiB |
real |
Options
-
Strip the shared libraries on Linux (strip=True in the spec for Linux, or build Python and the extensions without -g).
- Measured: 152 -> 78 MiB unpacked, 69.6 -> 43.4 MiB compressed (
llvm-strip --strip-all on every .so, not on borg.exe, which carries the appended archive; then the same tar czf). A stripped bundle was tried on Debian 13: repo-create with aes256-ocb, create, list, check, extract all fine.
- Pro: by far the biggest win, halves the unpack size and thus the single-file startup cost, no functional change. Python tracebacks are unaffected.
- Con: no C-level symbols for gdb / core dumps of the frozen binary. The spec is shared by all platforms, so this should be conditional (
strip=sys.platform == "linux"): on macOS, stripping interferes with code signing, and the Windows/MSYS2 build is a different story. Building without -g instead of stripping only works for the from-source Python of the glibc239 job, not for the setup-python builds of the glibc243 job.
-
Bundle only the botocore service models borg needs (filter a.datas in the spec, or a custom hook overriding the botocore hook): s3, sts (assume-role profiles), plus the top-level json files.
- Saves about 14 MiB compressed / 23 MiB unpacked.
- Pro: second biggest win, trivial to implement in the spec.
- Con: has to be kept in sync with what borgstore uses; a missing model shows up as an obscure botocore error at runtime, so the S3 test in CI should cover it. Some credential providers (SSO, web identity) would need more models (
sso, sso-oidc).
-
Build cryptography from source against the bundled OpenSSL instead of using the manylinux wheel.
- Saves an estimated 3-5 MiB compressed (the vendored OpenSSL inside
_rust.abi3.so).
- Pro: one OpenSSL in the binary instead of two, i.e. one thing to update on an OpenSSL CVE.
- Con: needs a Rust toolchain in the job, several minutes of build time, and the
cryptography build against a non-vendored OpenSSL is more fragile across versions. Poor ratio of savings to effort.
-
Bundle mfusepy instead of pyfuse3 in the Linux binaries.
-
Drop extras from the binary (cockpit: textual + rich; s3: boto3/botocore; sftp: paramiko + cryptography + pynacl), or offer a "lite" binary next to the full one.
- Pro: the only way to get well below 30 MiB.
- Con: loses features, or doubles the release matrix and the user confusion. Not recommended as the default.
-
.tar.xz instead of .tgz for the single-directory variant.
- Pro: 20-30 % smaller download.
- Con: renames a release asset, slower to unpack, does nothing for the single-file binary or for the unpack size.
-
UPX: the spec has upx=True for the single-file binary, but UPX is not installed on the runners, so it is a no-op today.
- Con: on top of PyInstaller's own zlib compression it would gain little, and UPX-packed binaries trigger AV false positives and start slower. Not recommended; the flag could be removed for clarity.
Options 1 and 2 together would put the Linux binaries at roughly 29 MiB single-file / 55 MiB unpacked, below the current FreeBSD and macOS binaries, without losing any feature.
🤖 Generated with Claude Code
The Linux standalone binaries are about 70 MiB as a single file and unpack to 152 MiB (the single-file variant does that in a temp dir at every start). The FreeBSD and macOS binaries are 40-42 MiB. Most of the difference is avoidable.
Measured on the
borg-linux-glibc239-x86_64-ghartifact of CI run 34374266883 (PR #10344); theborg-linux-glibc243-x86_64-ghbinary of 2.0.0b24 has the same size (70.2 MiB), so this is not specific to the new job.Where the size goes
CFLAGScontain-g, andscripts/borg.exe.spechasstrip=False. libpython is 30.5 MiB (5.7 MiB stripped), borg's own Cython modules 22 MiB, the stdlib extension modules 21 MiB, then pyfuse3, yaml, msgpack, ...botocore/data)s3client; it needsdata/s3,endpoints.json,partitions.json,sdk-default-configuration.json,_retry.json: about 0.3 MiB compressedborg.exe(PYZ)cryptography's Rust extension (_rust.abi3.so)Options
Strip the shared libraries on Linux (
strip=Truein the spec for Linux, or build Python and the extensions without-g).llvm-strip --strip-allon every.so, not onborg.exe, which carries the appended archive; then the sametar czf). A stripped bundle was tried on Debian 13: repo-create withaes256-ocb, create, list, check, extract all fine.strip=sys.platform == "linux"): on macOS, stripping interferes with code signing, and the Windows/MSYS2 build is a different story. Building without-ginstead of stripping only works for the from-source Python of the glibc239 job, not for the setup-python builds of the glibc243 job.Bundle only the botocore service models borg needs (filter
a.datasin the spec, or a custom hook overriding the botocore hook):s3,sts(assume-role profiles), plus the top-level json files.sso,sso-oidc).Build
cryptographyfrom source against the bundled OpenSSL instead of using the manylinux wheel._rust.abi3.so).cryptographybuild against a non-vendored OpenSSL is more fragile across versions. Poor ratio of savings to effort.Bundle mfusepy instead of pyfuse3 in the Linux binaries.
BORG_FUSE_IMPLorder (mfusepy first).Drop extras from the binary (cockpit: textual + rich; s3: boto3/botocore; sftp: paramiko + cryptography + pynacl), or offer a "lite" binary next to the full one.
.tar.xzinstead of.tgzfor the single-directory variant.UPX: the spec has
upx=Truefor the single-file binary, but UPX is not installed on the runners, so it is a no-op today.Options 1 and 2 together would put the Linux binaries at roughly 29 MiB single-file / 55 MiB unpacked, below the current FreeBSD and macOS binaries, without losing any feature.
🤖 Generated with Claude Code