EncFS provides an encrypted virtual filesystem. It runs in userspace, using the FUSE library. EncFS is open source software, licensed under the LGPL.
EncFS was first released in 2003, making it one of the earlier FUSE filesystems! At the time, there were few options available for Linux, and the kernel modules that were available had not kept pace with Linux development. This meant that every kernel upgrade was likely to break the filesystem until the module was updated. When FUSE became available, I wrote a CFS replacement for my own use and released it to Open Source.
EncFS encrypts individual files, by translating all requests for the virtual EncFS filesystem into the equivalent encrypted operations on the raw filesystem.
For more info, see:
- The technical overview in DESIGN.md
If you're considering setting up a new encrypted filesystem, I'd recommend looking into newer alternatives, such as the excellent GoCryptFS, or else use full-disk encryption when possible.
Data has a long lifespan, and people still have data that was encrypted with EncFS. This project has been mostly dormant for years. I've recently ported EncFS to Rust as a way to learn Rust and apply newer development tools.
The old C++ code has been removed. The code can be found in old branches, or old releases, but is not being maintained.
EncFS was a mature program, so while the new implementation is already functional it is still considered a beta release, and I would always have a separate backup for anything stored in it.
Overall status
- Core
- Read operations
- Basic write operations
- Truncate w/ holes
- Improvements
- new tamper-safe configuration file (v7 config)
- support for a new key derivation function (Argon2id)
- new block encryption mode (aes-gcm-siv)
- 96-bit random per-file IVs
- per-directory name IVs (
.encfs.diriv), so directory renames are a single rename - encrypted extended attributes, keyed per inode so they survive renames and hard links
- filename encoding chosen automatically for case-insensitive filesystems
- Extended features
- reverse encryption mode, with opt-in writes
- configurable unencrypted extended attribute prefixes
- on-demand mode with Touch ID and idle locking (macOS)
- Platforms
- Linux, macOS (macFUSE), FreeBSD
- Multi-language
- basic multi-language support
- translations beyond auto-generated FR and DE strings
This implementation reads and writes both legacy EncFS filesystems (created with EncFS 1.9.x, config formats V4/V5/V6) and newly created filesystems (V7 config). The cryptographic differences are:
| Aspect | Legacy (EncFS 1.9.x, V4–V6) | New (V7, current default) |
|---|---|---|
| Config file | .encfs6.xml / .encfs5 / .encfs4 (unsigned; tampering undetected) |
.encfs7 (authenticated; tamper-safe via config_hash) |
| Password KDF | PBKDF2-HMAC-SHA1 (configurable iterations) | Argon2id (memory-hard, GPU/ASIC-resistant; 64 MiB, 3 iterations, 4 lanes by default) |
| Block encryption | AES or Blowfish in CBC mode with an optional per-block MAC of at most 8 bytes (encrypt-then-MAC, 64-bit MACs) | AES-256-GCM-SIV authenticated encryption with a 16-byte tag per block (misuse-resistant AEAD) |
| Confidentiality + integrity | Separate cipher + weak MAC; integrity optional and truncated | Integrated AEAD; every block is authenticated |
| Block overhead | Up to 8-byte MAC header per block | 16-byte tag per block (default block size 4080 of 4096 bytes) |
| Filename encoding | Stream (CFB, multi-pass) or block mode | Same stream/block modes; encfsctl new picks Base32 block names on case-insensitive filesystems (so names survive case folding) and stream names elsewhere (--name-encoding overrides) |
| Filename IVs | IV from HMAC of the name, chained from the parent path, so every name depends on all directories above it | IV for the names in each directory comes from a random per-directory .encfs.diriv file, so names depend only on their directory (--no-directory-iv keeps path-chained IVs; reverse mode requires this) |
| Directory rename | With chained name IVs, a copy of the whole subtree under new names | A single rename(2). With external IV chaining (on by default) a file rename is still a copy, since file headers depend on the path |
| Extended attributes | Stored unencrypted, as EncFS 1.9.x does | Names and values encrypted under a random per-inode seed (user.encfs.~iv), so they survive renames, hard links and truncation; prefixes can be left unencrypted (--plaintext-xattr-prefix) |
| Symlink targets | Path form of EncFS 1.9.x: each component encoded separately, keeping /, . and .. |
Whole target encrypted as one name under the link's path IV |
| Volume key wrap | Encrypted with the PBKDF2-derived user key | Wrapped with a 32-byte Argon2id-derived AEAD key |
Per-file IV (with uniqueIV) |
64-bit, stored in an 8-byte file header | 96-bit by default, stored in a 12-byte file header (encfsctl new --legacy-file-iv keeps the 64-bit/8-byte form) |
| Format evolution | Config version only; EncFS 1.9.x cannot read V7 configs at all | Authenticated minimum_reader_version: a build too old for a feature the volume uses refuses it with a clear version error instead of misreading it (betas from before this field fail closed with a config hash mismatch) |
In short: legacy filesystems use PBKDF2-HMAC-SHA1 + CBC with an optional 64-bit per-block MAC, while new V7 filesystems use Argon2id + AES-GCM-SIV, giving memory-hard password hashing, authenticated per-block encryption with 128-bit tags, and a tamper-evident config file. V7 also gives each directory its own random name IV, so directory renames no longer copy their contents, and encrypts extended attributes, which legacy volumes store in the clear. Some protocol-level weaknesses of the legacy EncFS design remain for compatibility, but the V7 mode addresses the most significant ones (weak MACs, unauthenticated config, fast KDF). The design decisions behind the newer features are in docs/adr.
Every new V7 filesystem, including those made with --legacy-file-iv,
--no-unique-iv or --no-directory-iv, uses the per-inode extended
attribute format, so it can only be opened by builds that support that
format, not by earlier betas or EncFS 1.9.x.
Existing legacy filesystems can be partially upgraded in place with
encfsctl passwd --upgrade <rootdir> (moves to the V7 config and Argon2id;
requires uniqueIV and chained name IVs). The upgrade only rewrites the
config: file data encryption, file IVs, filename IVs, symlink targets and
unencrypted extended attributes all stay in their legacy form. Getting
AES-GCM-SIV, per-directory name IVs and encrypted extended attributes
requires creating a new filesystem and copying the data.
While the primary focus has been on providing an updated codebase to support reading old encrypted content, since writes have been implemented it also makes sense to support newer algorithms.
Some upgrades can be applied to existing filesystems. Use
encfsctl passwd --upgrade <rootdir> to convert an existing filesystem to the
V7 config format and switch to the Argon2id key-derivation function. You will be
prompted for the current password and a new one. The tool writes a new .encfs7
file; the old config file is left in place and can be removed after verification.
Ensure the volume is not mounted when upgrading.
For scripted or non-interactive setup, use encfsctl new --stdinpass or --extpass <program>,
and encfs -S or --extpass for mounting.
1. Create a new EncFS config in the directory that will hold the encrypted
files. This creates a V7 config (e.g. .encfs7) and prompts for a password:
mkdir -p ~/encrypted encfsctl new ~/encrypted
To reuse the settings of an existing Version 7 filesystem, pass its encrypted directory or its config file as a template; other flags apply on top of it:
encfsctl new --template ~/old-encrypted ~/encrypted
2. Mount the filesystem with encfs. The first argument is the encrypted root directory (where the config lives), the second is the mount point where you will see plaintext:
mkdir -p ~/mount encfs ~/encrypted ~/mount
You can now read and write files under ~/mount; they are stored encrypted
under ~/encrypted. When finished, unmount:
fusermount -u ~/mount # Linux # or: umount ~/mount # macOS / FreeBSD
encfs --touchid --idle-timeout 10 ~/encrypted ~/mount
After the password, encfs asks for Touch ID, then goes into the background.
After --idle-timeout minutes (default 10) without file activity it locks:
opening, reading, listing, or changing anything in the mount then brings up a
fresh Touch ID prompt, and the request waits for it. Cancelling the prompt
returns "Permission denied" (with no further prompts for 10 seconds).
What does and does not count:
- File and directory metadata (
stat, lookups,statfs, access checks) is always answered and never counts as activity, so Finder and system services polling the volume neither keep it unlocked nor cause prompts. This means sizes and timestamps of paths a caller already knows stay visible while locked; file contents, directory listings, symlink targets and extended attribute values do not. - Extended attribute reads never count as activity or prompt; while locked they are refused.
- Writes to files that were already open keep working while locked, so buffered data is not lost.
- Requests from background services (Spotlight, Time Machine, Quick Look
thumbnails, XProtect, and similar) never count as activity or prompt; while
locked they are refused. Add more with
--idle-ignore <process-name>.
encfs --no-apple-xattr ~/encrypted ~/mount
macOS tags every file it creates with com.apple.* extended attributes
(provenance, Finder info) and reads them back on open. --no-apple-xattr
mounts with macFUSE's noapplexattr, so those attributes are refused before
they reach encfs: file creation and reads get noticeably faster, but
quarantine flags, Finder tags and similar metadata are not kept on the volume.
Other extended attributes are unaffected.
encfsctl new --plaintext-xattr-prefix user.xdg. ~/encrypted encfsctl xattr-prefix add ~/encrypted security. trusted. encfsctl xattr-prefix remove ~/encrypted trusted. encfsctl xattr-prefix list ~/encrypted
Version 7 filesystems encrypt extended attribute names and values. Attributes
whose names start with a configured prefix are instead stored as they are,
under their own names, so tools reading the encrypted directory (backup
software, SELinux, and the like) can see them. Names under user.encfs. are
always encrypted. Adding or removing a prefix asks for the password, since the
volume key is bound to the config, and takes effect at the next mount.
Attributes already stored are not converted. A config with prefixes needs a
reader that supports configurable properties (minimum reader version 6).
encfsctl rpc ~/mount info encfsctl rpc ~/mount lock # engage the Touch ID idle lock now getfattr --only-values -n user.encfs.rpc.info ~/mount # Linux xattr -p user.encfs.rpc.info ~/mount # macOS
A mounted filesystem answers requests made by reading a user.encfs.rpc.*
extended attribute on its root, with a JSON reply. info reports the EncFS
version, the encrypted directory, the mount options and the idle-lock state.
lock engages the idle lock of an on-demand (--touchid) mount at once, so
the next access asks for Touch ID; other mounts answer it with an error.
Only the user who mounted the filesystem (or root) can ask. See
ADR 0004 for the protocol.
The encfsr binary provides reverse encryption: your plaintext files live on disk in a source directory, and encfsr mounts a read-only by default virtual filesystem that exposes the encrypted view of that directory. Use this when you want to back up or sync an encrypted representation of local data (e.g. to an untrusted or cloud storage) without storing plaintext there.
- Normal encfs: encrypted dir → mount point shows plaintext (read/write).
- encfsr: plaintext dir → mount point shows encrypted view (read-only).
- A V7 EncFS config (e.g.
.encfs7). Older configs are not supported. - Config should be created without per-file IV headers: use
encfsctl new --no-unique-iv .... This is required by encfsr, including writable reverse mounts; authenticated V7 block encryption and IV chaining remain supported. Such configs use path-chained filename IVs, since reverse mode cannot use the per-directory IV files that are otherwise the default. Headerless configs always use the 64-bit path-derived file IV — the 96-bit wide-header format only applies whenuniqueIVis enabled, so it never applies to reverse mode.
encfsr <config> <source_dir> <mount_point>
Example: plaintext in ~/Documents, encrypted view at /mnt/enc:
encfsr ~/Documents/.encfs7 ~/Documents /mnt/enc
To restore through the encrypted view, opt in explicitly to writable reverse mode. Writes are staged and authenticated before they are applied to the plaintext source, so a malformed or incomplete encrypted write fails at flush or close rather than corrupting the source:
encfsr --write ~/Documents/.encfs7 ~/Documents /mnt/enc
Then copy or sync from /mnt/enc to your backup/cloud target; the content and filenames there are encrypted.
Options (same as encfs where applicable):
-f/--foreground— run in foreground (do not daemonize).-S/--stdinpass— read password from stdin (e.g. for scripts).--extpass <program>— run a program to get the password;RootDiris set to the source directory.
Unmount when done: fusermount -u <mount_point> (Linux) or umount <mount_point> (macOS/FreeBSD).
encfsctl speed will test algorithm performance on your machine. The crypto algorithms are provided
by 3rd party crates, they are not implemented here.
Example output:
> ./target/release/encfsctl speed
encfsctl v2.0.0-beta.4; 2026-07-25 macos/aarch64
cpu: Apple M4 Pro; with AES hardware acceleration
AES-256-GCM-SIV 1869.56 MB/s (default, V7 file content)
AES-128-GCM-SIV 1891.51 MB/s
AES-256-CBC (legacy) 1659.87 MB/s
AES-192-CBC (legacy) 1849.51 MB/s
AES-128-CBC (legacy) 2039.94 MB/s
Blowfish-CBC (legacy) 180.26 MB/s (deprecated)
The repository includes an automated filesystem benchmark based on mdtest,
which is distributed as part of IOR. It measures a fresh V7 EncFS filesystem
using the encfsctl new default: per-directory name IVs and per-file IV headers
(directory-iv). Both baseline saves and comparison runs measure this configuration.
It mounts the filesystem with the release binaries, runs all of mdtest's standard directory and file phases, unmounts it, and removes its temporary files. The benchmark requires:
- a working FUSE installation and permission to mount a FUSE filesystem;
mdtestonPATH(or an explicitMDTEST_BIN);mpirunonly when running more than one rank.
First save a baseline for the current machine, then compare later runs with it:
task benchmark-save-baseline task benchmark
To compare gocryptfs with the saved EncFS directory-iv baseline:
task benchmark-gocryptfs
# Or select a specific installed binary:
task benchmark-gocryptfs GOCRYPTFS_BIN=/path/to/gocryptfsThis target requires gocryptfs on PATH (or GOCRYPTFS_BIN). It initializes
and mounts a fresh volume with gocryptfs defaults, runs the same mdtest phases,
then unmounts and removes the temporary volume. It prints the gocryptfs version
and per-operation deltas against the saved EncFS baseline; a positive delta
means gocryptfs achieved more operations per second. The baseline is left
unchanged. Use the same workload variables as when saving the EncFS baseline.
The filesystems use their respective encryption formats and settings, so this
compares their performance with these configurations.
The default workload uses one rank, 1,000 items per iteration, five iterations,
and 4,096-byte writes and reads. Results are normalized as mean operations per
second for every operation in mdtest's SUMMARY rate table. Comparisons print
one table comparing the saved directory-iv baseline against this run, but
changes are informational and never fail the command. Command failures, invalid output, mount/unmount failures,
and missing or incompatible baselines do fail with a diagnostic.
Task variables can select another baseline or workload. For example:
task benchmark-save-baseline BENCH_ITEMS=100 BENCH_ITERATIONS=2 task benchmark BENCH_ITEMS=100 BENCH_ITERATIONS=2 task benchmark BENCH_PROCS=4 MPIRUN_BIN=/opt/mpi/bin/mpirun task benchmark BENCH_BASELINE=/path/to/shared-baseline.json MDTEST_BIN=/opt/ior/bin/mdtest
The supported variables are BENCH_BASELINE, BENCH_ITEMS,
BENCH_ITERATIONS, BENCH_PROCS, BENCH_BYTES, MDTEST_BIN, MPIRUN_BIN, GOCRYPTFS_BIN,
and BENCH_MOUNT_TIMEOUT_SECS. The default baseline is
.benchmarks/filesystem-baseline.json, which is ignored by Git. Baselines are
machine-specific: compare results on the same host under similar load. The tool
warns if the recorded host context or mdtest version differs. Baselines saved
with the former no-unique-iv configuration (or without a variant label)
are incompatible and must be saved again with task benchmark-save-baseline.
This has been tested with libfuse3-dev on Linux (Ubuntu 24.04), and on MacOS with MacFuse 5.3.x.
The FUSE support comes from typed-fuse,
which is a modern Rust FUSE library on top of the C bindings from libfuse. I tried many FUSE libraries
for Rust and had a lot of trouble getting them working on both Linux and MacOS, along with handling
edge cases and multi-threading. This is an area where Rust libraries are not as mature as the Go
options, and so I went the route of improving on a low-level Rust FFI binding instead of a pure Rust
implementation. This resulted in both fixing the compatibility issues, and speed improvement of 70% - 3000% depending on the system.
Here's the benchmark results immediately after switching to typed-fuse:
Compiling encfs v2.0.0-beta.4
Finished `bench` profile [optimized] target(s) in 0.69s
Running benches/filesystem.rs (target/release/deps/filesystem-35066b3af94c35f4)
Filesystem benchmark: ranks=1, items/rank=1000, iterations=5, bytes/file=4096
Values are operations per second (ops/sec); higher is better, so a positive delta is an improvement.
Operation Baseline Current Delta
-------------------------------- ------------ ------------ ----------
Directory creation 1546.05 4714.33 +204.93%
Directory removal 2232.87 5468.78 +144.92%
Directory rename 948.21 2291.29 +141.65%
Directory stat 14355.23 444383.09 +2995.62%
File creation 1387.30 3167.44 +128.32%
File read 5716.02 9227.67 +61.44%
File removal 2266.72 4906.89 +116.48%
File stat 14424.88 497902.58 +3351.69%
Tree creation 1866.91 4040.45 +116.42%
Tree removal 1054.19 1354.10 +28.45%
Deltas are informational and do not affect the command exit status.
(this is a hold-over from the original encfs FAQ, which presumably still applies)
Disable External IV chaining. There have
been reports
of a pathological interaction of IV chaining mode with Dropbox' rename
detection.
New V7 filesystems already rename directories in place (per-directory name IVs), but external IV chaining is on by default, which turns a file rename into a copy. Disable it when creating a new filesystem:
encfsctl new --no-chained-iv ~/Dropbox/encryptedThis assumes the usual setup, where Dropbox only uploads what you change through the mount. While a filesystem with per-directory name IVs is mounted, EncFS caches each directory's IV, so every change to the encrypted directory must go through the mount.
If Dropbox also writes into the encrypted directory while it is mounted (for example, applying changes made on another machine), a directory it renames, or removes and recreates, can keep a stale IV until the next mount: files in it can be misnamed, or written under the wrong IV and become unreadable. EncFS does not optimize for this setup, but supports it if per-directory name IVs are turned off as well:
encfsctl new --no-directory-iv --no-chained-iv ~/Dropbox/encryptedNames then use no per-directory IVs and no IV chaining at all, so there is
nothing to go stale and renames stay plain renames. The trade-off is that
identical filenames in different directories encrypt to identical names.
(--no-directory-iv alone would also be safe, but would chain names from
the path again, turning every directory rename into a copy of its contents.)