Overlayfs allows one, usually read-write, directory tree to be overlaid onto another, read-only directory tree. All modifications go to the upper, writable layer. This type of mechanism is most often used for live CDs but there is a wide variety of other uses.
You can read more about this in numerous documentations and wiki pages.
It’s a popular environment for testing, and it can be used for full ephemerality. Now, I want to compile all ephemeral modules and scripts into a single guide so that anyone can use them for ephemerality in individual directories or for any type of VMs (dom0, AppVMs, Templates, StandaloneVMs).
The key difference between an overlayfs mount with tmpfs/zram/dm-crypt upper layer and a direct tmpfs mount is that the latter gives you either an empty directory or a full copy of all files consuming RAM upfront. OverlayFS completely avoids this compromise: files remain on disk in read-only mode, and only modifications are directed to the ephemeral upper layer. As a result, RAM is consumed solely by actual changes, not by the entire dataset.
For example, with a 5 GB
/homecontaining files: a direct tmpfs mount consumes 5 GB of RAM immediately, even if the user touches nothing. An overlayfs with the same 5 GB on disk as lowerdir and tmpfs as upperdir starts at near-zero RAM usage, growing only as files are actually modified.
We will cover three types of overlayfs setups: one backed by tmpfs, on a zram device, and a third using plain dm-crypt.
Dracut modules: entire system in overlayfs. This works well with dom0 or in StandaloneVMs.
Fedora / RHEL and openSUSE use dracut by default. If you are using Debian/Ubuntu in a StandaloneVM, you can safely install dracut as follows:
sudo apt install --no-install-recommends dracut
sudo apt purge initramfs-tools
sudo dpkg-reconfigure linux-image-$(uname -r)
1. Overlayfs on tmpfs (50% of RAM):
The kernel command-line option tmpfsovl is used to enable this module.
sudo tee /usr/lib/dracut/modules.d/90overlayfs-tmpfs/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
sudo tee /usr/lib/dracut/modules.d/90overlayfs-tmpfs/overlay-mount.sh<< 'EOF'
#!/bin/sh
. /lib/dracut-lib.sh
if ! getargbool 0 tmpfsovl ; 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=50%,nr_inodes=500k,noexec,nodev,nosuid,noatime,nodiratime tmpfs /cow
mkdir /cow/work /cow/rw
mount -t overlay -o noatime,nodiratime,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
umount $NEWROOT/live/cow
EOF
2. Overlayfs on zram block device (10GB):
The kernel command-line option zramovl is used to enable this module.
sudo tee /usr/lib/dracut/modules.d/90overlayfs-zram/module-setup.sh<< 'EOF'
#!/bin/bash
check() {
[ -d /lib/modules/$kernel/kernel/fs/overlayfs ] || return 1
[ -d /lib/modules/$kernel/kernel/drivers/block/zram ] || return 1
return 0
}
depends() {
return 0
}
installkernel() {
hostonly='' instmods overlay zram ext2
}
install() {
inst_multiple mkfs.ext2
# Optional
# inst_multiple zramctl
inst_hook pre-pivot 10 "$moddir/overlayz-mount.sh"
}
EOF
sudo tee /usr/lib/dracut/modules.d/90overlayfs-zram/overlay-mount.sh<< 'EOF'
#!/bin/bash
. /lib/dracut-lib.sh
if ! getargbool 0 zramovl ; then
return
fi
modprobe overlay
modprobe zram
if [ ! -b /dev/zram0 ]; then
echo 1 > /sys/class/zram-control/hot_add 2>/dev/null || true
fi
echo 1 > /sys/block/zram0/reset 2>/dev/null || true
echo 10G > /sys/block/zram0/disksize
mkfs.ext2 -F -m 0 -q /dev/zram0
mount -o remount,nolock,noatime $NEWROOT
mkdir -p /live/image
mount --bind $NEWROOT /live/image
umount $NEWROOT
mkdir -p /cow
mount -n -t ext2 -o noatime,nodiratime,noexec,nodev,nosuid /dev/zram0 /cow
mkdir -p /cow/work /cow/rw
mount -t overlay -o noatime,nodiratime,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
mount --bind /live/image $NEWROOT/live/image
umount /live/image
EOF
3. Overlayfs on plain dm-crypt (5GB):
The kernel command-line option cryptovl is used to enable this module.
sudo tee /usr/lib/dracut/modules.d/90overlayfs-crypt/module-setup.sh<< 'EOF'
#!/bin/bash
check() {
require_binaries cryptsetup || return 1
require_binaries losetup || return 1
require_binaries mkfs.ext4 || return 1
return 0
}
depends() {
return 0
}
installkernel() {
hostonly='' instmods overlay 2>/dev/null || true
hostonly='' instmods dm-crypt 2>/dev/null || true
}
install() {
inst_multiple cryptsetup losetup mkfs.ext4 dd modprobe mount umount shred
inst_hook pre-pivot 10 "$moddir/overlay-crypt.sh"
}
EOF
sudo tee /usr/lib/dracut/modules.d/90overlayfs-crypt/overlay-mount.sh<< 'EOF'
#!/bin/bash
. /lib/dracut-lib.sh
if ! getargbool 0 cryptovl ; then
return
fi
modprobe overlay 2>/dev/null || true
modprobe dm-crypt 2>/dev/null || true
mount -o remount,ro /sysroot 2>/dev/null || true
mkdir -p /live/image
mount --bind /sysroot /live/image
umount /sysroot
dd if=/dev/urandom bs=64 count=1 of=/dev/shm/overlay-key status=none
chmod 600 /dev/shm/overlay-key
mkdir -p /var/lib
dd if=/dev/zero of=/var/lib/overlay-crypt.img bs=1M count=0 seek=5120 status=none
losetup -f
LOOP_DEV=$(losetup -f --show /var/lib/overlay-crypt.img)
cryptsetup luksFormat --type luks2 \
--cipher aes-xts-plain64 --key-size 512 \
--hash sha256 --pbkdf pbkdf2 --pbkdf-force-iterations 1000 \
--batch-mode --key-file /dev/shm/overlay-key "$LOOP_DEV"
cryptsetup open --type luks2 --key-file /dev/shm/overlay-key "$LOOP_DEV" overlaycrypt
mkfs.ext4 -F -L "overlaycrypt" /dev/mapper/overlaycrypt
mkdir -p /cow
mount -o noatime,nodiratime,nobarrier /dev/mapper/overlaycrypt /cow
mkdir -p /cow/work /cow/rw
mount -t overlay -o noatime,nodiratime,volatile,lowerdir=/live/image,upperdir=/cow/rw,workdir=/cow/work,default_permissions,relatime overlay /sysroot
mkdir -p /sysroot/live/cow /sysroot/live/image
mount --bind /cow/rw /sysroot/live/cow
mount --bind /live/image /sysroot/live/image
umount /cow 2>/dev/null || true
umount /live/image 2>/dev/null || true
shred -u /dev/shm/overlay-key 2>/dev/null || rm -f /dev/shm/overlay-key
EOF
Scripts to mount individual directories in overlayfs.
All examples create an overlayfs for /home.
1. Overlayfs on tmpfs (2GB):
#!/bin/bash
OVERLAY_BASE="/run/home-overlay"
LOWERDIR="/home"
UPPERDIR="$OVERLAY_BASE/upper"
WORKDIR="$OVERLAY_BASE/work"
MOUNTPOINT="/home"
# Create directories
mkdir -p "$UPPERDIR" "$WORKDIR"
# Mount tmpfs for overlay upper+work layers
mount -t tmpfs -o size=2G,mode=0755 tmpfs "$OVERLAY_BASE"
# Recreate upper/work inside tmpfs after mount
mkdir -p "$UPPERDIR" "$WORKDIR"
# Mount overlayfs
mount -t overlay overlay \
-o lowerdir="$LOWERDIR",upperdir="$UPPERDIR",workdir="$WORKDIR" \
"$MOUNTPOINT"
2. Overlayfs on zram block device (2GB):
#!/bin/bash
ZRAM_DEV=""
ZRAM_SIZE="2G"
LOWERDIR="/home"
OVERLAY_BASE="/run/home-overlay"
UPPERDIR="$OVERLAY_BASE/upper"
WORKDIR="$OVERLAY_BASE/work"
MOUNTPOINT="/home"
# Load zram module if not loaded
if ! lsmod | grep -q "^zram"; then
modprobe zram num_devices=1 || modprobe zram
fi
# Wait for zram control interface
for _ in {1..10}; do
if [ -d /sys/class/zram-control ]; then
break
fi
sleep 0.1
done
# Find first free zram device
for i in /sys/block/zram*; do
[ -e "$i" ] || continue
if [ "$(cat "$i"/disksize)" = "0" ]; then
ZRAM_DEV="/dev/$(basename "$i")"
break
fi
done
if [ -z "$ZRAM_DEV" ]; then
# No free device; try to add one via hot_add
if [ -f /sys/class/zram-control/hot_add ]; then
idx=$(cat /sys/class/zram-control/hot_add)
ZRAM_DEV="/dev/zram$idx"
else
echo "ERROR: no free zram device found and hot_add not available" >&2
exit 1
fi
fi
echo "Using $ZRAM_DEV"
# Configure compression and size
echo lz4 > /sys/block/$(basename "$ZRAM_DEV")/comp_algorithm 2>/dev/null || true
echo "$ZRAM_SIZE" > /sys/block/$(basename "$ZRAM_DEV")/disksize
# Format and mount zram as ext4 (needed for overlay upper+work)
mkfs.ext4 -q "$ZRAM_DEV"
mkdir -p "$OVERLAY_BASE"
mount -t ext4 "$ZRAM_DEV" "$OVERLAY_BASE"
# Prepare overlay directories
mkdir -p "$UPPERDIR" "$WORKDIR"
# Mount overlayfs
mount -t overlay overlay \
-o lowerdir="$LOWERDIR",upperdir="$UPPERDIR",workdir="$WORKDIR" \
"$MOUNTPOINT"
3. Overlayfs on plain dm-crypt (2GB):
#!/bin/bash
OVERLAY_BASE="/run/home-overlay"
CRYPT_NAME="home-overlay-crypt"
LOWERDIR="/home"
MOUNTPOINT="/home"
SIZE="2G"
CIPHER="aes-xts-plain64"
KEY_SIZE=512
# Check if already mounted
if findmnt -n -o FSTYPE "$MOUNTPOINT" 2>/dev/null | grep -q overlay; then
echo "Overlay already active on $MOUNTPOINT"
exit 0
fi
# Require root privileges
if [[ $EUID -ne 0 ]]; then
echo "Need root" >&2
exit 1
fi
# Create tmpfs backing store for container file
mkdir -p "$OVERLAY_BASE"
mount -t tmpfs -o size=1100M,mode=0700,noexec,nodev,nosuid,noatime tmpfs "$OVERLAY_BASE"
# Create 2G sparse file
truncate -s "$SIZE" "$OVERLAY_BASE/container.img"
# Set up loop device
LOOP_DEV=$(losetup --find --show --sector-size 512 "$OVERLAY_BASE/container.img")
# Ephemeral key in memory only
KEYFILE=$(mktemp -p /dev/shm .key.XXXXXX)
dd if=/dev/urandom of="$KEYFILE" bs=64 count=1 status=none
chmod 600 "$KEYFILE"
# Open plain dm-crypt (no LUKS header)
cryptsetup open --type plain \
--cipher "$CIPHER" \
--key-size "$KEY_SIZE" \
--key-file "$KEYFILE" \
"$LOOP_DEV" "$CRYPT_NAME"
# Key no longer needed - destroy it
shred -u "$KEYFILE" 2>/dev/null || rm -f "$KEYFILE"
unset KEYFILE
# Filesystem on encrypted device
mkfs.ext4 -F -q -m 0 -O ^has_journal /dev/mapper/"$CRYPT_NAME"
# Mount encrypted volume ON TOP OF tmpfs
# (container.img remains alive because loop device holds it)
mount -o noatime,nodiratime,noexec,nodev,nosuid /dev/mapper/"$CRYPT_NAME" "$OVERLAY_BASE"
# Directories for overlay
mkdir -p "$OVERLAY_BASE/upper" "$OVERLAY_BASE/work"
# Final overlay mount
mount -t overlay overlay \
-o lowerdir="$LOWERDIR",upperdir="$OVERLAY_BASE/upper",workdir="$OVERLAY_BASE/work",noatime,nodiratime \
"$MOUNTPOINT"
License:Unlicense
Anyone may use and redistribute these scripts and modules under their own name.
