🛡 Qubes OS live mode. dom0 in RAM. Non-persistent Boot. RAM-Wipe. Protection against forensics. Tails mode. Hardening dom0. Root read‑only. Paranoid Security. Ephemeral Encryption

@RamLovingPenguin

Yes, dom0 running live mode + DVM using ephemeral volatile encryption (for root overlay on xvdc) + private volume (xvdb) running /home in tmpfs overlay + kernel params init_on_free=1 init_on_alloc=1 provides cryptographic-grade protection. Here’s exactly how it works and why it’s safe even after DVM shutdown while dom0 stays running.

What happens on clean DVM shutdown:

Volatile volume (xvdc) + root overlay = cryptographically wiped

  • Ephemeral key lived only in RAM (blkback crypto context).
  • On shutdown: key destroyed (memory freed), disk shows only unreadable encrypted garbage.
  • Result: All DVM root changes/swap data = 100% gone from disk.

Private volume (xvdb:/home) in tmpfs overlay = memory-only

  • /home runs entirely in tmpfs (RAM overlay), never touches persistent private.img on disk.
  • Sensitive data (files, VeraCrypt containers) lives only in DVM guest RAM.
  • Result: No disk traces of your actual working data whatsoever.

DVM guest RAM (root + /home tmpfs) = securely wiped

  • init_on_free=1/init_on_alloc=1 forces DVM kernel to zero all pages when freeing.
  • Xen hypervisor then frees guest pages, overwriting/recycling them securely (bootscrub=1).
  • Result: Files, passwords, VeraCrypt keys, screen buffers = completely destroyed.

This is roughly what a forensic analyst would see if he gained access to a running, decrypted dom0.

dispXXXX started 06:39, shutdown 06:45
Template: amnesic-dvm, pool: vm-pool  
Disk sizes: root=20G, volatile=12G, private=2G
Basic CPU/RAM/network stats by timestamp

He would only see some metadata indicating that a certain DVM had been started, and even those data would be erased once dom0 is shutdown.
He CANNOT recover:

  • Your /home tmpfs contents / cache or VeraCrypt data
  • Passwords/keys (all memory-only)

You can make all your AppVMs use ephemeral encryption too - add them to the ExecStart= line in /etc/systemd/system/rw.service and restart systemd:

sudo systemctl daemon-reload
sudo systemctl enable rw.service

To activate ephemeral encryption, the appVM must be shutdown. If you see errors in systemd, it’s because some specified appVM is running. Don’t worry about it - it will work after the dom0 reboot.

You can also make /home in all AppVMs run entirely in RAM (tmpfs overlay).
(Note: /usr/local/ doesn’t need tmpfs overlay since it doesn’t store logs/cache.)

Simply copy the commands from /rw/config/rc.local in my amnesic-dvm into your AppVMs.
Important: Commands differ for debian/fedora/kicksecure AppVMs vs Whonix AppVMs (Whonix has specific quirks).

Overlaying /home might break some programs (though I haven’t noticed issues). If something stops working after adding the overlay commands to /rw, replace them with this safer bind-mount approach:

mkdir -p /tmp/home-user
chown user:user /tmp/home-user  
chmod 700 /tmp/home-user
cp -a /home/user/. /tmp/home-user/
mount --bind /tmp/home-user /home/user
mount -o remount,size=2G /home/user
2 Likes

Does this work with the Lightqube project? https://forum.qubes-os.org/t/how-i-learned-to-love-liteqube-and-why-you-should-too-even-if-you-have-enough-ram

@tokaso80 Yes, it should work with all appVMs. I will perform the update now and add full ephemeral encryption.

@deeplow @OvalZero Please remove this comment - it’s outdated and may be misleading. It may mislead users. I can’t edit it. or label it “(outdated)”

The guide has been updated! AppVMs now feature full ephemeral encryption.

Your volatile volume (xvdc) with root and directories from the private volume now has ephemeral encryption. This is achieved by ephemeral_volatile=True , which encrypts the volatile volume with a RAM‑only key discarded on shutdown. In addition, qvm-volume config appvm:root rw False makes the root volume read‑only and mounts a temporary writable overlay whose writes are redirected to the encrypted volatile volume, so system changes outside /rw never persist. After launching an AppVM, all directories from the private volume’s (xvdb) /rw, /home, /var/spool/cron, and /usr/local (and other dirs in whonix-kicksecure appVM) - are copied into volatile space, their originals lazily unmounted, and volatile versions bind-mounted in place. This redirects all subsequent writes to ephemerally encrypted volatile volume (key discarded on shutdown), ensuring no forensic traces of changes persist on disk after reboot.

