BCVK-EPHEMERAL-RUN(8)

πŸ“¦
version 0.21.0 Β· 4 versions
, in fedora 43, fedora 44

πŸ¦‹ bcvk-ephemeral-run(8) πŸ¦‹

okay bestie, sit down, hydrate, and let me tell you about the most unhinged little VM launcher you've ever met πŸ’… this is bcvk-ephemeral-run, and it is literally the moment.


✨ NAME ✨

bcvk-ephemeral-run β€” run bootc containers as stateless VMs


🌸 SYNOPSIS 🌸

bcvk ephemeral run [OPTIONS] IMAGE

πŸ’– DESCRIPTION πŸ’–

Run bootc containers as stateless VMs managed by podman. The VM boots directly from the container image's filesystem via virtiofs, with no disk image creation required. That's the whole tea β˜• β€” startup is fast, and the VM is stateless by default. Iconic behaviour, no notes.

πŸŽ€ How It Works πŸŽ€

This command creates an ephemeral virtual machine by launching a podman container that contains and runs QEMU. The process works like this, step by step, no skips:

  1. Container Setup: A privileged podman container is launched with access to the host's virtualization infrastructure
  2. Host Virtualization Access: The container gains access to:
    • /dev/kvm for hardware virtualization
    • Host's virtiofsd daemon for filesystem sharing
    • QEMU binaries and virtualization stack
  3. VM Creation: Inside the container, QEMU is executed to create a virtual machine
  4. Root Filesystem: The bootc container image's root filesystem becomes the VM's root filesystem, mounted via virtiofs
  5. Kernel Boot: The VM boots using the kernel and initramfs from the bootc container image

This architecture provides several advantages:

  • Isolation: The VM runs in a contained environment separate from the host
  • Fast I/O: virtiofs provides efficient filesystem access between container and VM
  • Resource Efficiency: Leverages existing container infrastructure while providing full VM capabilities

🧚 Container-VM Relationship 🧚

The relationship between the podman container and the VM inside it, spelled out because it is a little bit of a situationship:

  • Podman Container: Acts as the virtualization environment, providing QEMU and system services
  • QEMU Process: Runs inside the podman container, creating the actual virtual machine
  • VM Guest: The bootc container image runs as a complete operating system inside the VM
  • Filesystem Sharing: The container's root filesystem is shared with the VM via virtiofs at runtime

This design allows bcvk to provide VM-like isolation and boot behavior while leveraging container tooling. it's giving "have your cake and virtualize it too" πŸ“


πŸ‘‘ OPTIONS πŸ‘‘

  • IMAGE β€” Container image to run as ephemeral VM. This argument is required. (yes bestie, required required, not "required" like your ex was "busy" πŸ™ˆ)
  • --itype=ITYPE β€” Instance type (e.g., u1.nano, u1.small, u1.medium). Overrides vcpus/memory if specified.
  • --memory=MEMORY β€” Memory size (e.g. 4G, 2048M, or plain number for MB). Default: 4G
  • --vcpus=VCPUS β€” Number of vCPUs (overridden by --itype if specified)
  • --console β€” Connect the QEMU console to the container's stdio (visible via podman logs/attach)
  • --debug β€” Enable debug mode (drop to shell instead of running QEMU)
  • --virtio-serial-out=NAME:FILE β€” Add virtio-serial device with output to file (format: name:/path/to/file)
  • --execute=EXECUTE β€” Execute command inside VM via systemd and capture output
  • -K, --ssh-keygen β€” Generate SSH keypair and inject via systemd credentials
  • --virtiofsd=VIRTIOFSD_BINARY β€” Path to virtiofsd binary (overrides auto-detection)
  • --output=OUTPUT β€” Select how VM output is presented

Possible values:

  • console
  • journal

Default: console

  • --log-dir=STREAMS=DIR β€” Write VM log streams to files in DIR
  • -t, --tty β€” Allocate a pseudo-TTY for container
  • -i, --interactive β€” Keep STDIN open for container
  • -d, --detach β€” Run container in background
  • --rm β€” Automatically remove container when it exits
  • --name=NAME β€” Assign a name to the container
  • --network=NETWORK β€” Configure the network for the container
  • --label=LABEL β€” Add metadata to the container in key=value form
  • -e, --env=ENV β€” Set environment variables in the container (key=value)
  • --debug-entrypoint=DEBUG_ENTRYPOINT β€” Do not run the default entrypoint directly, but instead invoke the provided command (e.g. bash)
  • --bind=HOST_PATH[:NAME] β€” Bind mount host directory (RW) at /run/virtiofs-mnt-<name>
  • --ro-bind=HOST_PATH[:NAME] β€” Bind mount host directory (RO) at /run/virtiofs-mnt-<name>
  • --systemd-units=SYSTEMD_UNITS_DIR β€” Directory with systemd units to inject (expects system/ subdirectory)
  • --bind-storage-ro β€” Mount host container storage (RO) at /run/virtiofs-mnt-hoststorage
  • --add-swap=ADD_SWAP β€” Allocate a swap device of the provided size
  • --mount-disk-file=FILE[:NAME] β€” Mount disk file as virtio-blk device at /dev/disk/by-id/virtio-<name>
  • --karg=KERNEL_ARGS β€” Additional kernel command line arguments
  • --ignition=IGNITION_CONFIG β€” Path to Ignition config file (JSON format) to inject via fw_cfg

