Qubes OS live mode. dom0 in RAM (zram0, overlay in tmpfs). Protection against forensics. Tails mode

This guide combines the best practices from this topic Qubes in tmpfs 🤫.

It offers two live‑mode options in a single convenient menu. The old topic’s founder and one of its main contributors are no longer active on forum. I created this guide so users don’t have to read through the entire topic of almost 100 comments looking for a final solution. I’ve been using this guide for four months.

You will get two ways to launch dom0 in RAM for protection against forensics:

  1. dom0 in zram0
  2. dom0 in overlay-tmpfs (Kicksecure/Whonix live mode)

This also works great for experiments in Qubes or for beginners who want to learn without fear of breaking anything - all changes disappear after a reboot. This guide also solves the old problem of porting grub‑live to Qubes.
launching requires a lot of memory: at least 16 GB for zram0 mode and at least 32 GB for overlayfs on tmpfs mode.

Make a backup before you start working!

+ Step 1. Disable Dom0 Swap:

sudo nano /etc/fstab

Using nano, type a # character in front of the line containing “/dev/mapper/qubes_dom0-swap”, to comment it out and disable it, like this:

#/dev/mapper/qubes_dom0-swap ...

Press Ctrl + O to save file.
Press Ctrl + X to exit nano editor.

That will permanently disable your Dom0 Swap after restart, which is likely best for operating Qubes Dom0 in live mode.

If you still want to frequently use Qubes in Persistent mode and have Dom0 Swap enabled, then you can alternatively skip this permanent disable step, and rather temporarily disable Dom0 Swap when you startup Qubes Live by remembering (each and every time) to manually run the following Dom0 command…

sudo swapoff -a

+ Step 2. Edit GRUB to Increase Dom0’s Max RAM:

sudo nano /etc/default/grub

Within the GRUB_CMDLINE_XEN_DEFAULT line, make a partial modification of this line, from dom0_mem=max:4096M to now be dom0_mem=max:10240M → Just change the text “4096M” to “10240M” (without quotes).

Press Ctrl + O to save grub file.
Press Ctrl + X to exit nano editor.

Note: If you have something like 32GB of RAM or greater in your system, and you want to run several fully stateless user qubes within Dom0 RAM space, then you may want to consider increasing this “10240M” Dom0 RAM maximum value, in order to allow for more Dom0 RAM space where you can store more user qubes as fully stateless. For example, with 32GB of system RAM, you may choose to allocate “20480M” (20GB) or even greater if you wish.
!! Important: also change ram-disk size in Step 8 line echo 10G > /sys/block/zram0/disksize

Regenerate with New GRUB Configuration:
If you are using Qubes 4.2 / 4.3, you should run this single command:

sudo grub2-mkconfig -o /boot/grub2/grub.cfg

+ Step 3. Make New Directorys for Dracut Automation Modules:

sudo mkdir /usr/lib/dracut/modules.d/01ramboot
sudo mkdir /usr/lib/dracut/modules.d/90overlayfs-root

+ Step 4. Make Two Dracut Script Files module-setup.sh:

sudo touch /usr/lib/dracut/modules.d/01ramboot/module-setup.sh
sudo nano /usr/lib/dracut/modules.d/01ramboot/module-setup.sh

add:

#!/usr/bin/bash
check() {
return 0
}
depends() {
return 0
}
install() {
inst_simple "$moddir/tmpfs.sh" "/usr/bin/tmpfs"
inst_hook cleanup 00 "$moddir/pass.sh"
}

Press Ctrl + O to save file.
Press Ctrl + X to exit nano editor.

sudo touch /usr/lib/dracut/modules.d/90overlayfs-root/module-setup.sh
sudo nano /usr/lib/dracut/modules.d/90overlayfs-root/module-setup.sh

add:

#!/bin/bash

check() {
    # do not add modules if the kernel does not have overlayfs support
    [ -d /lib/modules/$kernel/kernel/fs/overlayfs ] || return 1
}

depends() {
    # We do not depend on any modules - just some root
    return 0
}

# called by dracut
installkernel() {
    hostonly='' instmods overlay
}

install() {
    inst_hook pre-pivot 10 "$moddir/overlay-mount.sh"
}

Press Ctrl + O to save file.
Press Ctrl + X to exit nano editor.

+ Step 5. Make New Dracut Script File overlay-mount.sh:

sudo touch /usr/lib/dracut/modules.d/90overlayfs-root/overlay-mount.sh
sudo nano /usr/lib/dracut/modules.d/90overlayfs-root/overlay-mount.sh

add:

#!/bin/sh

# make a read-only nfsroot writeable by using overlayfs
# the nfsroot is already mounted to $NEWROOT

. /lib/dracut-lib.sh

if ! getargbool 0 rootovl ; then
    return
fi

modprobe overlay