$ lsblk
NAME       MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
xvda       202:0    1    20G  1 disk 
├─xvda1    202:1    1   200M  1 part 
├─xvda2    202:2    1     2M  1 part 
└─xvda3    202:3    1  19.8G  1 part 
  └─dmroot 253:0    0  19.8G  0 dm   /rw
                                     /usr/local
                                     /home
                                     /
xvdb       202:16   1     2G  0 disk 
xvdc       202:32   1    10G  0 disk 
├─xvdc1    202:33   1     1G  0 part [SWAP]
└─xvdc2    202:34   1     9G  0 part 
  └─dmroot 253:0    0  19.8G  0 dm   /rw
                                     /usr/local
                                     /home
                                     /

You have only 60 seconds to edit /rw after launch, after which it remounts to the ephemeral volume and any changes are lost. To keep /rw on the private volume or enable fully ephemeral AppVMs immediately, modify these lines in /rw/config/rc.local:

sleep 60
remount_dir "/rw"

Now script no longer requires extra device memory - operations run in dom0 memory only, while overlay mode minimizes RAM usage, so you will see 100% free disk space reported. It works even on 8 GB devices. Ephemeral encryption offers strong forensics protection because the encryption key resides solely in RAM, and gets wiped by the kernel and Xen on shutdown, rendering disk data irretrievable.

I’ve tested it on many apps and on AppVMs in based on Debian, Fedora, Whonix, and Kicksecure templates - everything worked flawlessly.

2 Likes

Hey can you create a codeberg repo or github whatever so we can track your change ?

This would be more easier for people to see what changed

Also can you reply to the me in private ? i sent you a message and u didn’t reply

Hi! I didn’t even know there were private messages here (interesting does that use E2EE?). I want to create a perfect scenario from it, so I’m not rushing to codeberg and github yet. It seems like this is as close to ideal as possible right now. Sometimes it feels like only I and a couple of other users find it interesting :slightly_smiling_face: I think the Qubes team will add it in the next version - directing a private volume to a volatile volume isn’t such a big problem. But, since they can’t even fix the white squares on the panel, they have many other tasks :slightly_smiling_face: But I love the Qubes team for adding the ephemeral encryption feature for volatile and root volumes.

Your guide is awesome! Look at how many new users have written comments on this topic. thanks to your script, I now have a new hobby - testing guides from the qubes forum :grin: now I’m not afraid to break something in the live dom0.

5 Likes

Your post has more than 2K views people don’t give you some feedback but trust me many people are using it

Indeed but your live mode need a lot of test for that (this is not my domain)

2 Likes

@newqube @dkzkz thanks!

Test and enjoy. Now all my friends use this script. Live mode for dom0 works very simply, and modules from script can launch live mode for any Linux. Now ephemeral encryption for appVMs also works very simply. This is much safer and more efficient than my old script. And it’s better and safer than running appVM in the varlibqubes pool (RAM) with an outdated driver.

This reminded me actually, does this script make any part of Qubes OS any less secure? I remember reading about changing the default vm pool having security implications. Wondering if we’re gaining additional security in some areas at the expense of others. Is there ways to know for certain?

@RamLovingPenguin
This script cannot make Qubes less secure, as both scenarios render the real dom0 root strictly read-only. You cannot affect the real dom0 - whether by infecting it with malware or deleting root directories - since everything reverts after reboot.

The default varlibqubes pool was never intended for running appVMs, making the old approach inconvenient and vulnerable: dom0 had to run in default persistent mode for appVM modifications (customizations), any customization increased appVM disk size (TRIM didn’t work), and performing TRIM required copying the appVM to vm-pool, running TRIM, then copying it back to varlibqubes. Moreover, running appVMs from the varlibqubes pool consumed excessive RAM.

Now the issue is resolved, with much greater efficiency and security:

  • Ephemerally encrypted appVMs require no additional RAM (no need to buy expensive DDR5).
  • TRIM works automatically.
  • For customizations, simply disable ephemeral encryption on private volume directories (root encryption always remains active for logs and journals).
  • Forensic protection enable even in default persistent dom0 boot (forensic analysts see only metadata about appVM launches).
  • Attackers cannot make persistent changes to /rw, even during default persistent dom0 boot.

I now understand that even if I ran appVMs with private volumes in RAM instead of ephemeral encryption, I’d still use overlay/tmpfs inside appVMs from the vm-pool. Therefore, I recommend using the varlibqubes pool only in extreme cases or experimental scenarios.
It appears the varlibqubes pool may be removed entirely in future Qubes v5 (marmarek wrote about it on github).

2 Likes

I added this diagram to the post so you could better understand how it works

