Lima VM Testing#

Immutablue supports local VM testing using Lima, a lightweight VM manager that provides a seamless experience for running Linux VMs with automatic SSH, port forwarding, and file mounts.

Prerequisites#

  • Lima installed: brew install lima
  • QEMU installed: dnf install qemu-system-x86 (or via your package manager)
  • A built and pushed Immutablue container image

Quick Start#

# Build qcow2 with Lima SSH key support
make LIMA=1 qcow2

# Generate Lima configuration
make lima

# Start the VM
make lima-start

# Shell into the VM
make lima-shell

# Stop the VM when done
make lima-stop

Build Options#

Interactive Configuration#

Use qcow2-config to interactively configure your qcow2 image:

make qcow2-config

This prompts for:

  • Username (default: immutablue)
  • Password
  • Wheel group membership (sudo access)
  • SSH public key

For Lima builds, use LIMA=1 to auto-add your SSH key:

make LIMA=1 qcow2-config

Basic qcow2 Build#

Build a qcow2 without custom configuration (uses defaults):

make qcow2

Default credentials: immutablue / immutablue

If a config exists from qcow2-config, it will be used automatically.

Lima-enabled qcow2 Build#

Build a qcow2 with SSH keys for Lima access (without running qcow2-config):

make LIMA=1 qcow2

This automatically adds your SSH key to the image:

  1. Lima key (~/.lima/_config/user.pub) - preferred
  2. ED25519 key (~/.ssh/id_ed25519.pub) - fallback
  3. RSA key (~/.ssh/id_rsa.pub) - fallback

Building Variants#

All build options work with Lima:

# Trueblue variant with Lima support
make TRUEBLUE=1 LIMA=1 qcow2

# Kinoite (KDE) with LTS kernel
make KINOITE=1 LTS=1 LIMA=1 qcow2

# Kuberblue variant
make KUBERBLUE=1 LIMA=1 qcow2

Makefile Targets#

TargetDescription
make qcow2-configInteractive configuration for qcow2 (add LIMA=1 for auto SSH key)
make qcow2Build qcow2 image (uses config if exists, or add LIMA=1 for SSH key support)
make limaGenerate Lima YAML configuration
make lima-startStart the Lima VM
make lima-shellOpen a shell in the running VM
make lima-stopStop the Lima VM
make lima-deleteDelete the Lima VM instance

Lima Configuration#

The make lima target generates a Lima YAML configuration at .lima/immutablue-<TAG>.yaml with:

  • VM Type: QEMU with KVM acceleration
  • Resources: 4 CPUs, 4GB RAM, 100GB disk
  • User: immutablue
  • Mounts: Home directory (read-only)
  • SSH: Auto-assigned port with key-based auth
  • Mode: Plain mode (skips Lima provisioning for pre-built images)

Generated Configuration#

minimumLimaVersion: "2.0.0"
vmType: qemu
arch: x86_64

user:
  name: immutablue

images:
- location: "/path/to/images/qcow2/43/qcow2/disk.qcow2"
  arch: "x86_64"

cpus: 4
memory: "4GiB"
disk: "100GiB"

mounts:
- location: "~"
  writable: false

ssh:
  localPort: 0
  loadDotSSHPubKeys: true
  overVsock: false  # Required for Fedora 43 SELinux compatibility

containerd:
  system: false
  user: false

plain: true  # Skip Lima provisioning for pre-built images

Alternative: Raw QEMU#

If you prefer raw QEMU without Lima:

# Build qcow2
make qcow2

# Run with QEMU directly
make run_qcow2

This starts QEMU with:

  • SSH on port 2222
  • Serial console (exit with Ctrl-A X)
  • KVM acceleration

SSH access: ssh -p 2222 immutablue@localhost (password: immutablue)

Workflow Examples#

Testing a New Build#

# Build and push the image
make build push

# Option A: Quick build with LIMA=1 (auto SSH keys)
make LIMA=1 qcow2

# Option B: Interactive config first
make LIMA=1 qcow2-config
make qcow2

# Start Lima VM
make lima lima-start

# Shell in and test
make lima-shell

# Inside VM: verify the build
rpm-ostree status
cat /etc/immutablue-release

# Exit and stop
exit
make lima-stop

Testing Multiple Variants#

# Test default Silverblue
make LIMA=1 qcow2 lima lima-start
make lima-shell
# ... test ...
make lima-stop
make lima-delete

# Test Trueblue
make TRUEBLUE=1 LIMA=1 qcow2 lima lima-start
make TRUEBLUE=1 lima-shell
# ... test ...
make TRUEBLUE=1 lima-stop

Cleaning Up#

# Delete specific VM
make lima-delete

# Or with variant
make TRUEBLUE=1 lima-delete

# Clean all generated files
make clean

Troubleshooting#

SSH Connection Issues#

If Lima hangs on “Waiting for ssh”:

  1. Ensure you built with LIMA=1:

    make LIMA=1 qcow2
  2. Verify the SSH key was added:

    cat ./images/qcow2/43/config.toml | grep key
  3. Delete and recreate the VM:

    make lima-delete
    make lima lima-start

VM Won’t Start#

  1. Check if KVM is available:

    ls /dev/kvm
  2. Verify QEMU is installed:

    which qemu-system-x86_64
  3. Check Lima logs:

    cat ~/.lima/immutablue-43/serial*.log

User Session Issues#

If Lima hangs on “user session is ready”:

The plain: true setting should prevent this. If you still see it:

  1. Regenerate the Lima config:

    make lima
  2. Verify plain: true is in the YAML:

    grep plain .lima/immutablue-43.yaml

File Permission Issues#

After make qcow2, if files are owned by root:

sudo chown -R $(id -u):$(id -g) ./images/

This is normally handled automatically by the Makefile.

Architecture Notes#

Why Plain Mode?#

Immutablue qcow2 images are pre-built bootc images, not cloud images with cloud-init. Lima’s normal provisioning workflow expects to inject SSH keys and set up the user environment via cloud-init, which doesn’t apply to our images.

Setting plain: true tells Lima:

  • Skip user session requirement checks
  • Skip cloud-init provisioning
  • Skip containerd setup
  • Just boot the VM and provide SSH access

SSH Key Injection#

Since we can’t use cloud-init, SSH keys are baked into the qcow2 at build time via the bootc-image-builder config. There are two ways to inject SSH keys:

Option A: Interactive configuration

  1. Run make qcow2-config (or make LIMA=1 qcow2-config for auto SSH key)
  2. Config is saved to ./images/qcow2/<TAG>/config-<TAG>.toml
  3. Run make qcow2 to build using that config

Option B: LIMA=1 flag (quick method)

  1. Run make LIMA=1 qcow2 directly
  2. Makefile generates a temporary config.toml with your SSH public key
  3. bootc-image-builder creates the qcow2 with the immutablue user and your SSH key

In both cases, Lima can then SSH in using your private key.

Image Location#

qcow2 images are stored at:

./images/qcow2/<TAG>/qcow2/disk.qcow2

qcow2 configuration files (from qcow2-config) are stored at:

./images/qcow2/<TAG>/config-<TAG>.toml

Lima configurations are stored at:

./.lima/immutablue-<TAG>.yaml

Both directories are gitignored as they contain machine-specific paths and large binary files.