This guide combines the best practices from this topic https://forum.qubes-os.org/t/qubes-in-tmpfs/11127 and it adds new options and capabilities for launching Qubes in RAM. (The old topic’s founder and one of its main contributors are no longer active on forum)
This guide adds two new options to the GRUB menu for safely launching live modes. You will get two ways to launch dom0 in RAM for protection against forensics: 1. Qubes Overlay-Live Mode 2. Qubes Zram-Live Mode
This guide solves the old problem of implement live boot by porting grub-live to Qubes - amnesia / non-persistent boot / anti-forensics. 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. It will extend the lifespan of your SSD.
> ❗️ Overlay-Live Mode (used by native kicksecure/Whonix) is more secure and fast:
> Root filesystem is effectively read‑only.
> The system runs on an overlay: the original root is mounted as lowerdir=/live/image (read‑only view), and all changes go into upperdir=/cow/rw on tmpfs. This makes it hard for malware to persist on the real disk, since any modifications live only in the volatile upper layer. Overlay in tmpfs does not create and format new block devices nor copy the entire root filesystem at runtime. There are fewer points where an attacker could hook into disk‑level operations or tamper with filesystem creation and bulk copying.
Overlay is much closer to a real Tails OS (a read‑only image). I added some additional flags to overlay‑tmpfs to strengthen the security of the live mode.
> Zram‑Live Mode works by copying (cp -a) root to zram0 - it is a significantly less secure and slower scenario. But it saves RAM usage dramatically (~ 2x Overlay-Live Mode).
You will need: at least 16 GB RAM for Zram-Live Mode and at least 24 GB RAM for Overlay-Live Mode for comfortably launching several qubes in live 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 with word “swap” in the middle (usually the last line). Like this:
#UUID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx swap defaults,x-systemd.device-timeout=0 0 0
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.
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.
> ❗️ 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
+ 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/zram-mount.sh"
inst_hook cleanup 00 "$moddir/zram-mount.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"
}
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
. /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=95%,nr_inodes=500k,noexec,nodev,nosuid,relatime tmpfs /cow
mkdir /cow/work /cow/rw
mount -t overlay -o noatime,volatile,lowerdir=/live/image,upperdir=/cow/rw,workdir=/cow/work,default_permissions,relatime 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
Ctrl + O to save file.
Press Ctrl + X to exit nano editor.
+ Step 6. Make New Dracut Script File zram-mount.sh:
sudo touch /usr/lib/dracut/modules.d/01ramboot/zram-mount.sh
sudo chmod 755 /usr/lib/dracut/modules.d/01ramboot/zram-mount.sh
sudo nano /usr/lib/dracut/modules.d/01ramboot/zram-mount.sh
add:
#!/bin/sh
. /lib/dracut-lib.sh
if ! getargbool 0 rootzram ; then
return
fi
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
Ctrl + O to save file.
Press Ctrl + X to exit nano editor.
+ Step 7. 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 "
Ctrl + O to save file.
Press Ctrl + X to exit nano editor.
+ Step 8. Regenerate with New Dracut Automation Module:
sudo dracut --verbose --force
+Step 9. Creating a script to automatically edit /etc/grub.d/40_custom.
sudo touch /usr/local/bin/grub-custom.sh
sudo chmod 755 /usr/local/bin/grub-custom.sh
sudo nano /usr/local/bin/grub-custom.sh
Add:
#!/bin/bash
#BOOT_UUID
BOOT_UUID=$(findmnt -n -o UUID /boot 2>/dev/null || echo "AUTO_BOOT_NOT_FOUND")
if [ "$BOOT_UUID" = "AUTO_BOOT_NOT_FOUND" ]; then
BOOT_UUID=$(blkid -s UUID -o value -d $(findmnt -n -o SOURCE /boot 2>/dev/null))
fi
# LUKS_UUID
LUKS_DEVICE=$(blkid -t TYPE="crypto_LUKS" -o device 2>/dev/null | head -n1 || echo "")
if [ -n "$LUKS_DEVICE" ]; then
LUKS_UUID=$(sudo cryptsetup luksUUID "$LUKS_DEVICE" 2>/dev/null)
else
LUKS_UUID="AUTO_LUKS_NOT_FOUND"
fi
# Latest XEN_PATH
XEN_PATH=$(ls /boot/xen*.gz 2>/dev/null | sort -V | tail -1 | xargs basename 2>/dev/null || echo "/xen-4.19.4.gz")
# Latest kernel/initramfs
LATEST_KERNEL=$(ls /boot/vmlinuz-*qubes*.x86_64 2>/dev/null | grep -E 'qubes\.fc[0-9]+' | sort -V | tail -1 | xargs basename)
LATEST_INITRAMFS=$(echo "/initramfs-${LATEST_KERNEL#vmlinuz-}.img")
cat > /etc/grub.d/40_custom << EOF
#!/usr/bin/sh
exec tail -n +3 \$0
menuentry 'Qubes Overlay-Live Mode (latest kernel)' --class qubes --class gnu-linux --class gnu --class os --class xen \$menuentry_id_option 'xen-gnulinux-simple-/dev/mapper/qubes_dom0-root' {
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root $BOOT_UUID
echo 'Loading Xen ...'
if [ "\$grub_platform" = "pc" -o "\$grub_platform" = "" ]; then
xen_rm_opts=
else
xen_rm_opts="no-real-mode edd=off"
fi
insmod multiboot2
multiboot2 /$XEN_PATH placeholder console=none dom0_mem=min:1024M dom0_mem=max:10240M ucode=scan smt=off gnttab_max_frames=2048 gnttab_max_maptrack_frames=4096 \${xen_rm_opts}
echo 'Loading Linux $LATEST_KERNEL ...'
module2 /$LATEST_KERNEL placeholder root=/dev/mapper/qubes_dom0-root ro rd.luks.uuid=$LUKS_UUID rd.lvm.lv=qubes_dom0/root rd.lvm.lv=qubes_dom0/swap plymouth.ignore-serial-consoles rhgb rootovl quiet usbcore.authorized_default=0
echo 'Loading initial ramdisk ...'
insmod multiboot2
module2 --nounzip $LATEST_INITRAMFS
}
menuentry 'Qubes Zram-Live Mode (latest kernel)' --class qubes --class gnu-linux --class gnu --class os --class xen \$menuentry_id_option 'xen-gnulinux-simple-/dev/mapper/qubes_dom0-root' {
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root $BOOT_UUID
echo 'Loading Xen ...'
if [ "\$grub_platform" = "pc" -o "\$grub_platform" = "" ]; then
xen_rm_opts=
else
xen_rm_opts="no-real-mode edd=off"
fi
insmod multiboot2
multiboot2 /$XEN_PATH placeholder console=none dom0_mem=min:1024M dom0_mem=max:10240M ucode=scan smt=off gnttab_max_frames=2048 gnttab_max_maptrack_frames=4096 \${xen_rm_opts}
echo 'Loading Linux $LATEST_KERNEL ...'
module2 /$LATEST_KERNEL placeholder root=/dev/mapper/qubes_dom0-root ro rd.luks.uuid=$LUKS_UUID rd.lvm.lv=qubes_dom0/root rd.lvm.lv=qubes_dom0/swap plymouth.ignore-serial-consoles rhgb rootzram quiet usbcore.authorized_default=0
echo 'Loading initial ramdisk ...'
insmod multiboot2
module2 --nounzip $LATEST_INITRAMFS
}
EOF
# Update Grub
grub2-mkconfig -o /boot/grub2/grub.cfg
Press Ctrl + O to save file.
Press Ctrl + X to exit nano editor.
Edit 40_custom file and update GRUB:
sudo /usr/local/bin/grub-custom.sh
+ Step 10. Clone dangerous qubes (dvm‑template, appVMs) into a pool in dom0
> ❗️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.
In the Qube Manager click clone qube, then in Advanced select a pool in dom0 (varlibqubes or your new pool. not vm‑pool). If you have a lot of memory, you can run all appVMs (sys, dvm, appVM) in live mode.
> ❗️Don't add templates to varlibqubes - templates don't retain any session data in an appVM (that's the point of Qubes' isolation). So, in live mode it's sufficient that only the private and volatile storages are active. You can inspect all template metadata yourself and verify that none of them contain artifacts from sessions in appVM.
Restart Qubes OS and Test Qubes live modes 😉
> Zram-Live Mode takes longer to start (about 30–40 seconds more).
> ❗️ You can update templates in live modes, but update dom0 in persistent mode!
Now if dom0 updates kernels, just run sudo /usr/local/bin/grub-custom.sh and the live modes will work with the new kernels.
> Remember that the data will be erased only after a reboot of Qubes OS (like Tails). > 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).
🔦
You can add a “System Monitor” widget to the XFCE panel and configure it to run command findmnt -n -o SOURCE /. This widget will display which mode you’re currently in:
You can also 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: https://forum.qubes-os.org/t/really-disposable-ram-based-qubes/21532