[Real dom0 root on disk]
 |
+--> Overlay-Live Mode (fast)
 |   [Real root] -> /live/image (read‑only)
 |   [tmpfs]     -> /cow/rw, /cow/work (RW)
 |   overlay -> lower: /live/image (disk read-only), work: /cow/work (RAM), upper:/cow/rw (RAM)
 |   kernel hardening
 |    => hardened dom0 runs from upper overlay (operates in RAM, all changes lost on shutdown) 
 |
+--> Zram-Live Mode (slowly)
     [Real root] -> /mnt (read‑only)
     /mnt copy on /dev/zram0 (zram block device)
     /mnt unmounted
     kernel hardening
      => hardened dom0 runs entirely from /dev/zram0 (all in RAM, all changes lost on shutdown)

[live dom0 (RAM)]
 └─ starts ephemeral appVM with volumes:
    xvda  -> root volume (template‑based, read‑only)
     └─ xvda3 -> dmroot_ro (read‑only lower overlay)
    xvdb  -> private volume (persistent, unused for live data)
    xvdc  -> volatile volume (encrypted, ephemeral - all changes lost on shutdown)
     ├─ xvdc1 -> SWAP
     └─ xvdc2 -> dmroot_rw (root on upper/work overlay)
          ├─ /home
          ├─ /rw
          └─ /usr/local

[ephemeral appVM shutdown]
     ├─ ephemeral encryption (all changes in volumes lost)
     └─ RAM-wipe (all data in RAM lost)

[dom0 shutdown]
 └─ Dracut RAM-wipe (all data in dom0 RAM lost)
1 Like

Fully ephemeral encrypted and RAM appVMs / DVMs without live dom0.

:lock: If you don’t want to install dom0 live modes and you only need ephemeral encrypted appVMs / DVMs in default persistent dom0 or if you want add ephemeral encryption to your old appVMs:

  • run in dom0 terminal:

qvm-pool set vm-pool -o ephemeral_volatile=True
qvm-volume config default-dvm:root rw False
qvm-volume config whonix-workstation-18-dvm:root rw False
(ps: and similar commands to other appVMs - change name appVM in commands)

  • add this commands to /rw/config/rc.local in appVM / dvm

for whonix-workstation-18-dvm and other whonix / kicksecure base appVMs / DVMs:

for i in {1..15}; do
if [ -b /dev/xvdc ] && mountpoint -q /volatile 2>/dev/null; then
break
fi
done

if ! mountpoint -q /volatile 2>/dev/null; then
mount /dev/xvdc /volatile || echo "Volatile mount failed"
fi

#
remount_dir() {
local dir="$1"
local volatile_dir="/volatile$dir"

mkdir -p "$volatile_dir"

[ -z "$(ls -A "$volatile_dir" 2>/dev/null)" ] && cp -a "/rw$dir/." "$volatile_dir/" 2>/dev/null || true
umount -l "$dir" 2>/dev/null || true
mount --bind "$volatile_dir" "$dir" || echo "Bind $dir failed"
}

#
mkdir -p /volatile/home
cp -a /rw/home/. /volatile/home/ 2>/dev/null || true
umount -l /home 2>/dev/null || true
mount --bind /volatile/home /home
remount_dir "/var/spool/cron"
remount_dir "/usr/local"
remount_dir "/var/lib/systemcheck"
remount_dir "/var/lib/canary"
remount_dir "/var/cache/setup-dist"
remount_dir "/var/lib/sdwdate"
remount_dir "/var/lib/dummy-dependency"
remount_dir "/var/cache/anon-base-files"
remount_dir "/var/lib/whonix"
USER_NAME="user"

USER_HOME="/home/$USER_NAME"
LOCAL_APP_DIR="$USER_HOME/.local/share/applications"
SYSTEM_DESKTOP="/usr/share/applications/pcmanfm-qt.desktop"
USER_DESKTOP="$LOCAL_APP_DIR/pcmanfm-qt.desktop"

mkdir -p "$LOCAL_APP_DIR"

if [ -r "$SYSTEM_DESKTOP" ]; then
if [ ! -e "$USER_DESKTOP" ]; then
cp "$SYSTEM_DESKTOP" "$USER_DESKTOP"
fi

sed -i "s|^Exec=.*|Exec=pcmanfm-qt $USER_HOME|" "$USER_DESKTOP"
fi

QTERMINAL_USER="$LOCAL_APP_DIR/qterminal.desktop"
if [ -r "/usr/share/applications/qterminal.desktop" ]; then
    cp "/usr/share/applications/qterminal.desktop" "$QTERMINAL_USER"
    # КЛЮЧЕВОЕ: bash -c с cd!
    sed -i "0,/^Exec=/s|^Exec=.*|Exec=bash -c 'cd /home/user \&\& exec qterminal'|" "$QTERMINAL_USER"
