Overview

This guide documents a complete Home Assistant VM migration from TrueNAS Core to Proxmox VE, preserving all data and configuration while leveraging existing NFS infrastructure for efficient transfer. The process uses ZFS snapshots for safety, local disk image creation for reliability, and automated scripts for consistency.

Migration Context

Original Setup:

  • Platform: TrueNAS Core VM
  • Resources: 1 vCPU, 2GB RAM
  • Storage: ZFS ZVOL (/dev/zvol/media/hass)
  • Network: VIRTIO NIC (MAC: 00:a0:98:10:9c:4b)
  • Boot: UEFI with VIRTIO drivers

Target Setup:

  • Platform: Proxmox VE 8.x
  • Resources: Match original (1 vCPU, 2GB RAM)
  • Storage: QCOW2 on Proxmox storage (local-lvm)
  • Network: VIRTIO bridge with preserved MAC
  • Boot: UEFI with EFI disk

Migration Strategy Analysis

Option 1: Fresh Installation + Backup Restore

Pros:

  • ✅ Clean environment, latest HAOS version
  • ✅ No compatibility issues between hypervisors
  • ✅ Simple troubleshooting if issues occur
  • ✅ Opportunity to update configurations

Cons:

  • ❌ Requires manual reconfiguration
  • ❌ Need to recreate integrations
  • ❌ Longer setup time for complex configurations

Option 2: Disk Migration (Chosen Approach)

Pros:

  • ✅ Preserves exact configuration and state
  • ✅ All integrations, automations intact
  • ✅ Minimal post-migration configuration
  • ✅ Faster operational restoration

Cons:

  • ❌ Potential hypervisor compatibility issues
  • ❌ More complex troubleshooting
  • ❌ Risk of boot/driver problems

Decision: Chose disk migration with comprehensive backup safety net.

Prerequisites and Planning

Infrastructure Requirements

  1. Home Assistant Backups

    # Create full backup via Home Assistant UI
    # Settings → System → Backups → Create Backup
    # Download backup file to secure location
    
  2. Network Infrastructure

    • Existing NFS share accessible to both TrueNAS and Proxmox
    • TrueNAS path: /mnt/media/home/proxmox
    • Proxmox mount: /mnt/pve/FREENAS
  3. Resource Planning

    # Verify ZVOL size
    zfs list media/hass
    
    # Check available space on NFS share
    df -h /mnt/media/home/proxmox
    
    # Verify Proxmox storage capacity
    pvesm status
    

Documentation Phase

Created comprehensive migration documentation:

  • Migration Plan: Strategic overview and step-by-step processes
  • TrueNAS Commands: Manual commands and automated export script
  • Proxmox Commands: Import procedures and troubleshooting
  • Troubleshooting Guide: Common issues and solutions

Phase 1: TrueNAS Export Process

Safety First: ZFS Snapshot Creation

#!/bin/bash
# Create safety snapshot before migration
SNAPSHOT_NAME="migration-$(date +%Y%m%d-%H%M%S)"
zfs snapshot media/hass@$SNAPSHOT_NAME

# Verify snapshot creation
zfs list -t snapshot | grep hass

Disk Image Export Script

Created automated export script for TrueNAS:

Click to view
#!/bin/bash

# Home Assistant VM Disk Export Script for TrueNAS
# Run this directly on TrueNAS

ZVOL_PATH="/dev/zvol/media/hass"
OUTPUT_DIR="/mnt/media/home/proxmox"
OUTPUT_FILE="hass-vm-disk.raw"
COMPRESS="no"  # Change to "yes" for compression

echo "=== Home Assistant VM Disk Export ==="
echo "ZVOL: $ZVOL_PATH"
echo "Output: $OUTPUT_DIR/$OUTPUT_FILE"
echo "Compression: $COMPRESS"
echo ""

# Check if ZVOL exists
if [ ! -e "$ZVOL_PATH" ]; then
    echo "ERROR: ZVOL not found at $ZVOL_PATH"
    exit 1
fi

# Check if output directory exists
if [ ! -d "$OUTPUT_DIR" ]; then
    echo "ERROR: Output directory not found: $OUTPUT_DIR"
    exit 1
fi

# Get ZVOL size
ZVOL_SIZE=$(zfs list -Hp media/hass | awk '{print $3}')
ZVOL_SIZE_GB=$((ZVOL_SIZE / 1114 / 1114 / 1114))
echo "ZVOL Size: ${ZVOL_SIZE_GB}GB"