🌈 EXAMPLES 🌈

πŸ’œ Build and Test Workflow πŸ’œ

The most common use case is testing container images you're building. this is the daily routine, the main character arc:

# Build your bootc container image
podman build -t localhost/mybootc .

# Run it as an ephemeral VM (background, auto-cleanup, SSH keys)
bcvk ephemeral run -d --rm -K --name test localhost/mybootc

# SSH in to verify it works
bcvk ephemeral ssh test

# Stop when done (container auto-removed due to --rm)
podman stop test

For a faster iteration loop, use bcvk-ephemeral-run-ssh(8) which combines run and SSH into one command with automatic cleanup. obsessed with her honestly 🫢

⭐ Common Flag Combinations ⭐

Testing a public image:

bcvk ephemeral run -d --rm -K --name testvm quay.io/fedora/fedora-bootc:42
bcvk ephemeral ssh testvm

Development with mounted source code:

bcvk ephemeral run -d --rm -K \
    --bind /home/user/project:src \
    --name devvm localhost/mybootc
bcvk ephemeral ssh devvm
# Files available at /run/virtiofs-mnt-src inside VM

Resource-intensive workloads:

bcvk ephemeral run -d --rm -K \
    --memory 8G --vcpus 4 \
    --name bigvm localhost/mybootc

Debugging boot issues:

bcvk ephemeral run --console -it --name debugvm localhost/mybootc

πŸŽ€ Understanding the Flags πŸŽ€

  • -d, --detach: Run in background. Without this, the container runs in foreground and you see QEMU output directly.
  • --rm: Auto-remove the container when it stops. Highly recommended for ephemeral testing to avoid accumulating stopped containers. (bestie, your disk will thank you, no cap)
  • -K, --ssh-keygen: Generate SSH keypair and inject into the VM via systemd credentials. Required for bcvk ephemeral ssh to work.
  • --name: Assign a name for easy reference with other commands like bcvk ephemeral ssh.

πŸ¦‹ Bind Mounts πŸ¦‹

Share host directories with the VM using --bind or --ro-bind:

# Read-write mount
bcvk ephemeral run -d --rm -K --bind /host/path:name --name vm image

# Inside VM, access at:
/run/virtiofs-mnt-name

# Read-only mount (safer for sensitive data)
bcvk ephemeral run -d --rm -K --ro-bind /etc/myconfig:config --name vm image

🌸 Disk File Mounts 🌸

Mount raw disk images as block devices:

# Create a test disk
truncate -s 10G /tmp/testdisk.raw

# Mount into VM
bcvk ephemeral run -d --rm -K \
    --mount-disk-file /tmp/testdisk.raw:testdisk \
    --name vm localhost/mybootc

# Inside VM: /dev/disk/by-id/virtio-testdisk

πŸ’… Port Forwarding πŸ’…

For SSH port forwarding (recommended), use bcvk ephemeral ssh with -L or -R:

# Forward VM port 80 to localhost:8080
bcvk ephemeral ssh myvm -L 8080:localhost:80

For network-level port forwarding via podman, configure slirp4netns:

bcvk ephemeral run -d --rm -K \
    --network slirp4netns:port_handler=slirp4netns,allow_host_loopback=true \
    --name webvm localhost/mybootc

πŸ”₯ Instance Types πŸ”₯

Use predefined instance types for consistent resource allocation:

bcvk ephemeral run -d --rm -K --itype u1.small --name vm localhost/mybootc
bcvk ephemeral run -d --rm -K --itype u1.medium --name vm localhost/mybootc
bcvk ephemeral run -d --rm -K --itype u1.large --name vm localhost/mybootc

🧚 Ignition Configuration 🧚