fi

sleep 60
remount_dir "/rw"

for default-dvmand other debian / fedora base appVMs:

mount /dev/xvdc /volatile 2>/dev/null || true

remount_dir() {
    local dir=$1
    local volatile_dir=/volatile$dir
    mkdir -p $volatile_dir
    [ -z "$(ls -A $volatile_dir 2>/dev/null)" ] && cp -a "/rw$dir/." $volatile_dir/ 2>/dev/null || true
    umount -l $dir 2>/dev/null || true
    mount --bind $volatile_dir $dir || true
}

mkdir -p /volatile/home
cp -a /rw/home/. /volatile/home/ 2>/dev/null || true
umount -l /home 2>/dev/null || true
mount --bind /volatile/home /home
remount_dir /var/spool/cron
remount_dir /usr/local
sleep 60
remount_dir /rw
  • create a systemd service for activate commands root rw False at system startup

sudo nano /etc/systemd/system/rw.service
add this commands:

[Unit]
Description=root rw False
After=qubesd.service
Requires=qubesd.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/qvm-volume config default-dvm:root rw False
ExecStart=/usr/bin/qvm-volume config whonix-workstation-18-dvm:root rw False

[Install]
WantedBy=multi-user.target

Ctrl + O, Ctrl + X
(add similar ExecStart commands to other appVMs)

run in dom0 terminal:

sudo systemctl daemon-reload
sudo systemctl enable rw.service

:white_check_mark: Done.

:clock2: If you don’t want to install dom0 live modes and you only need RAM appVMs / DVMs:

  • Change dom0_mem=max: in /etc/default/grub. For example, to 10G. Run in dom0 terminal:
sudo nano /etc/default/grub
#change dom0_mem=max: -> dom0_mem=max:10240M
#click CTRL + O and CTRL + X
  • Create new pool in dom0 RAM (varlibqubes pool not anti-forensic in the default persistent dom0). Run in dom0 terminal:
sudo mkdir -p /mnt/ram-pool
sudo mount -t tmpfs -o size=10G tmpfs /mnt/ram-pool
qvm-pool add rampool file --option revisions_to_keep=1 --option dir_path=/mnt/ram-pool
qvm-pool set rampool -o ephemeral_volatile=True
  • Then, in Qube Manager create a new appVM, and select rampool Storage pool in the Advanced Options.

:white_check_mark: Done.

:warning: Default persistent dom0 will retain metadata about DVM launch!
See Reduce leakage of disposable VM content and history into dom0 filesystem ¡ Issue #4972 ¡ QubesOS/qubes-issues ¡ GitHub

1 Like

Anti-forensics feature (live mode and ephemeral encryption) for appVMs should appear by default in the next version of Qubes, thanks to the kicksecure devs!

2 Likes

The /rw Is Still a Persistence Vector

The script copies /rw/home , /rw/usr/local , etc., into volatile space.
If /rw is compromised (e.g., malware in cron, .bashrc , or bind-dirs), it replicates into every session .
This breaks true ephemeral behavior you’re copying persistence into volatility .

Just use clean Whonix and Kicksecure appVMs. An attacker will not be able to obtain root access to remount the directories back to private volume.

I think it’s still better to make the private volume (/rw) of those dvms read only. Otherwise, a compromised dvm is one umount away from writing to non-ephemeral disk (and planting evidence etc.). And the Qubes security model recommends that dvms should be used when dealing with things that may compromise you.

Or even better, just drop the sleep line and ask users to make the private volume read-write when making modifications. Let the appvm/dvm show a warning dialog and skip the bind mount when it detects the private volume is rw.

I was trying this idea of ro root + ro private. This almost works, except that it now asks you to download and install tor browser.

Additionally, it seems that if you skip all this and just make the volume group ephemeral_volatile=True and the root/private volumes rw False, then everything works, because the private volume is unformatted, its mounts fail, and the overlay ends up covering /rw and /home too.

BTW, the script for dvm-only ephemeral seems to have a bug, where the copy source should be /rw/bind-dirs, not /rw. It also still has unneeded \ escapes.

I already described it - I left 1 minute for editing /rw for those who want to customize appVM. Otherwise, appVM will never save customizations. It’s hard for me to imagine that someone could get infected by such a malicious script within 1 minute, whose task is to remount the private volume. If someone wants to remove the sleep, they can do so - I mentioned it in the guide. But I don’t think leaving it on by default is a good idea.