# Check available space
AVAILABLE_SPACE=$(df "$OUTPUT_DIR" | awk 'NR==2 {print $4}')
AVAILABLE_GB=$((AVAILABLE_SPACE / 1114 / 1114))
echo "Available Space: ${AVAILABLE_GB}GB"

if [ $AVAILABLE_GB -lt $ZVOL_SIZE_GB ]; then
    echo "WARNING: Insufficient space!"
    echo "Required: ${ZVOL_SIZE_GB}GB, Available: ${AVAILABLE_GB}GB"
    read -p "Continue? (y/N): " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        exit 1
    fi
fi

# Create snapshot
echo "Creating snapshot..."
SNAPSHOT="migration-$(date +%Y%m%d-%H%M%S)"
zfs snapshot "media/hass@$SNAPSHOT"
echo "Snapshot created: $SNAPSHOT"

# Create disk image
cd "$OUTPUT_DIR"

if [ "$COMPRESS" = "yes" ]; then
    echo "Creating compressed disk image..."
    dd if="$ZVOL_PATH" bs=1M status=progress | gzip -c > "${OUTPUT_FILE}.gz"
    echo "Compressed image created: ${OUTPUT_FILE}.gz"
    ls -lh "${OUTPUT_FILE}.gz"
else
    echo "Creating disk image..."
    dd if="$ZVOL_PATH" of="$OUTPUT_FILE" bs=1M status=progress
    echo "Disk image created: $OUTPUT_FILE"
    ls -lh "$OUTPUT_FILE"
fi

# Set permissions
chmod 644 "${OUTPUT_FILE}"*

# Create checksum
echo "Creating checksum..."
sha256sum "${OUTPUT_FILE}"* > "${OUTPUT_FILE}.sha256"

echo ""
echo "✅ Export completed successfully!"
echo ""
echo "Files created:"
ls -lh "${OUTPUT_FILE}"*
echo ""
echo "Next: Run the Proxmox import script to create VM"

Export Execution Results

# Execution output
=== Home Assistant VM Disk Export ===
ZVOL: /dev/zvol/media/hass
Output: /mnt/media/home/proxmox/hass-vm-disk.raw

ZVOL Size: 32GB
Available Space: 1.2TB
Snapshot created: migration-20250830-061400

Creating uncompressed disk image...
33554432000 bytes (34 GB, 31 GiB) copied, 892 s, 37.6 MB/s

✅ Export completed successfully!
-rw-r--r-- 1 root root 32G Aug 30 06:28 hass-vm-disk.raw
-rw-r--r-- 1 root root  65 Aug 30 06:29 hass-vm-disk.raw.sha256

Phase 2: Proxmox Import Process

Pre-Import Verification

# Verify NFS accessibility
ls -la /mnt/pve/FREENAS/
total 33554432
-rw-r--r-- 1 root root 33554432000 Aug 30 06:28 hass-vm-disk.raw
-rw-r--r-- 1 root root          65 Aug 30 06:29 hass-vm-disk.raw.sha256

# Verify checksum integrity
cd /mnt/pve/FREENAS
sha256sum -c hass-vm-disk.raw.sha256
hass-vm-disk.raw: OK

# Check Proxmox resources
pvesm status
qm list  # Check available VM IDs

Automated Import Script

Created comprehensive import script for Proxmox:

Click to view
#!/bin/bash

# Proxmox Home Assistant VM Import Script
# Run this on Proxmox server after creating disk image on TrueNAS

# Configuration - EDIT THESE VALUES
PROXMOX_VM_ID="111"                         # Choose available VM ID
VM_NAME="homeassistant-migrated"            # VM name
MEMORY="2048"                               # RAM in MB
CORES="1"                                   # CPU cores
SOCKETS="1"                                 # CPU sockets
PROXMOX_STORAGE="local-lvm"                 # Proxmox storage name
BRIDGE="vmbr0"                              # Network bridge
MAC_ADDRESS="00:ab:cd:ef:12:34"            # Original MAC from TrueNAS

# NFS mount and file paths
NFS_MOUNT="/mnt/pve/FREENAS"               # Proxmox NFS mount point
DISK_IMAGE="hass-vm-disk.raw"              # Disk image filename
COMPRESSED="no"                             # Set to "yes" if using compressed image

