Good evening everyone, after 5–6 hours of research and discussions with Claude, I finally managed to piece everything together.
Below is the finalized manual edited by Claude.
The goal was to have my sdb1 attached to the VM with the memory configured as a parameter, because I use DispVMs to transfer files via SSH. But depending on the file size, I couldn’t transfer them from one VM to another due to lack of space. So I “found” a solution that works for me while keeping the partition encrypted — therefore inaccessible and invisible at first glance.
Thank you.
You're gonna create an offline VM with 900 GB of private storage on your /dev/sdb1 partition (encrypted). The disk will be unlocked manually when you need it, then locked back up after you're done.
Why this rocks:
bash
lsblk -f | grep sdb
You should see sdb1 with ~900G
bash
sudo dnf list installed lvm2 cryptsetup
If missing:
bash
sudo dnf install lvm2 cryptsetup
⚠️ WARNING: This step ERASES all data on /dev/sdb1
bash
# Unmount if mounted
sudo umount /dev/sdb1 2>/dev/null
# Wipe existing signatures
sudo wipefs -a /dev/sdb1
bash
sudo cryptsetup luksFormat /dev/sdb1
What's gonna happen:
YES in capitals💾 IMPORTANT: Save this passphrase somewhere safe!
bash
lsblk -f | grep sdb1
You should see crypto_LUKS as the filesystem type.
bash
sudo cryptsetup luksOpen /dev/sdb1 myencrypted
Breaking it down:
sudo cryptsetup luksOpen = FIXED COMMAND/dev/sdb1 = YOUR PARTITION (fixed)myencrypted = NAME you choose (keep this one)What it asks: Your LUKS passphrase
Verify it worked:
bash
lsblk
You should see /dev/mapper/myencrypted
bash
sudo pvcreate /dev/mapper/myencrypted
Breaking it down:
sudo pvcreate = FIXED COMMAND/dev/mapper/myencrypted = Uses the name from step 2Verify:
bash
sudo pvdisplay
You should see your PV with ~900G
bash
sudo vgcreate myvg /dev/mapper/myencrypted
Breaking it down:
sudo vgcreate = FIXED COMMANDmyvg = NAME you choose (keep this one)/dev/mapper/myencrypted = Reuses the name from step 2Check available size:
bash
sudo vgdisplay myvg
🔍 NOTE the "VG Size" (example: 899.50 GiB)
Use 100% of available space:
bash
sudo lvcreate -l 100%FREE -T myvg/mypool
Breaking it down:
sudo lvcreate = FIXED COMMAND-l 100%FREE = Uses all free space-T = Creates a thin pool (for snapshots)myvg/mypool = VG/pool nameVerify:
bash
sudo lvs -a myvg
You should see mypool with type twi-a-tz--
⚠️ CORRECT SYNTAX (spaces between each -o - this is important!):
bash
qvm-pool add mypool lvm_thin -o volume_group=myvg -o thin_pool=mypool -o revisions_to_keep=3
Breaking it down:
qvm-pool add mypool = Pool name in Qubeslvm_thin = Pool type-o volume_group=myvg = Your VG (step 3.2)-o thin_pool=mypool = Your pool (step 3.3)-o revisions_to_keep=3 = Keeps 3 snapshotsVerify:
bash
qvm-pool list
You should see mypool with driver lvm_thin
bash
qvm-ls --template
Note a template (examples: debian-12, fedora-40, debian-11)
⚠️ Replace debian-12 with YOUR template!
bash
qvm-create --pool private=mypool --label red --template debian-12 --standalone --property netvm=none my-offline-vm
Breaking it down:
qvm-create = FIXED COMMAND--pool private=mypool = Uses your pool--label red = Color (changeable: blue, green, orange...)--template debian-12 = YOUR TEMPLATE (change it!)--standalone = Independent VM--property netvm=none = No network (offline)my-offline-vm = VM name (changeable)bash
qvm-volume extend my-offline-vm:private 850G
Why 850G? Leaves 50G margin for snapshots.
Verify:
bash
qvm-volume info my-offline-vm:private
You should see size: 850 GiB
bash
qvm-start my-offline-vm
Wait 20-30 seconds (first-time initialization).
Method 1 - Via Qube Manager:
my-offline-vm (red)Method 2 - From dom0:
bash
qvm-run my-offline-vm xterm
bash
df -h /home
✅ You should see ~850G available!
bash
echo "Secure storage test" > ~/test.txt
cat ~/test.txt
🎉 If it works: CONGRATS, everything is operational!
1. Unlock the disk (in dom0):
bash
sudo cryptsetup luksOpen /dev/sdb1 myencrypted
sudo vgchange -ay myvg
2. Start the VM:
bash
qvm-start my-offline-vm
3. Use normally via Qube Manager or terminal
1. Stop the VM:
bash
qvm-shutdown my-offline-vm
Wait until it's completely stopped (qvm-ls no longer shows it in green)
2. Deactivate the VG:
bash
sudo vgchange -an myvg
3. Lock the disk:
bash
sudo cryptsetup luksClose myencrypted
✅ Secured: The disk is now completely inaccessible
~/start-offline-vm.shbash
#!/bin/bash
echo "🔓 Unlocking disk..."
sudo cryptsetup luksOpen /dev/sdb1 myencrypted || exit 1
sudo vgchange -ay myvg || exit 1
echo "🚀 Starting VM..."
qvm-start my-offline-vm
echo "✅ VM ready!"
~/stop-offline-vm.shbash
#!/bin/bash
echo "🛑 Stopping VM..."
qvm-shutdown --wait my-offline-vm
echo "🔒 Locking disk..."
sudo vgchange -an myvg
sudo cryptsetup luksClose myencrypted
echo "✅ Disk secured!"
Make them executable:
bash
chmod +x ~/start-offline-vm.sh ~/stop-offline-vm.sh
Use them:
bash
~/start-offline-vm.sh
# ... your work ...
~/stop-offline-vm.sh
From a networked VM to my-offline-vm:
bash
# In the source VM
qvm-copy /path/to/file
Select my-offline-vm in the window
In my-offline-vm, files arrive in:
~/QubesIncoming/source-vm-name/
bash
# Check if disk is unlocked
lsblk | grep myencrypted
# Check if VG is active
sudo vgs
# Check pool space usage
qvm-pool info mypool
# Check VM space
qvm-volume info my-offline-vm:private
# VM status
qvm-ls | grep my-offline-vm
Solution:
bash
# Check VG is active
sudo vgs
# If "inactive":
sudo vgchange -ay myvg
# Restart qubesd
sudo systemctl restart qubesd
# Retry with SPACES between each -o:
qvm-pool add mypool lvm_thin -o volume_group=myvg -o thin_pool=mypool -o revisions_to_keep=3
bash
sudo vgchange -an myvg --force
sudo cryptsetup luksClose myencrypted
Cause: Disk not unlocked
bash
sudo cryptsetup luksOpen /dev/sdb1 myencrypted
sudo vgchange -ay myvg
qvm-start my-offline-vm
Unfortunately: LUKS without passphrase = data lost forever Prevention: Save the passphrase in a secure password manager
bash
# 1. Stop the VM
qvm-shutdown my-offline-vm
# 2. Remove the VM
qvm-remove my-offline-vm
# 3. Remove the pool
qvm-pool remove mypool
# 4. Destroy LVM structure
sudo lvremove myvg/mypool
sudo vgremove myvg
sudo pvremove /dev/mapper/myencrypted
# 5. Close LUKS
sudo cryptsetup luksClose myencrypted
# 6. (Optional) Completely erase sdb1
sudo cryptsetup luksErase /dev/sdb1 # WARNING: Permanent loss!
sudo wipefs -a /dev/sdb1
bash
sudo cryptsetup luksChangeKey /dev/sdb1
Enter old passphrase, then new one (twice).