Guide: Offline VM with Encrypted Disk on Qubes OS - V4 Setup: /dev/sdb1 - Internal Drive - Manual Unlock

Original forum link
https://forum.qubes-os.org/t/37363
Original poster
User.LinuxOne
Created at
2025-11-15 22:21:27
Posts count
9
Likes count
2
Tags
configuration

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.

What We're Building

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:


⚙️ PREREQUISITES

Check your partition in dom0

bash

lsblk -f | grep sdb

You should see sdb1 with ~900G

Check if you have the tools

bash

sudo dnf list installed lvm2 cryptsetup

If missing:

bash

sudo dnf install lvm2 cryptsetup

🔐 STEP 1: Encrypt the Disk

⚠️ WARNING: This step ERASES all data on /dev/sdb1

1.1 Clean up the partition

bash

# Unmount if mounted
sudo umount /dev/sdb1 2>/dev/null

# Wipe existing signatures
sudo wipefs -a /dev/sdb1

1.2 Encrypt with LUKS

bash

sudo cryptsetup luksFormat /dev/sdb1

What's gonna happen:

💾 IMPORTANT: Save this passphrase somewhere safe!

1.3 Verify encryption

bash

lsblk -f | grep sdb1

You should see crypto_LUKS as the filesystem type.


🔓 STEP 2: Unlock the Disk

bash

sudo cryptsetup luksOpen /dev/sdb1 myencrypted

Breaking it down:

What it asks: Your LUKS passphrase

Verify it worked:

bash

lsblk

You should see /dev/mapper/myencrypted


📦 STEP 3: Create the LVM Structure

3.1 Create the Physical Volume

bash

sudo pvcreate /dev/mapper/myencrypted

Breaking it down:

Verify:

bash

sudo pvdisplay

You should see your PV with ~900G


3.2 Create the Volume Group

bash

sudo vgcreate myvg /dev/mapper/myencrypted

Breaking it down:

Check available size:

bash

sudo vgdisplay myvg

🔍 NOTE the "VG Size" (example: 899.50 GiB)


3.3 Create the Thin Pool

Use 100% of available space:

bash

sudo lvcreate -l 100%FREE -T myvg/mypool

Breaking it down:

Verify:

bash

sudo lvs -a myvg

You should see mypool with type twi-a-tz--


🖥️ STEP 4: Connect to Qubes

4.1 Add the pool to Qubes

⚠️ 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:

Verify:

bash

qvm-pool list

You should see mypool with driver lvm_thin


🚀 STEP 5: Create the Offline VM

5.1 List your available templates

bash

qvm-ls --template

Note a template (examples: debian-12, fedora-40, debian-11)


5.2 Create the VM

⚠️ 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:


5.3 Extend the private volume

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


✅ STEP 6: Test the VM

6.1 Start the VM

bash

qvm-start my-offline-vm

Wait 20-30 seconds (first-time initialization).

6.2 Open a terminal in the VM

Method 1 - Via Qube Manager:

Method 2 - From dom0:

bash

qvm-run my-offline-vm xterm

6.3 Check available space (INSIDE the VM)

bash

df -h /home

✅ You should see ~850G available!

6.4 Test writing

bash

echo "Secure storage test" > ~/test.txt
cat ~/test.txt

🎉 If it works: CONGRATS, everything is operational!


📝 DAILY USE: How to Use It

✅ START AND USE THE VM

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


🛑 STOP AND LOCK THE DISK

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


🔄 Automation Scripts (Optional)

Startup script: ~/start-offline-vm.sh

bash

#!/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!"

Shutdown script: ~/stop-offline-vm.sh

bash

#!/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

📤 File Transfer

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/

🔧 Verification Commands

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

⚠️ Troubleshooting

❌ "Got empty response from qubesd"

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

❌ "Device or resource busy" during luksClose

bash

sudo vgchange -an myvg --force
sudo cryptsetup luksClose myencrypted

❌ VM won't start ("volume missing")

Cause: Disk not unlocked

bash

sudo cryptsetup luksOpen /dev/sdb1 myencrypted
sudo vgchange -ay myvg
qvm-start my-offline-vm

❌ Forgot LUKS passphrase

Unfortunately: LUKS without passphrase = data lost forever Prevention: Save the passphrase in a secure password manager


🗑️ Complete Removal

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

🔐 Change LUKS Passphrase

bash

sudo cryptsetup luksChangeKey /dev/sdb1

Enter old passphrase, then new one (twice).