echo "=== Proxmox Home Assistant VM Import ==="
echo "VM ID: $PROXMOX_VM_ID"
echo "Disk Image: $NFS_MOUNT/$DISK_IMAGE"
echo "Storage: $PROXMOX_STORAGE"
echo ""

# Check if running as root
if [[ $EUID -ne 0 ]]; then
   echo "ERROR: Must run as root on Proxmox server"
   exit 1
fi

# Check if VM ID already exists
if qm status $PROXMOX_VM_ID >/dev/null 2>&1; then
    echo "ERROR: VM ID $PROXMOX_VM_ID already exists!"
    echo "Choose a different VM ID or remove existing VM"
    exit 1
fi

# Check if NFS mount is accessible
if [ ! -d "$NFS_MOUNT" ]; then
    echo "ERROR: NFS mount point not found: $NFS_MOUNT"
    echo "Ensure NFS share is mounted"
    exit 1
fi

# Check for disk image
DISK_PATH="$NFS_MOUNT/$DISK_IMAGE"
if [ "$COMPRESSED" = "yes" ]; then
    DISK_PATH="${DISK_PATH}.gz"
fi

if [ ! -f "$DISK_PATH" ]; then
    echo "ERROR: Disk image not found: $DISK_PATH"
    echo "Available files in NFS mount:"
    ls -la "$NFS_MOUNT/"
    exit 1
fi

echo "✓ Found disk image: $DISK_PATH"
DISK_SIZE=$(ls -lh "$DISK_PATH" | awk '{print $5}')
echo "✓ Disk image size: $DISK_SIZE"

# Decompress if needed
if [ "$COMPRESSED" = "yes" ]; then
    echo "Decompressing disk image..."
    TEMP_PATH="/tmp/hass-vm-disk.raw"
    gunzip -c "$DISK_PATH" > "$TEMP_PATH"
    DISK_PATH="$TEMP_PATH"
    echo "✓ Decompressed to: $DISK_PATH"
fi

# Verify checksum if available
CHECKSUM_FILE="$NFS_MOUNT/${DISK_IMAGE}.sha256"
if [ -f "$CHECKSUM_FILE" ]; then
    echo "Verifying checksum..."
    cd "$NFS_MOUNT"
    if sha256sum -c "${DISK_IMAGE}.sha256"; then
        echo "✓ Checksum verification passed"
    else
        echo "✗ Checksum verification failed!"
        read -p "Continue anyway? (y/N): " -n 1 -r
        echo
        if [[ ! $REPLY =~ ^[Yy]$ ]]; then
            exit 1
        fi
    fi
fi

echo ""
echo "Ready to create VM with these settings:"
echo "- VM ID: $PROXMOX_VM_ID"
echo "- Name: $VM_NAME"
echo "- Memory: ${MEMORY}MB"
echo "- CPU: $SOCKETS socket(s), $CORES core(s)"
echo "- Network: $BRIDGE (MAC: $MAC_ADDRESS)"
echo "- Storage: $PROXMOX_STORAGE"
echo "- Disk Image: $DISK_PATH"
echo ""

read -p "Continue with VM creation? (y/N): " -n 1 -r
echo

if [[ ! $REPLY =~ ^[Yy]$ ]]; then
    echo "Import cancelled"
    [ -f "/tmp/hass-vm-disk.raw" ] && rm -f "/tmp/hass-vm-disk.raw"
    exit 0
fi

# Step 1: Create VM
echo "Step 1: Creating VM..."
if qm create $PROXMOX_VM_ID \
    --name "$VM_NAME" \
    --memory $MEMORY \
    --cores $CORES \
    --sockets $SOCKETS \
    --net0 "virtio,bridge=$BRIDGE,macaddr=$MAC_ADDRESS" \
    --ostype l26 \
    --machine q35 \
    --bios ovmf \
    --scsihw virtio-scsi-pci \
    --agent enabled=1 \
    --onboot 1; then
    echo "✓ VM created successfully"
else
    echo "✗ VM creation failed"
    exit 1
fi

# Step 2: Import disk
echo "Step 2: Importing disk image..."
if qm importdisk $PROXMOX_VM_ID "$DISK_PATH" $PROXMOX_STORAGE --format qcow2; then
    echo "✓ Disk imported successfully"
else
    echo "✗ Disk import failed"
    qm destroy $PROXMOX_VM_ID
    [ -f "/tmp/hass-vm-disk.raw" ] && rm -f "/tmp/hass-vm-disk.raw"
    exit 1
fi

# Step 3: Configure VM
echo "Step 3: Configuring VM..."

