Hidden volume
NAILS never unlocks, mounts or encrypts the hidden volume. It needs a filesystem that is already mounted and writable, and it keeps the hidden session’s upper layer there.
The tested way to open a VeraCrypt hidden volume is
cryptsetup open --type tcrypt --veracrypt --tcrypt-hidden. Make the outer and
the hidden volume with PBKDF2, because no cryptsetup release can open a volume
made with Argon2id yet. Opening the volume has the
commands for each backend.
Whether the hidden volume’s existence can be denied depends on the backend you choose. NAILS adds nothing to the backend’s own claim and does not hide that a hidden volume exists. It narrows what an offline examination of the decoy can find about the session.
A LUKS2 header announces an encrypted volume, so the deniability of a LUKS2 volume rests on it having a plausible reason to exist. A VeraCrypt hidden volume is created inside the free space of an outer VeraCrypt volume, and the outer volume is visible. VeraCrypt’s hidden volume documentation says that even when the outer volume is mounted, it “should be impossible to prove whether there is a hidden volume within it or not,” provided that the instructions in its Volume Creation Wizard and its security requirements for hidden volumes are followed.
Which backends work
Section titled “Which backends work”- Tested: LUKS2 with ext4 on a whole block device. Every end-to-end test run uses it.
- Tested: a VeraCrypt hidden volume opened with cryptsetup, with ext4 inside. A virtual machine test creates an outer and a hidden VeraCrypt volume, opens the hidden one with cryptsetup, runs a session over it and images the decoy root. It runs in every end-to-end run that rebuilds the tests, and nightly. The whole setup was also evaluated by hand once, before the current whole-root design.
- Tested: a directory on a separate local disk, bind-mounted at the hidden root. A virtual machine test covers it, with an ext4 disk that the decoy neither mounts nor lists in its fstab.
- Expected to work: any local Linux filesystem on a kernel block device that passes the pre-flight checks. This is untested. ext4, xfs, btrfs and f2fs can host the upper layer.
- Refused: a folder, or a filesystem, subvolume, bind mount, volume or unencrypted image file on storage the decoy uses. It is not storage of its own. The attach gate refuses it before anything is written.
- Unsupported: NFS, CIFS, sshfs, 9p, overlays, FUSE filesystems and ZFS datasets. They show no disk they store their data on, so where their writes land cannot be told. The attach gate refuses them.
- Unsupported: FAT, exFAT and NTFS. They cannot host an overlay upper layer and are refused before activation.
- Unsupported: a VeraCrypt volume made with Argon2id. No cryptsetup release can open it yet. See Use PBKDF2, not Argon2id.
- Not evaluated: Shufflecake, and volumes mounted by the VeraCrypt application itself. NAILS makes no claims about them.
What the hidden volume must provide
Section titled “What the hidden volume must provide”- Storage of its own, mounted and writable at the hidden root: a whole device, a partition or an encrypted volume that the decoy does not use. It must be a mount point, with no second filesystem mounted over it.
- Managed by the kernel, such as a dm-crypt mapping or a loop device. The soft-reboot stops every userspace process, so a FUSE mount backed by a process dies at the switch.
- One filesystem. Do not mount another filesystem inside the hidden root. An overlay’s upper and work directories must share a filesystem.
- Overlay support: symbolic links,
trusted.*extended attributes, file types (d_type) in directory listings and overlay whiteouts. Thesymlink-supportcheck tests symbolic links, and theupper-fs-capabilitycheck proves the rest with a throwaway trial overlay before the session is touched.
What the attach gate refuses
Section titled “What the attach gate refuses”init, activate, check and the other commands that write ask the attach
gate first. Before they write or probe a single file, they refuse:
- a folder that is not a mount point
- a btrfs subvolume or a bind mount of a filesystem the decoy uses
- a filesystem that the decoy’s fstab mounts
- a logical volume or other device stacked on the decoy’s storage or swap
- an unencrypted image file on a decoy filesystem
- a root with a second filesystem mounted over it
- a filesystem that shows no disk it stores its data on, such as an overlay, FUSE, a network filesystem or a ZFS dataset, because where its writes land cannot be told
A refusal names the storage and says that nothing was written. For a folder or
storage the decoy uses, it says the root “is not a volume of its own.” The gate
also refuses a root whose storage it cannot judge because only root can read
/etc/crypttab or where a container file lives. Run the command with sudo in
that case. --no-preflight does not skip the gate.
An encrypted container file on a decoy filesystem is accepted with a warning. That filesystem’s journal and copy-on-write snapshots can keep old copies of the parts of the container that changed, so a whole device or a partition the decoy does not use is better.
--allow-decoy-storage, on init, activate and check, turns the refusal of
storage the decoy uses into a warning. Use it only for storage that the gate
takes for the decoy’s but that you know the decoy does not use, such as a disk
its fstab lists but never mounts, because everything written there lands on
that storage. It never covers a folder, a covered root, storage that cannot be
judged or a decoy crypttab or fstab line for the volume.
The gate judges only what the running system’s mount table, fstab, crypttab and sysfs describe. It cannot see a decoy filesystem mounted only by hand and never listed, or storage that another operating system on the same machine uses.
Prepare it away from the decoy
Section titled “Prepare it away from the decoy”Create the hidden volume where nothing reaches the decoy’s disk, never on the decoy itself:
- On an external drive, prepared on another computer. This is the
recommended way. Create and format the volume there, then attach the drive to
the computer that runs the decoy, unlock and mount it, and run
nailsfrom it. - From a live system started from a USB stick on the decoy’s computer, such as Tails, with the hidden volume on a drive or partition of its own. A live system writes to the internal disk too when you use it to change the disk’s partition table or to mount a decoy filesystem, for example to reach a container file there.
NAILS refuses a hidden root on the decoy’s storage, but it cannot see what creating a volume on the decoy left behind, such as the setup tools’ own files, shell history or a changed partition table. Only preparing the volume elsewhere avoids those traces.
Tails can unlock the hidden volume inside a VeraCrypt volume, but new VeraCrypt volumes have to be created outside of Tails, as the Tails documentation on VeraCrypt says. Make a VeraCrypt volume with VeraCrypt on another computer. First install from Tails shows how to install NAILS onto it from Tails.
VeraCrypt hidden volumes
Section titled “VeraCrypt hidden volumes”A VeraCrypt hidden volume opened with cryptsetup is a tested backend. Before you create one, read VeraCrypt’s own requirement for Linux and choose a key derivation that cryptsetup can open. For how NAILS differs from VeraCrypt itself, see NAILS compared with other tools.
Use PBKDF2, not Argon2id
Section titled “Use PBKDF2, not Argon2id”The NAILS storage backends guide, last checked on 2026-10-19, says that VeraCrypt 1.26.29 and newer can protect a volume’s header with a key derived by Argon2id, listed as “Argon2” in the KDF list of its volume wizard. It also says that no cryptsetup release can open such a volume yet, so cryptsetup on the decoy cannot unlock it. The guide lists the cryptsetup version of each decoy.
The failure does not name its cause. cryptsetup open --type tcrypt prints
No device header detected with this passphrase., the message it gives for a
mistyped password, and asks again. Nothing outside the volume shows which
algorithm its header uses, so neither cryptsetup nor NAILS can warn you.
- Make both volumes with PBKDF2. In VeraCrypt’s wizard, choose any KDF entry other than Argon2, for the hidden volume and for the outer volume. The decoy has to be able to open the outer volume when you show it.
- Change a volume already made with Argon2id on another computer. The same guide says that VeraCrypt’s Volumes ▸ Set Header Key Derivation Algorithm, with any entry other than Argon2 chosen, re-encrypts the header with a key derived by PBKDF2 and leaves the data as it is. With the hidden volume’s password it changes the hidden volume’s header, and with the outer volume’s password the outer one’s.
A first install from Tails needs PBKDF2 as well.
Opening the volume
Section titled “Opening the volume”Prefer cryptsetup over the VeraCrypt application for VeraCrypt volumes. It
creates a plain kernel mapping with no userspace process behind it. Type these
commands on the decoy in a shell with history off (set +o history). The
examples use the neutral mapping name data and the mount point /mnt/data.
A VeraCrypt hidden volume inside a container file.
sudo cryptsetup open --type tcrypt --veracrypt --tcrypt-hidden \ /path/to/container datasudo mount /dev/mapper/data /mnt/data- cryptsetup attaches the container to a free loop device itself, and closing the mapping detaches it again.
- Run
nails initandnails checkon this root with sudo. Only root can read the loop device’s record of where the container file lives, and without it they refuse the root. - Every read-write session updates the container file’s modification and change times. The change time cannot be set back, so the container shows when it was last used.
- Opening the container from a file manager can add it to the desktop’s recent-files list.
- VeraCrypt’s release notes warn that a security issue fixed in VeraCrypt 1.26.29 may affect the deniability of hidden volumes created inside file containers with VeraCrypt 1.26.6 to 1.26.28. If you rely on that deniability, they say to recreate the outer container and its hidden volume with VeraCrypt 1.26.29 or later, then securely erase the old container.
A VeraCrypt hidden volume on a whole partition or disk.
sudo cryptsetup open --type tcrypt --veracrypt --tcrypt-hidden /dev/sdX2 datasudo mount /dev/mapper/data /mnt/data- A partition or whole disk created by VeraCrypt holds the outer and hidden volumes directly. There is no container file whose timestamps change.
- The partition shows up as random data, which the outer volume is there to explain.
A LUKS2 volume on a partition or disk.
sudo cryptsetup open /dev/sdX2 datasudo mount /dev/mapper/data /mnt/data- This is the backend the end-to-end suite runs on.
- A LUKS2 header announces an encrypted volume, so deniability rests on that volume having a plausible reason to exist. A detached header leaves the device without a signature. Fill the device with random data before formatting, so that used and unused areas look alike.
cryptsetup luksDumpmust report(no flags). A storedallow-discardsflag enables discards on every later open.
nails deactivate and nails emergency close the volume on the way back, so
you do not lock it after a session. Lock it yourself, with
sudo umount /mnt/data and then sudo cryptsetup close data, in three cases:
when you unlocked it on the decoy without activating, when nails recover
leaves it open and says so, and for a backend that is not a device-mapper
mapping. Closing the mapping does not clear every copy of the key from RAM. To
let RAM decay, end the session with nails deactivate --poweroff or
nails emergency --poweroff.
Hazards
Section titled “Hazards”- Outer volume writes. Write to a VeraCrypt outer volume only with
VeraCrypt’s hidden-volume protection. cryptsetup cannot open an outer volume
with that protection, so a write through it can overwrite the hidden volume.
Open the outer volume with
--readonlywhen you only need to show it. Seed its decoy content once, when you create it. - Copy-on-write filesystems and SSDs keep old copies of rewritten blocks. On such a decoy, prefer a hidden volume on a whole device or partition over a file container, and turn off automatic snapshots of any filesystem that holds a container.
- Wear-leveling. VeraCrypt’s security requirements for hidden volumes say not to store hidden volumes on devices or filesystems that use wear-leveling, such as a flash-memory SSD or a USB flash drive, because a copy or fragment of the volume may remain on the device.
- Repeated imaging. An adversary who images the disk twice, before and after further use, can see which encrypted regions changed. A hidden volume inside an outer volume that changed while the outer volume’s files did not is the classic way such volumes are found. NAILS cannot help with this, and the threat model leaves it out of scope.
- Container files during a session. Do not open a container file for writing in the hidden session. The overlay copies the whole file up first.
Keep it unremarkable
Section titled “Keep it unremarkable”- Choose neutral names for the mapping, the mount point and the filesystem
label. They surface in
/dev/mapper,/dev/disk/by-label, generated mount unit names and the journal. NAILS warns when one contains a telling word. - Unlock and mount the volume with
cryptsetupandmountfrom a shell, not through a file manager, GNOME Disks orudisksctl. Those go through udisks, which keeps per-drive state under/var/lib/udisks2. - Never list the hidden volume in the decoy’s
/etc/crypttab,/etc/fstabor NixOS configuration, and open it by hand each time. Thebackend-entriescheck refuses activation while a crypttab or fstab line opens or mounts the hidden volume or a device it is stored on, and--no-preflightdoes not skip it. A line that opens only another volume on the same drive warns. Remove such lines long before the next use, because the decoy’s configuration history can keep them. - On NixOS, the
suspicious-nails-referencecheck refuses activation while a.nixfile in/etc/nixosnames the hidden volume’s mount point, its device or the storage it is on, opens its mapping or names NAILS, in comments as in code. - The
backend-detectcheck reports hazards it can see, such as enabled discards, a file container, a container on a copy-on-write filesystem and LUKS2 tokens that name an unlock method. It warns and never blocks activation.