The 4,333x C++ Build Paradox: How Compiling 2.7 MB of CryFS Source Code Consumed 11.7 GB of Disk Space

Forensic breakdown showing how 2.7 MB of CryFS C++ source code expanded into 11.7 GB of disk usage across CMake build trees, DWARF debug symbols, GoogleTest static links, and Conan Boost caches
A real-world disk audit on an attached 1 TB Samsung 870 QVO Linux SSD revealed a 4,333x storage multiplier: 2.7 MB of C++ source code in cryfs/src left behind 11.7 GB of compiler artifacts, static archives, and Conan Boost dependency caches.

Every systems engineer eventually conducts a storage post-mortem on an old workstation drive. When we attached a 1 TB Samsung 870 QVO Linux SSD into a WSL2 environment to see where 454 GB of storage had gone, one directory immediately stood out in the home folder: a 3.7 GB directory at /home/robert/cryfs. At first glance, anyone would assume a 3.7 GB folder named cryfs is an encrypted CryFS vault holding gigabytes of private documents. When we opened it, there was not a single encrypted block inside. It was a source checkout of the open-source CryFS C++ repository—and compiling its 2.7 MB of C++ source code had quietly consumed 11.7 GB of disk space across the drive.

The 4,333x Amplification: From 2.7 MB of C++ Source to 11.7 GB on Disk

CryFS is a cryptographic FUSE filesystem written in modern C++ that splits files into fixed-size encrypted chunks stored via Crypto++, Boost, spdlog, and libcurl. If you measure the actual human-written C++ source code inside cryfs/src/—covering the blockstore, blobstore, FUSE filesystem layer (fspp), CLI front-end, and utility classes—the entire codebase weighs just 2.7 MB (plus 2.2 MB of unit tests in cryfs/test/ and 23 MB of bundled Crypto++ headers and sources in cryfs/vendor/cryptopp).

Yet once CMake, GCC/Clang, GNU binutils, and the Conan C++ package manager finished building the project in March 2025, that 2.7 MB directory left a 4,333x footprint across /home/robert:

Filesystem PathMeasured Disk UsageShare of TotalWhat Actually Lived Inside
~/cryfs/src/2.7 MB0.02%All human-written C++ implementation and header files across cryfs, fspp, blockstore, and cpp-utils
~/cryfs/build/Debug/2.0 GB17.1%Unstripped C++ debug object files (.o), static archives (.a), and 9 statically linked debug & GoogleTest binaries
~/cryfs/build/RelWithDebInfo/1.2 GB10.3%Optimized -O2 -g build tree where a single cryfs binary reached 133 MB from template inlining debug trees
~/cryfs/build/src & vendor503 MB4.3%A third root-level CMake out-of-source build directory (308 MB in build/src + 195 MB in build/vendor)
~/.conan2/p/ (Conan 2.x Cache)2.8 GB23.9%Boost source tree (918 MB) + multiple Boost ABI builds (638 MB) + OpenSSL builds (742 MB) + bundled CMake (241 MB)
~/.conan/data/ (Conan 1.x Cache)2.6 GB22.2%Legacy Conan 1 cache storing boost/1.75.0 (1.6 GB), boost/1.65.1 (805 MB), and spdlog/1.8.5 (94 MB)
~/boost/ + ~/cmake-3.*2.53 GB21.6%Standalone Boost C++ source build (1.5 GB) plus two compiled CMake toolchains (cmake-3.16.5 at 526 MB and cmake-3.28.3 at 509 MB)

How does a 2.7 MB C++ project devour nearly twelve gigabytes of solid-state storage without the developer noticing? When we dissected the ELF binaries and build directories with readelf -SW, du, and strip, four distinct multiplicative mechanics emerged.

Culprit #1: Header-Heavy C++ Templates & DWARF Debug Symbol Explosion

In C, compiling a 50 KB .c file produces a tiny object file because headers only declare function prototypes and compact structs. Modern C++ works very differently. Libraries like Boost, Crypto++, spdlog, and GoogleTest rely heavily on deeply nested header-only templates, policy classes, and constexpr metaprogramming.

Every time a .cpp translation unit includes Boost filesystem, thread, chrono, or Crypto++ cipher templates, the compiler instantiates full concrete types and emits exhaustive DWARF debugging information entries (DIEs) into the resulting .o object file. To see just how extreme this metadata tax becomes, we ran readelf -SW directly against the 88 MB cryfs/build/Debug/src/cryfs-cli/cryfs executable:

ELF Section NameHex SizeMeasured SizePurpose Inside the 88 MB Binary
.debug_info0x2214e7735.74 MBDWARF type trees, class layouts, and template instantiations across every compilation unit
.debug_str0x0deb1b814.59 MBDeduplicated DWARF string table holding thousands of deeply nested C++ template type names
.strtab0x06f752c7.30 MBELF linker symbol string table for 42,690 mangled C++ symbol names
.debug_line0x0586c6d5.79 MBInstruction-address to source-file and line-number mapping tables for GDB
.symtab0x02088002.13 MB42,690 ELF symbol table entries (24 bytes per symbol record)
.debug_abbrev, .debug_aranges, .debug_rnglists0x04270334.35 MBDWARF schema abbreviations (1.73 MB), address ranges (1.58 MB), and range lists (1.04 MB)
.text (Executable Code)0x093ee209.69 MBThe actual x86_64 CPU machine instructions—only 11.0% of the 88 MB file!