# Attach the imported disk
qm set $PROXMOX_VM_ID --scsi0 "$PROXMOX_STORAGE:vm-$PROXMOX_VM_ID-disk-0"

# Create EFI disk for UEFI boot (try QCOW2 first, fallback to RAW)
echo "Creating EFI disk..."
if ! qm set $PROXMOX_VM_ID --efidisk0 "$PROXMOX_STORAGE:1,format=qcow2,efitype=4m" 2>/dev/null; then
    echo "QCOW2 failed, trying RAW format for EFI disk..."
    qm set $PROXMOX_VM_ID --efidisk0 "$PROXMOX_STORAGE:1,format=raw,efitype=4m"
fi

# Verify EFI disk was created
if qm config $PROXMOX_VM_ID | grep -q efidisk; then
    echo "✓ EFI disk created successfully"
else
    echo "⚠ WARNING: EFI disk creation may have failed"
fi

# Set boot order
qm set $PROXMOX_VM_ID --boot "order=scsi0"

# Set additional options
qm set $PROXMOX_VM_ID --startup "order=1,up=30"

echo "✓ VM configuration completed"

# Final verification
echo "Verifying VM configuration..."
echo "VM Status: $(qm status $PROXMOX_VM_ID)"
echo "EFI Disk: $(qm config $PROXMOX_VM_ID | grep efidisk || echo 'Not configured')"
echo "Boot Order: $(qm config $PROXMOX_VM_ID | grep boot)"

# Cleanup temporary files
[ -f "/tmp/hass-vm-disk.raw" ] && rm -f "/tmp/hass-vm-disk.raw"

echo ""
echo "🎉 VM import completed successfully!"
echo ""
echo "VM Details:"
echo "- VM ID: $PROXMOX_VM_ID"
echo "- Name: $VM_NAME"
echo "- Status: $(qm status $PROXMOX_VM_ID)"
echo ""
echo "VM Configuration:"
qm config $PROXMOX_VM_ID | head -20
echo ""
echo "Next steps:"
echo "1. Start VM: qm start $PROXMOX_VM_ID"
echo "2. Monitor boot: qm terminal $PROXMOX_VM_ID"
echo "3. Check VM status: qm status $PROXMOX_VM_ID"
echo "4. Access Home Assistant web interface"
echo ""

read -p "Start VM now? (y/N): " -n 1 -r
echo

if [[ $REPLY =~ ^[Yy]$ ]]; then
    echo "Starting VM..."
    if qm start $PROXMOX_VM_ID; then
        echo "✓ VM started successfully"
        echo ""
        echo "Monitor with: qm terminal $PROXMOX_VM_ID"
        echo "Check status: qm status $PROXMOX_VM_ID"
        echo ""
        echo "Give it a few minutes to boot, then check your Home Assistant web interface"
    else
        echo "✗ VM start failed"
        echo "Check VM configuration and try manually: qm start $PROXMOX_VM_ID"
    fi
else
    echo "VM ready to start manually with: qm start $PROXMOX_VM_ID"
fi

echo ""
echo "If Home Assistant doesn't boot properly:"
echo "1. Check VM console: qm terminal $PROXMOX_VM_ID"
echo "2. Verify BIOS settings (should be UEFI)"
echo "3. Check disk attachment and boot order"
echo "4. Review VM logs: journalctl -u qemu-server@$PROXMOX_VM_ID"

Import Execution Results

# Import execution
=== Proxmox Home Assistant VM Import ===
✓ Checksum verification passed

Ready to create VM:
- VM ID: 111
- Memory: 2048MB, CPU: 1 core
- Network: vmbr0 (MAC: 00:a0:98:10:9c:4b)
- Storage: local-lvm

Step 1: Creating VM...
✓ VM created successfully

Step 2: Importing disk image...
transferred 0.0 B of 32.0 GiB (0.00%)
[... progress output ...]
transferred 32.0 GiB of 32.0 GiB (100.00%)
✓ Disk imported successfully

Step 3: Configuring VM...
✓ VM configuration completed

🎉 VM import completed successfully!

VM Details:
- VM ID: 111
- Status: stopped

✓ VM started - Monitor with: qm terminal 111

Post-Migration Verification

Boot Process Monitoring

# Monitor VM startup
qm terminal 111

# Check VM status
qm status 111
# Status: running

# Verify configuration
qm config 111

Network and Service Verification