# a little bit tuning
mount -o remount,nolock,noatime $NEWROOT

# Move root
mkdir -p /live/image
mount --bind $NEWROOT /live/image
umount $NEWROOT

# Create tmpfs
mkdir /cow
mount -n -t tmpfs -o mode=0755,size=100% tmpfs /cow
mkdir /cow/work /cow/rw

# Merge both to new Filesystem
mount -t overlay -o noatime,lowerdir=/live/image,upperdir=/cow/rw,workdir=/cow/work,default_permissions overlay $NEWROOT

# Let filesystems survive pivot
mkdir -p $NEWROOT/live/cow
mkdir -p $NEWROOT/live/image
mount --bind /cow/rw $NEWROOT/live/cow
umount /cow
mount --bind /live/image $NEWROOT/live/image
umount /live/image

Press Ctrl + O to save file.
Press Ctrl + X to exit nano editor.

+ Step 6. Make New Dracut Script File pass.sh:

sudo touch /usr/lib/dracut/modules.d/01ramboot/pass.sh
sudo chmod 755 /usr/lib/dracut/modules.d/01ramboot/pass.sh
sudo nano /usr/lib/dracut/modules.d/01ramboot/pass.sh

Add:

#!/usr/bin/bash
command -v ask_for_password >/dev/null || . /lib/dracut-crypt-lib.sh
PROMPT="Enter boot mode: t-tmpfs, z-zram0, s-ssd"
CMD="/usr/bin/tmpfs"
TRY="3"
ask_for_password --cmd "$CMD" --prompt "$PROMPT" --tries "$TRY" --ply-cmd "$CMD" --ply-prompt "$PROMPT" --ply-tries "$TRY" --tty-cmd "$CMD" --tty-prompt "$PROMPT" --tty-tries "$TRY" --tty-echo-off

Press Ctrl + O to save file.
Press Ctrl + X to exit nano editor.

You will need to press one of three letters during the Qubes startup. You can edit the line “Enter boot mode: t-tmpfs, z-zram, s-ssd” however you like (but keep in mind that the letter used to launch a mode must not match the first letter of your password! Otherwise, one of the modes will start automatically after you enter the password). If you want to use different letters to launch the modes, then specify those same letters in the next script (Step 8)!

+ Step 7. Make New Dracut Script File tmpfs.sh:

sudo touch /usr/lib/dracut/modules.d/01ramboot/tmpfs.sh
sudo chmod 755 /usr/lib/dracut/modules.d/01ramboot/tmpfs.sh
sudo nano /usr/lib/dracut/modules.d/01ramboot/tmpfs.sh

add:

#!/bin/sh
read line
case "${line:-Nn}" in
[Tt]* )
. /lib/dracut-lib.sh
modprobe overlay
# a little bit tuning
mount -o remount,nolock,noatime $NEWROOT
# Move root
mkdir -p /live/image
mount --bind $NEWROOT /live/image
umount $NEWROOT
# Create tmpfs
mkdir /cow
mount -n -t tmpfs -o mode=0755,size=100% tmpfs /cow
#mount /dev/zram0 /cow
mkdir /cow/work /cow/rw
# Merge both to new Filesystem
mount -t overlay -o noatime,lowerdir=/live/image,upperdir=/cow/rw,workdir=/cow/work,default_permissions overlay $NEWROOT
# Let filesystems survive pivot
mkdir -p $NEWROOT/live/cow
mkdir -p $NEWROOT/live/image
mount --bind /cow/rw $NEWROOT/live/cow
umount /cow
mount --bind /live/image $NEWROOT/live/image
umount /live/image
;;
[Zz]* )
mkdir /mnt
umount /sysroot
mount /dev/mapper/qubes_dom0-root /mnt
modprobe zram
echo 10G > /sys/block/zram0/disksize
/mnt/usr/sbin/mkfs.ext2 /dev/zram0
mount /dev/zram0 /sysroot
cp -a /mnt/* /sysroot
exit 0
;;
[Ss]* )
exit 0
;;
* )
exit 1
;;
esac

Press Ctrl + O to save file.
Press Ctrl + X to exit nano editor.

+ Step 8. Make New Dracut Config File ramboot.conf:

sudo touch /etc/dracut.conf.d/ramboot.conf
sudo nano /etc/dracut.conf.d/ramboot.conf

add:

add_drivers+=" zram "
add_dracutmodules+=" ramboot "

Press Ctrl + O to save file.
Press Ctrl + X to exit nano editor.

+ Step 9. Regenerate with New Dracut Automation Module:

sudo dracut --verbose --force

+ Step 10. Clone dangerous qubes (dvm‑template, appVMs) into a pool in dom0

In the Qube Manager click Clone Qube, then in Advanced select a pool in dom0 (not vm‑pool). If you have a lot of memory, you can run all appVMs (sys, dvm, appVM) in live mode.

Since only dom0 runs in live mode, you have to start the VMs from the pool in dom0. You can use the default pool varlibqubes or create a new pool.

Then Restart Qubes OS and Test Qubes live modes :wink:

My boot menu version (I just type the first letter of each mode at boot :slightly_smiling_face:)

zram0 mode saves a lot of memory (you can run roughly twice as many qubes in live mode!), but it takes longer to start (about 30–40 seconds more) and is slightly less secure than overlay-tmpfs.
overlay in tmpfs loads quickly and is maximally secure, but it requires more memory.

Remember that the data will be erased only after a reboot of Qubes OS (like Tails).
You can update templates in live mode, but update dom0 in persistent mode.
If you create a qube in the vm‑pool while in live mode, this qube won’t be saved in the Qube Manager after a reboot.
You can make backups in live mode (I’ve done it many times using a tmpfs setup).

:flashlight: You can use this terminal theme so can see which mode you’re currently in:
Click CTRL + H in thunar of dom0 and add this code into .bashrc instead of the default code:

# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
	. /etc/bashrc
fi

# User specific environment
if ! [[ "$PATH" =~ "$HOME/.local/bin:$HOME/bin:" ]]
then
    PATH="$HOME/.local/bin:$HOME/bin:$PATH"
fi
export PATH

###########################


export VIRTUAL_ENV_DISABLE_PROMPT=true


__qubes_update_prompt_data() {
	local RETVAL=$?

	__qubes_venv=''
	[[ -n "$VIRTUAL_ENV" ]] && __qubes_venv=$(basename "$VIRTUAL_ENV")

	__qubes_git=''
	__qubes_git_color=$(tput setaf 10)  # clean
	local git_branch=$(git --no-optional-locks rev-parse --abbrev-ref HEAD 2> /dev/null)
	if [[ -n "$git_branch" ]]; then
		local git_status=$(git --no-optional-locks status --porcelain 2> /dev/null | tail -n 1)
		[[ -n "$git_status" ]] && __qubes_git_color=$(tput setaf 11)  # dirty
		__qubes_git="‹${git_branch}›"
	fi

	__qubes_prompt_symbol_color=$(tput sgr0)
	[[ "$RETVAL" -ne 0 ]] && __qubes_prompt_symbol_color=$(tput setaf 1)


	return $RETVAL  # to preserve retcode
}


if [[ -n "$git_branch" ]]; then
	PROMPT_COMMAND="$PROMPT_COMMAND; __qubes_update_prompt_data"
else
	PROMPT_COMMAND="__qubes_update_prompt_data"
fi


PS1=''
PS1+='\[$(tput setaf 7)\]$(echo -ne $__qubes_venv)\[$(tput sgr0)\]'
PS1+='\[$(tput setaf 14)\]\u'
PS1+='\[$(tput setaf 15)\] đź‘‘ '
PS1+='\[$(tput setaf 9)\]\h'
PS1+=" $(findmnt -n -o SOURCE /)"
PS1+='\[$(tput setaf 15)\]:'
PS1+='\[$(tput setaf 7)\]\w '
PS1+='\[$(echo -ne $__qubes_git_color)\]$(echo -ne $__qubes_git)\[$(tput sgr0)\] '
PS1+='\[$(tput setaf 8)\]\[$([[ -n "$QUBES_THEME_SHOW_TIME" ]] && echo -n "[\t]")\]\[$(tput sgr0)\]'
PS1+='\[$(tput sgr0)\]\n'
PS1+='\[$(echo -ne $__qubes_prompt_symbol_color)\]\$\[$(tput sgr0)\] '


If you have limited memory, use this guide: Really disposable (RAM based) qubes

3 Likes

Script for creating Dracut modules for live mode.

Script does not modify /etc/default/grub and /etc/fstab from Step 1 and Step 2.

1. Perform Step 1 and Step 2 from guide (Disable dom0 Swap and Edit dom0 Max Ram in GRUB)

2. Run this script with sudo (for example, sudo ./live.sh).

Important! If you increased dom0_mem=max to more than 10 GB in grub, adjust ram‑disk size in the script on the 127 line echo 10G > /sys/block/zram0/disksize

#!/bin/bash

# Create Dracut Modules for Live Mode
# ⚠️ Make backup before running!
# Run as root: sudo ./live.sh

set -e  # Exit on any error

echo "=== Qubes Dom0 Live Modules ==="
echo "Disable Dom0 Swap"
swapoff -a
echo "Create Dracut directories"
mkdir -p /usr/lib/dracut/modules.d/01ramboot
mkdir -p /usr/lib/dracut/modules.d/90overlayfs-root

echo "Create module-setup.sh files..."

# 01ramboot/module-setup.sh
cat > /usr/lib/dracut/modules.d/01ramboot/module-setup.sh << 'EOF'
#!/usr/bin/bash
check() {
return 0
}
depends() {
return 0
}
install() {
inst_simple "$moddir/tmpfs.sh" "/usr/bin/tmpfs"
inst_hook cleanup 00 "$moddir/pass.sh"
}
EOF

# 90overlayfs-root/module-setup.sh
cat > /usr/lib/dracut/modules.d/90overlayfs-root/module-setup.sh << 'EOF'
#!/bin/bash

check() {
    [ -d /lib/modules/$kernel/kernel/fs/overlayfs ] || return 1
}

depends() {
    return 0
}

installkernel() {
    hostonly='' instmods overlay
}

install() {
    inst_hook pre-pivot 10 "$moddir/overlay-mount.sh"
}
EOF

echo "Create overlay-mount.sh"
cat > /usr/lib/dracut/modules.d/90overlayfs-root/overlay-mount.sh << 'EOF'
#!/bin/sh
. /lib/dracut-lib.sh

if ! getargbool 0 rootovl ; then
    return
fi

modprobe overlay
mount -o remount,nolock,noatime $NEWROOT
mkdir -p /live/image
mount --bind $NEWROOT /live/image
umount $NEWROOT
mkdir /cow
mount -n -t tmpfs -o mode=0755,size=100% tmpfs /cow
mkdir /cow/work /cow/rw
mount -t overlay -o noatime,lowerdir=/live/image,upperdir=/cow/rw,workdir=/cow/work,default_permissions overlay $NEWROOT
mkdir -p $NEWROOT/live/cow
mkdir -p $NEWROOT/live/image
mount --bind /cow/rw $NEWROOT/live/cow
umount /cow
mount --bind /live/image $NEWROOT/live/image
umount /live/image
EOF

echo "Create pass.sh and tmpfs.sh"
chmod 755 /usr/lib/dracut/modules.d/01ramboot/pass.sh 2>/dev/null || touch /usr/lib/dracut/modules.d/01ramboot/pass.sh && chmod 755 /usr/lib/dracut/modules.d/01ramboot/pass.sh

cat > /usr/lib/dracut/modules.d/01ramboot/pass.sh << 'EOF'
#!/usr/bin/bash
command -v ask_for_password >/dev/null || . /lib/dracut-crypt-lib.sh
PROMPT="Enter boot mode: t-tmpfs, z-zram, s-ssd"
CMD="/usr/bin/tmpfs"
TRY="3"
ask_for_password --cmd "$CMD" --prompt "$PROMPT" --tries "$TRY" --ply-cmd "$CMD" --ply-prompt "$PROMPT" --ply-tries "$TRY" --tty-cmd "$CMD" --tty-prompt "$PROMPT" --tty-tries "$TRY" --tty-echo-off
EOF

cat > /usr/lib/dracut/modules.d/01ramboot/tmpfs.sh << 'EOF'
#!/bin/sh
read line
case "${line:-Nn}" in
[Tt]* )
. /lib/dracut-lib.sh
modprobe overlay
# a little bit tuning
mount -o remount,nolock,noatime $NEWROOT
# Move root
# --move does not always work. Google >mount move "wrong fs"< for
#     details
mkdir -p /live/image
mount --bind $NEWROOT /live/image
umount $NEWROOT
# Create tmpfs
mkdir /cow
mount -n -t tmpfs -o mode=0755,size=100% tmpfs /cow
#mount /dev/zram0 /cow
mkdir /cow/work /cow/rw
# Merge both to new Filesystem
mount -t overlay -o noatime,lowerdir=/live/image,upperdir=/cow/rw,workdir=/cow/work,default_permissions overlay $NEWROOT
# Let filesystems survive pivot
mkdir -p $NEWROOT/live/cow
mkdir -p $NEWROOT/live/image
mount --bind /cow/rw $NEWROOT/live/cow
umount /cow
mount --bind /live/image $NEWROOT/live/image
umount /live/image
;;
[Zz]* )
mkdir /mnt
umount /sysroot
mount /dev/mapper/qubes_dom0-root /mnt
modprobe zram
echo 10G > /sys/block/zram0/disksize
/mnt/usr/sbin/mkfs.ext2 /dev/zram0
mount /dev/zram0 /sysroot
cp -a /mnt/* /sysroot
exit 0
;;
[Ss]* )
exit 0
;;
* )
exit 1
;;
esac
EOF

chmod 755 /usr/lib/dracut/modules.d/01ramboot/tmpfs.sh

echo "Create dracut.conf"
cat > /etc/dracut.conf.d/ramboot.conf << 'EOF'
add_drivers+=" zram "
add_dracutmodules+=" ramboot "
EOF

echo "Regenerate Dracut (this takes several minutes)..."
dracut --verbose --force
echo "âś… ALL STEPS COMPLETED SUCCESSFULLY!"