When we ran strip on that 88 MB binary, it immediately shrank to 21 MB—proving that 67 MB (76.1% of the executable) was pure DWARF debugging metadata and mangled symbol strings.

The RelWithDebInfo (-O2 -g) Paradox: Most developers assume a RelWithDebInfo binary is smaller than a Debug binary. On our drive, build/RelWithDebInfo/src/cryfs-cli/cryfs was 133 MB—45 MB larger than the 88 MB Debug binary! Why? Because -O2 aggressively inlines thousands of C++ template functions across header boundaries, forcing the compiler to emit massive DWARF DW_TAG_inlined_subroutine trees and location lists for every single inlined call site.

Culprit #2: The Static Archive Triple-Counting Tax (.o → .a → 9 Binaries)

If a single debug binary is 88 MB, why did cryfs/build/Debug/ consume a full 2.0 GB? Because standard CMake static-library projects store every compiled instruction and every DWARF debug section three separate times:

  1. First Copy (Intermediate .o Object Files — ~680 MB): CMake compiles each source file into CMakeFiles/*.dir/*.cpp.o. For example, fspp-test.dir alone held 235 MB of .o files, blockstore-test.dir held 149 MB, cryfs.dir held 100 MB, and cryfs-test.dir held 99 MB.
  2. Second Copy (Static .a Archives — ~365 MB): ar bundles those exact same unstripped .o files into static libraries: libcryfs.a (91 MB), libcryptopp.a (167 MB with object files), libblockstore.a (38 MB), libcpp-utils.a (28 MB), libblobstore.a (24 MB), and libcryfs-cli_lib.a (15 MB).
  3. Third Copy (Statically Linked Executables & Unit Test Runners — ~670 MB): Because CryFS builds separate GoogleTest executables for each subsystem and links the static archives into all of them, the linker copies those massive DWARF sections into nine separate binaries inside build/Debug/:
    • cryfs-test: 94 MB
    • blockstore-test: 94 MB
    • cryfs-cli-test: 90 MB
    • cryfs (main CLI binary): 88 MB
    • fspp-test: 77 MB
    • cpp-utils-test: 69 MB
    • blobstore-test: 51 MB
    • cryfs-stats: 50 MB
    • parallelaccessstore-test & cryfs-unmount: 43 MB

Just compiling the unit test suite in build/Debug/test/ consumed 1.2 GB—more than 440 times the size of the entire project's source code.

Culprit #3: The Hidden 5.4 GB in ~/.conan and ~/.conan2

The 3.6 GB inside ~/cryfs/build/ was only a third of the story. CryFS uses conanfile.py to manage its C++ dependencies. Because the project bridged the ecosystem transition from Conan 1.x to Conan 2.x—and because Conan compiles and caches separate binary packages for every combination of compiler version, C++ standard, and build_type=Debug vs. build_type=Release—two hidden directories in /home/robert quietly amassed 5.4 GB:

  • ~/.conan/data/ (2.6 GB): Held two complete source-and-binary builds of Boost—boost/1.75.0 at 1.6 GB and boost/1.65.1 at 805 MB—plus spdlog/1.8.5 at 94 MB.
  • ~/.conan2/p/ (2.8 GB): Held another full uncompressed Boost source tree (918 MB), two Boost binary package revisions (342 MB and 296 MB), two full OpenSSL build trees (365 MB and 301 MB), a private Conan-managed CMake installation (241 MB), and two libcurl builds (151 MB).

When combined with a standalone 1.5 GB ~/boost checkout and 1.03 GB across ~/cmake-3.16.5 and ~/cmake-3.28.3 built during environment setup, compiling a single 2.7 MB C++ utility left behind 11.7 GB of permanent SSD consumption.

How to Keep C++ Builds from Eating Your SSD

If you build modern C++ projects on Linux workstations, CI runners, or WSL2 virtual disks (where ext4.vhdx expands to the high-water mark and never shrinks automatically), four compiler and build-system flags eliminate 75% to 90% of this disk bloat immediately:

  1. Enable Split DWARF (-gsplit-dwarf) and Compressed Debug Sections (-gz=zstd): Passing CMAKE_CXX_FLAGS="-gsplit-dwarf -gz=zstd" tells GCC/Clang to compress DWARF sections with Zstandard and keep debug info in separate .dwo files instead of copying 67 MB of duplicated DWARF trees into every static archive and test executable.
  2. Link With mold or lld Using --gdb-index: Modern linkers process split DWARF in milliseconds without duplicating .debug_info across nine GoogleTest binaries.
  3. Build Shared Libraries for Unit Test Suites (-DBUILD_SHARED_LIBS=ON during Debug): Linking cryfs-test, blockstore-test, and fspp-test dynamically against libcryfs.so in local debug builds avoids copying 91 MB of libcryfs.a into every test runner.
  4. Prune Conan Build Folders After Packaging: Running conan remove "*" --build --src -c (in Conan 2) or conan remove "*" -b -s -f (in Conan 1) clears intermediate source and build trees from ~/.conan2 while keeping the final compiled library binaries intact.

Software bloat does not only happen at runtime in browser tabs and cloud containers. When 2.7 MB of C++ source code requires 11.7 GB of build scaffolding, inspecting your toolchain's disk footprint is one of the highest-leverage hygiene habits a systems engineer can practice.

At WebDesigner.LA, our systems engineering team applies this same forensic discipline across bare-metal Linux clusters, compiler toolchains, and high-speed web applications—eliminating hidden bloat so your infrastructure runs lean, fast, and predictable.