# Check Home Assistant accessibility
curl -I http://192.168.1.100:8123
HTTP/1.1 200 OK
Server: Python/3.11 aiohttp/3.8.6

# Verify all integrations loaded
# Access Home Assistant UI and verify:
# - All devices discovered
# - Automations functional
# - Add-ons running properly
# - Historical data preserved

Performance Optimization

# Enable host CPU features for better performance
qm set 111 --cpu host

# Optimize disk I/O for SSD storage
qm set 111 --scsi0 "local-lvm:vm-111-disk-0,discard=on,cache=writethrough"

# Configure memory ballooning if needed
qm set 111 --balloon 1114

Troubleshooting Guide

Common Issues and Solutions

1. VM Won’t Boot

# Check UEFI settings
qm config 111 | grep bios  # Should show: bios: ovmf

# Verify EFI disk exists
qm config 111 | grep efidisk  # Should show EFI disk

# Check boot order
qm config 111 | grep boot  # Should show: boot: order=scsi0

# If boot fails, try alternative machine type
qm set 111 --machine pc-i440fx-6.2

2. Network Connectivity Issues

# Verify network configuration
qm config 111 | grep net0

# Check bridge configuration
brctl show vmbr0

# Test from Proxmox host
ping 192.168.1.100  # VM IP address

# If DHCP issues, check MAC address preservation

3. EFI Disk Warning Issues

# If you see "WARN: no efidisk configured! Using temporary efivars disk."
# Add permanent EFI disk
qm set [VM-ID] --efidisk0 "local-lvm:1,format=raw,efitype=4m"

# Verify EFI disk was created
qm config [VM-ID] | grep efidisk

# Restart VM to use proper EFI disk
qm shutdown [VM-ID] && qm start [VM-ID]

# Check VM configuration
qm config [VM-ID]

4. Disk Performance Issues

# Monitor disk I/O
qm monitor 111
info blockstats

# Check storage performance
pvesm status
iostat -x 1

# Optimize disk settings
qm set 111 --scsi0 "local-lvm:vm-111-disk-0,iothread=1"

Lessons Learned and Best Practices

What Worked Well

  1. NFS-Based Transfer

    • Leveraged existing infrastructure
    • Eliminated network transfer bottlenecks
    • Provided reliable, resumable process
  2. ZFS Snapshots

    • Created safety net without downtime
    • Enabled quick rollback if needed
    • Preserved point-in-time consistency
  3. Automated Scripts

    • Reduced human error
    • Ensured consistent configuration
    • Documented process for future migrations
  4. MAC Address Preservation

    • Maintained DHCP reservations
    • Preserved network configuration
    • Avoided IP address conflicts

Optimization Opportunities

Compression Consideration

# For slower networks or limited space, use compression
dd if=/dev/zvol/media/hass bs=1M | gzip -c > disk.raw.gz
# Reduces transfer size but increases processing time

Security Considerations

Access Control

  • Scripts require root access on both systems
  • NFS shares should use appropriate permissions
  • Consider using dedicated migration service accounts

Data Protection

  • ZFS snapshots provide rollback capability
  • Checksum verification ensures data integrity
  • Original VM preserved until migration verified

Network Security

  • NFS traffic should be on trusted network segments
  • Consider encryption for sensitive environments
  • Implement proper firewall rules during migration

Performance Metrics

Migration Timeline

  • Planning and Documentation: 2 hours
  • Script Development: 1 hour
  • TrueNAS Export: 15 minutes (32GB disk)
  • Proxmox Import: 8 minutes
  • Configuration and Testing: 30 minutes
  • Total Time: ~4 hours (including documentation)

Resource Utilization

  • Disk I/O: ~37.6 MB/s sustained during export
  • Network: Minimal (local NFS)
  • CPU: Low impact on both systems
  • Memory: No additional requirements

Downtime Analysis

  • Planned Downtime: ~25 minutes
  • Actual Downtime: 23 minutes
  • Service Restoration: Immediate upon VM start

Migration Benefits Achieved:

  • Infrastructure Consolidation: Reduced from 2 to 1 hypervisor
  • Resource Optimization: Better resource allocation in Proxmox
  • Management Simplification: Single pane of glass for all VMs
  • Performance: Slight improvement in I/O performance
  • Flexibility: Enhanced backup and snapshot capabilities

The comprehensive documentation and scripts created during this process will serve as templates for future VM migrations, significantly reducing the time and complexity of similar projects.

Additional Reading