Inject Ignition (https://coreos.github.io/ignition/) configuration files for first-boot provisioning on CoreOS-based images:

# Create an Ignition config file (v3.3.0 format)
cat > config.ign <<EOF
{
  "ignition": {
    "version": "3.3.0"
  },
  "passwd": {
    "users": [
      {
        "name": "core",
        "sshAuthorizedKeys": [
          "ssh-ed25519 AAAAC3... user@example.com"
        ]
      }
    ]
  },
  "storage": {
    "files": [
      {
        "path": "/etc/hostname",
        "contents": {
          "source": "data:,my-coreos-vm"
        },
        "mode": 420
      }
    ]
  }
}
EOF

# Run Fedora CoreOS with Ignition config
bcvk ephemeral run -d --rm \
    --ignition config.ign \
    --name fcos-vm \
    quay.io/fedora/fedora-coreos:stable

Important notes:

  • Only works with Ignition-capable images (Fedora CoreOS, RHEL CoreOS, or custom bootc images with Ignition support)
  • Config is injected via fw_cfg on x86_64/aarch64, virtio-blk on s390x/ppc64le (following FCOS conventions)
  • The ignition.platform.id=qemu kernel argument is automatically added
  • For ephemeral VMs, Ignition typically only runs on the first boot of a newly provisioned system

See the Ignition documentation (https://coreos.github.io/ignition/) and bootc initramfs documentation (https://docs.fedoraproject.org/en-US/bootc/initramfs/) for creating custom bootc images with Ignition support.


πŸ’€ DEBUGGING πŸ’€

When troubleshooting ephemeral VM issues, bcvk provides several debugging logs that can be accessed from within the container. it's giving "she left receipts" and honestly we love that for us

😭 Guest Journal Log 😭

The systemd journal from the guest VM is automatically streamed to /run/journal.log inside the container. This log captures all boot messages, service startup events, and system errors from the VM's perspective.

To view the journal log:

# For a running detached VM
podman exec <container-id> tail -f /run/journal.log

# View specific systemd service messages
podman exec <container-id> grep "dbus-broker" /run/journal.log

# Save journal for offline analysis
podman exec <container-id> cat /run/journal.log > guest-journal.log

The journal log is particularly useful for:

  • Diagnosing boot failures and systemd service issues
  • Investigating permission denied errors
  • Understanding VM initialization problems
  • Debugging network and device configuration

✨ Virtiofsd Logs ✨

The virtiofsd daemon logs are written to /run/virtiofsd.log and /run/virtiofsd-<mount-name>.log for each filesystem mount. These logs show filesystem sharing operations between the container and VM.

To view virtiofsd logs:

# Main virtiofsd log
podman exec <container-id> cat /run/virtiofsd.log

# Logs for additional bind mounts
podman exec <container-id> cat /run/virtiofsd-workspace.log

Virtiofsd logs are helpful for:

  • Debugging filesystem access issues
  • Understanding file handle support warnings
  • Investigating mount-related errors

πŸ’€ Podman Can't See bcvk's Filesystem πŸ’€

okay bestie this one is a whole saga so buckle up 🫢 To boot a VM, bcvk asks podman to bind-mount paths straight out of its own filesystem β€” including its own binary. That only works if podman can literally see the same filesystem bcvk does. When it can't, you get an error that looks like this:

Podman command failed: Error: statfs /usr/bin/bcvk: no such file or directory

and you're just standing there like 🧍 no cap.

When does this happen tho? It hits when bcvk is running inside a toolbox or distrobox container and podman is forwarded to the host instead of actually running inside the container. Think a flatpak-spawn --host podman wrapper, or the podman socket β€” that kind of setup. It also happens when podman is a remote client. In all those cases, the paths bcvk hands to podman get resolved on podman's side, so a bcvk binary that only exists inside your toolbox simply isn't findable over there. The good news: bcvk notices this and slaps a hint onto the error explaining it. Iconic behaviour honestly πŸ‘‘

The fix: install bcvk on the host, right alongside podman and QEMU, and just run it there. Coming from a toolbox? Call the host's copy with:

flatpak-spawn --host bcvk ...

Coming from a distrobox? Then it's:

distrobox-host-exec bcvk ...

and you're so back 🌸

⚠️ okay now the scary part, bestie: beware of version skew. If bcvk binaries exist at the same path on both the host and in the toolbox β€” say, both installed from packages β€” this error won't even show up. Instead, podman mounts the host's copy into the VM's container, which can be a totally different version from the one you actually ran. That's giving chaos energy πŸ’€ Keep them in sync, or better yet, install bcvk only on the host. periodt.

✨ SEE ALSO ✨

  • bcvk(8) β€” yeah bestie, the main character herself πŸŽ€

🌈 VERSION 🌈

  • v0.21.0

and that's a wrap on the whole page, babe 🫢 you just read the bcvk(8) man page, v0.21.0, straight from the source. go forth and virtualize responsibly, mwah πŸ’…βœ¨πŸ’–

πŸ“¦ where this page comes from