DEV Community

David Tio
David Tio

Posted on • Originally published at blog.dtio.app

Persistent VMs in Podman: Install Alpine to a qcow2 Disk Image

Persistent VMs in Podman: Install Alpine to a Disk Image

Quick one-liner: Create a qcow2 disk image with qemu-img, install Alpine Linux into it, and boot from disk — so your VM survives container restarts.


Why This Matters

Post #2 proved that KVM hardware acceleration is fast. But there's a catch: every time the container stops, the VM state vanishes. The Alpine ISO is read-only — any changes you make inside the VM exist only in RAM. Stop the container and they're gone.

That's fine for a boot-speed demo, but it's not a real VM. A real VM has a disk that persists between runs. The disk lives on the host filesystem, the container is just the runtime, and the two are completely independent. Stop and restart the container as many times as you want — the disk doesn't care.

This post adds that layer. You'll create a qcow2 disk image, boot from ISO + disk to run the Alpine installer, then boot from disk alone to confirm it survived.


Prerequisites

  • qemu:base image from Post #1
  • Alpine ISO from Post #2 at ~/Downloads/alpine-standard-3.23.3-x86_64.iso
  • ~/vm directory (you'll create it below)

Step 1: Create the VM Directory and Disk Image

First, create a dedicated directory for your VM disk images:

$ mkdir -p ~/vm
Enter fullscreen mode Exit fullscreen mode

Then create the disk image:

$ podman run --rm \
    -v ~/vm:/vm:z \
    qemu:base \
    qemu-img create -f qcow2 /vm/alpine.qcow2 8G
Enter fullscreen mode Exit fullscreen mode

You should see:

Formatting '/vm/alpine.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=8589934592 lazy_refcounts=off refcount_bits=16
Enter fullscreen mode Exit fullscreen mode

What's qcow2? It stands for QEMU Copy-On-Write version 2. The key property is thin provisioning: the file on your host starts tiny (a few hundred KB) and only grows as the VM actually writes data. Specifying 8G sets the maximum size the VM sees, not the space it consumes on disk immediately.


Step 2: Boot from ISO + Disk to Install

Now boot with both the ISO and the disk attached. The -boot d flag tells QEMU to boot from the CD-ROM first:

$ podman run --rm -it \
    --device /dev/kvm \
    -v ~/vm:/vm:z \
    -v ~/Downloads:/iso:z \
    qemu:base \
    qemu-system-x86_64 \
        -enable-kvm -cpu host \
        -nographic \
        -m 512 \
        -cdrom /iso/alpine-standard-3.23.3-x86_64.iso \
        -drive file=/vm/alpine.qcow2,format=qcow2 \
        -boot d
Enter fullscreen mode Exit fullscreen mode

Alpine will boot from the ISO into a live environment. Log in as root — no password required.


Step 3: Install Alpine

Once you're at the shell, run the Alpine installer:

# setup-alpine
Enter fullscreen mode Exit fullscreen mode

Work through the prompts. Most defaults are fine. The ones that matter:

  • Hostname: anything, e.g. alpine
  • Network: eth0, DHCP
  • Proxy: none
  • Root password: set something you'll remember
  • Timezone: your choice
  • Mirror: pick the fastest (or just press Enter for the default)
  • SSH server: openssh or none — your call
  • Setup a user: enter a username — don't skip this; logging in as root is bad practice
  • Full name: optional, press Enter to skip
  • User password: set one
  • SSH key or URL: none
  • Disk: sda — this is your qcow2 image
  • How to use it: sys — full system install to disk
  • Erase above disk and continue: y

When the installer finishes, power off:

# poweroff
Enter fullscreen mode Exit fullscreen mode

The container exits. The alpine.qcow2 file on your host now contains a complete Alpine installation.


Step 4: Boot from Disk Only

Drop the ISO flags entirely. The disk knows how to boot now:

$ podman run --rm -it \
    --device /dev/kvm \
    -v ~/vm:/vm:z \
    qemu:base \
    qemu-system-x86_64 \
        -enable-kvm -cpu host \
        -nographic \
        -m 512 \
        -drive file=/vm/alpine.qcow2,format=qcow2
Enter fullscreen mode Exit fullscreen mode

Alpine boots from the installed disk. Log in with the username you created during setup. Now write a file to prove the disk persists:

$ echo "hello from install" > ~/persistence-test.txt
$ cat ~/persistence-test.txt
hello from install
$ su -
# poweroff
Enter fullscreen mode Exit fullscreen mode

The container exits. Run the exact same boot command again:

$ podman run --rm -it \
    --device /dev/kvm \
    -v ~/vm:/vm:z \
    qemu:base \
    qemu-system-x86_64 \
        -enable-kvm -cpu host \
        -nographic \
        -m 512 \
        -drive file=/vm/alpine.qcow2,format=qcow2
Enter fullscreen mode Exit fullscreen mode

Log in and check:

$ cat ~/persistence-test.txt
hello from install
Enter fullscreen mode Exit fullscreen mode

The file survived. The container was destroyed and recreated, but the disk image on your host never changed. That's persistence.


Step 5: Why This Persists Across Container Restarts

The container is ephemeral — --rm means Podman deletes it the moment QEMU exits. But the disk image at ~/vm/alpine.qcow2 lives on your host filesystem, completely outside the container lifecycle.

The bind mount (-v ~/vm:/vm:z) is just a path into the host. Writing to /vm/alpine.qcow2 inside the container is writing to ~/vm/alpine.qcow2 on the host. When the container is gone, the file remains.

New Flags at a Glance

Flag Where What It Does
-drive file=/vm/alpine.qcow2,format=qcow2 QEMU Attaches the disk image as a block device (sda inside the VM)
-boot d QEMU Sets boot order to CD-ROM first; needed during install so Alpine boots from ISO, not the blank disk
format=qcow2 QEMU -drive option Tells QEMU the image format explicitly; avoids format auto-detection warnings
-v ~/vm:/vm:z Podman Bind-mounts the host ~/vm directory; the disk image lives here, not inside the container
-v ~/Downloads:/iso:z Podman Bind-mounts the ISO directory; only needed during the install step

What You've Built

  • ✅ qcow2 disk image created on the host
  • ✅ Alpine Linux installed to disk inside a KVM container
  • ✅ VM boots from disk and survives container restarts
  • ✅ Host filesystem as the persistence layer

What's Next?

That install took a few minutes of interactive prompts. Every time you want a new Alpine VM, you'd repeat it from scratch.

Post #4: We'll skip the installer entirely by using a cloud image — a pre-built disk image ready to boot in seconds.


This guide is Part 3 of the KVM Virtual Machines on Podman series.

Part 1: Build a KVM-Ready Container Image from Scratch
Part 2: KVM Acceleration in a Rootless Podman Container
Coming up in Part 4: Cloud Images — Skip the Installer, Boot in Seconds


Found this helpful?


Published: 6 Apr 2026
Author: David Tio
Tags: KVM, QEMU, Podman, Virtualization, Containers, Alpine Linux, qcow2, Linux, Tutorial
Series: KVM Virtual Machines on Podman
Word Count: ~750


SEO Metadata:

  • Title: Persistent VMs in Podman: Install Alpine to a qcow2 Disk Image (2026)
  • Meta Description: Create a qcow2 disk image with qemu-img, install Alpine Linux inside a rootless Podman container, and boot from disk so your VM survives container restarts.
  • Target Keywords: qcow2 podman vm, persistent vm podman, qemu-img create qcow2, alpine linux install qemu, podman kvm persistent disk, vm disk image container
  • Series: KVM Virtual Machines on Podman

Top comments (0)