A fast high-compression read-only file system for Linux, FreeBSD, macOS and Windows
A fast high-compression read-only file system for Linux, FreeBSD, macOS and Windows
The Deduplicating Warp-speed Advanced Read-only File System.
A fast high-compression read-only file system for Linux, FreeBSD, macOS and Windows.
DwarFS is a mountable archive: you pack a directory into one file (an "image"), then open it instantly like a normal folder — no full extraction, no temporary files. It’s read-only when mounted, so you can browse, open, and run files safely in place. This works particularly well for big collections with lots of similar or repeated content — think many versions of a project or dataset, folders of documents and text files, backups/snapshots that mostly overlap, or libraries with many near-duplicates. The image can be mounted in fractions of a second, you can use the contents immediately with no delay, and you extract only if you actually need to.
Traditional archives are fine for storage, but they’re usually slow to open and awkward for random access (jumping around inside large files or across many files). DwarFS is built for fast random reads and space savings. It groups similar files and removes duplication, so images are often smaller than a simple tar/zip, while reads stay snappy — even when accessing lots of files simultaneously. In practice, you keep huge directories compressed, mount them in milliseconds, and work as if they were already unpacked.
This comparison uses DwarFS 0.14.1, 7-Zip 25.00 (x64), and tar +
pigz for all .tar.gz tests since plain
gzip would have been incredibly slow. Both .tar.gz and 7zip archives
were mounted using fuse-archive
v1.16 with the -olazycache option since the default of caching the entire
archive in memory is infeasible for large archives.
| Perl (47.49 GiB, 1.9M files) | .tar.gz [^pl1] | .tar.zst [^pl2] | 7zip (-mx=7) |
SquashFS [^pl7] | DwarFS (lzma) | DwarFS (zstd) |
|---|---|---|---|---|---|---|
| Compression time | 4m 59s | 8m 06s | 23m 27s | 5m 37s | 2m 13s | 5m 3s |
| Compression CPU time | 1h 47m | 2h 18m | 5h 5m | 2h 29m | 31m 17s | 49m 51s |
| Compressed size | 12.17 GiB | 0.387 GiB | 1.219 GiB | 3.245 GiB | 0.310 GiB | 0.352 GiB |
| Compression ratio | 3.902 | 122.7 | 38.96 | 14.63 | 153.2 | 134.9 |
| Decompression time | 2m 19s | 57.2s | 1m 14s | 39.3s | 48.7s | 46.5s |
| Decompression CPU time | 3m 44s | 1m 21s | 2m 28s | 1m 25s | 2m 23s | 1m 59s |
| Mount time | 2m 07s | ❌ [^pl3] | 3.638s | 0.011s | 0.420s | 0.009s |
| Find all 1.9M files [^pl8] | 5.670s | ❌ [^pl3] | 5.695s | 5.311s | 2.800s | 2.821s |
| Checksum 1139 files (2.58 GiB) [^pl4] | ❌ [^pl5] | ❌ [^pl3] | ~5h [^pl6] | 1.541s | 4.330s | 1.134s |
[^pl1]: using pigz -9
[^pl2]: using zstd --long=31 --ultra -22 -T0
[^pl3]: not supported by fuse-archive
[^pl4]: $ ls -1 mnt/*/perl*/bin/perl5* | xargs -d $'\n' -n1 -P16 sha256sum
[^pl5]: killed after making no progress for 15 minutes
[^pl6]: killed when only 78 files were finished after about 20 minutes
[^pl7]: using -comp zstd -Xcompression-level 22 -b 1M -tailends; using squashfuse_ll 0.6.0
[^pl8]: $ fd -t f . mnt | wc -l
| DwarFS CI (465.2 GiB, 3.6M files) | .tar.gz (pigz -9) |
7zip (-mx=7) |
DwarFS (zstd) |
|---|---|---|---|
| Compression time | 40m 28s | 141m 46s | 68m 6s |
| Compression CPU time | 13h 55m | 42h 47m | 31h 12m |
| Compressed size | 142.9 GiB | 60.04 GiB | 28.63 GiB |
| Compression ratio | 3.255 | 7.748 | 16.25 |
| Decompression time | 26m 17s | 8m 15s | 7m 21s |
| Decompression CPU time | 35m 16s | 41m 29s | 8m 59s |
| Mount time | 22m 18s | 10.5s | 0.024s |
| Run 441 binaries (2.72 GiB) [^ci1] | ❌ [^ci2] | ❌ [^ci3] | 0.774s |
[^ci1]: $ ls -1 mnt/*/dwarfs-*-Linux-x86_64-*-minsize*/bin/mkdwarfs | xargs -d $'\n' -n1 -P16 sh -c '$0 -h'
[^ci2]: killed after making no progress for 15 minutes
[^ci3]: killed after making no progress and consuming more than 32 GiB of RAM
| Sonniss (3.072 GiB, 171 files) ^wav1 | .tar.gz (pigz -9) |
7zip (-mx=7) |
SquashFS [^wav3] | DwarFS (categorize) |
|---|---|---|---|---|
| Compression time | 5.34s | 6m 21s | 19.52s | 3.98s |
| Compression CPU time | 2m 1s | 19m 14s | 9m 47s | 29.0s |
| Compressed size | 2.725 GiB | 2.255 GiB | 2.711 GiB | 1.664 GiB |
| Compression ratio | 1.127 | 1.362 | 1.133 | 1.846 |
| Decompression time | 13.7s | 1m 50s | 0.774s | 1.32s |
| Decompression CPU time | 17.8s | 1m 52s | 4.60s | 9.15s |
| Mount time | 13.6s | 0.014s | 0.018s | 0.008s |
| Checksum all files [^wav2] | 3m 2s | 30m 42s | 2.81s | 2.64s |
[^wav2]: $ find mnt -type f | xargs -d $'\n' -n1 -P16 sha256sum
[^wav3]: using -comp zstd -Xcompression-level 22 -b 1M -tailends
To create a DwarFS image from a directory, use mkdwarfs:
$ mkdwarfs -i /path/to/input/dir -o /path/to/image.dwarfsTo mount the image, use the dwarfs FUSE driver:
$ mkdir /path/to/mountpoint
$ dwarfs /path/to/image.dwarfs /path/to/mountpoint
$ ls /path/to/mountpointTo extract the image, use dwarfsextract:
$ dwarfsextract -i /path/to/image.dwarfs -o /path/to/output/dirThere's also the dwarfsck tool that can be used
for a variety of tasks, for example listing the files in a DwarFS image:
$ dwarfsck /path/to/image.dwarfs -lIt can also be used for generating checksums for each file in the image
in a format that is recognized by the *sum commands:
$ dwarfsck /path/to/image.dwarfs --checksum=sha512 | sha512sum --checkThis is useful if you want to verify the integrity of the actual files stored in the DwarFS image.
DwarFS is a read-only file system with a focus on achieving very high compression ratios, particularly for highly redundant data.
This probably doesn't sound very exciting, because if it's redundant, it should compress well. However, I found that other read-only, compressed file systems don't do a very good job at making use of this redundancy. See here for a comparison with other compressed file systems.
DwarFS also doesn't compromise on speed; in my use cases, it performs on par with, or better than, SquashFS. For my primary use case, DwarFS compression is an order of magnitude better than SquashFS compression, it's 6 times faster to build the file system, it's typically faster to access files on DwarFS and it uses less CPU resources.
To give you an idea of what DwarFS is capable of, here's a quick comparison of DwarFS and SquashFS on a set of video files with a total size of 39 GiB. The twist is that each unique video file has two sibling files with a different set of audio streams (this is an actual use case). So there's redundancy in both the video and audio data, but as the streams are interleaved and identical blocks are typically very far apart, it's challenging to make use of that redundancy for compression. SquashFS essentially fails to compress the source data at all, whereas DwarFS is able to reduce the size to nearly one-third, which is close to the theoretical maximum:
$ du -hs dwarfs-video-test
39G dwarfs-video-test
$ ls -lh dwarfs-video-test.*fs
-rw-r--r-- 1 mhx users 14G Jul 2 13:01 dwarfs-video-test.dwarfs
-rw-r--r-- 1 mhx users 39G Jul 12 09:41 dwarfs-video-test.squashfsFurthermore, when mounting the SquashFS image and performing a random-read
throughput test using fio-3.34, both
squashfuse and squashfuse_ll top out at around 230 MiB/s:
$ fio --readonly --rw=randread --name=randread --bs=64k --direct=1 \
--opendir=mnt --numjobs=4 --ioengine=libaio --iodepth=32 \
--group_reporting --runtime=60 --time_based
[...]
READ: bw=230MiB/s (241MB/s), 230MiB/s-230MiB/s (241MB/s-241MB/s), io=13.5GiB (14.5GB), run=60004-60004msecIn comparison, DwarFS manages to sustain random read rates of 20 GiB/s:
READ: bw=20.2GiB/s (21.7GB/s), 20.2GiB/s-20.2GiB/s (21.7GB/s-21.7GB/s), io=1212GiB (1301GB), run=60001-60001msecDistinct features of DwarFS are:
No open issues yet, or sync has not completed.