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 Path | Measured Disk Usage | Share of Total | What Actually Lived Inside |
|---|---|---|---|
~/cryfs/src/ | 2.7 MB | 0.02% | All human-written C++ implementation and header files across cryfs, fspp, blockstore, and cpp-utils |
~/cryfs/build/Debug/ | 2.0 GB | 17.1% | Unstripped C++ debug object files (.o), static archives (.a), and 9 statically linked debug & GoogleTest binaries |
~/cryfs/build/RelWithDebInfo/ | 1.2 GB | 10.3% | Optimized -O2 -g build tree where a single cryfs binary reached 133 MB from template inlining debug trees |
~/cryfs/build/src & vendor | 503 MB | 4.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 GB | 23.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 GB | 22.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 GB | 21.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 Name | Hex Size | Measured Size | Purpose Inside the 88 MB Binary |
|---|---|---|---|
.debug_info | 0x2214e77 | 35.74 MB | DWARF type trees, class layouts, and template instantiations across every compilation unit |
.debug_str | 0x0deb1b8 | 14.59 MB | Deduplicated DWARF string table holding thousands of deeply nested C++ template type names |
.strtab | 0x06f752c | 7.30 MB | ELF linker symbol string table for 42,690 mangled C++ symbol names |
.debug_line | 0x0586c6d | 5.79 MB | Instruction-address to source-file and line-number mapping tables for GDB |
.symtab | 0x0208800 | 2.13 MB | 42,690 ELF symbol table entries (24 bytes per symbol record) |
.debug_abbrev, .debug_aranges, .debug_rnglists | 0x0427033 | 4.35 MB | DWARF schema abbreviations (1.73 MB), address ranges (1.58 MB), and range lists (1.04 MB) |
.text (Executable Code) | 0x093ee20 | 9.69 MB | The 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:
- First Copy (Intermediate
.oObject Files — ~680 MB): CMake compiles each source file intoCMakeFiles/*.dir/*.cpp.o. For example,fspp-test.diralone held 235 MB of.ofiles,blockstore-test.dirheld 149 MB,cryfs.dirheld 100 MB, andcryfs-test.dirheld 99 MB. - Second Copy (Static
.aArchives — ~365 MB):arbundles those exact same unstripped.ofiles 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), andlibcryfs-cli_lib.a(15 MB). - 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 MBblockstore-test: 94 MBcryfs-cli-test: 90 MBcryfs(main CLI binary): 88 MBfspp-test: 77 MBcpp-utils-test: 69 MBblobstore-test: 51 MBcryfs-stats: 50 MBparallelaccessstore-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.0at 1.6 GB andboost/1.65.1at 805 MB—plusspdlog/1.8.5at 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 twolibcurlbuilds (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:
- Enable Split DWARF (
-gsplit-dwarf) and Compressed Debug Sections (-gz=zstd): PassingCMAKE_CXX_FLAGS="-gsplit-dwarf -gz=zstd"tells GCC/Clang to compress DWARF sections with Zstandard and keep debug info in separate.dwofiles instead of copying 67 MB of duplicated DWARF trees into every static archive and test executable. - Link With
moldorlldUsing--gdb-index: Modern linkers process split DWARF in milliseconds without duplicating.debug_infoacross nine GoogleTest binaries. - Build Shared Libraries for Unit Test Suites (
-DBUILD_SHARED_LIBS=ONduring Debug): Linkingcryfs-test,blockstore-test, andfspp-testdynamically againstlibcryfs.soin local debug builds avoids copying 91 MB oflibcryfs.ainto every test runner. - Prune Conan Build Folders After Packaging: Running
conan remove "*" --build --src -c(in Conan 2) orconan remove "*" -b -s -f(in Conan 1) clears intermediate source and build trees from~/.conan2while 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.
