Skip to main content

Minivirt

VMs should be easy.

Discord

Minivirt is a lightweight QEMU manager that provides a Docker-like user experience. The default image is based on Alpine Linux, which is tiny and fast: 50MB compressed disk image, boots to SSH in second(s).

Installation

  1. Install QEMU and other dependencies.

    • MacOS: brew install qemu socat
    • Debian: apt install qemu-kvm qemu-utils qemu-efi-aarch64 socat
    • Alpine: apk add py3-pip qemu qemu-system-x86_64 qemu-img socat tar
    • Arch: pacman -S python-pip qemu-base socat
  2. Install Minivirt and run a checkup.

    pip3 install minivirt
    miv doctor
    
  3. Pull an image and start a VM.

    miv remote add default https://f003.backblazeb2.com/file/minivirt
    miv pull default alpine-{arch} alpine  # {arch} is automatically replaced with your architecture.
    miv run alpine
    

The miv run command will create an ephemeral VM and open an SSH session into it. When you exit the session, the VM is destroyed.

Under the hood

The actual work of emulating virtual machines is done by QEMU. It runs in many environments, which means we can provide (mostly) the same features everywhere.

Virtual machines run as user processes, no root privileges necessary. The user does however need permissions for hardware virtualization (e.g. access to /dev/kvm on Linux).

It's possible to interact with the VM in three ways:

  • Serial console: this is the default for miv start.
  • Graphical display: enabled by the --display argument.
  • SSH: miv run connects through SSH, using the Vagrant well-known SSH key. Also, miv ssh can shell into a running VM.

The QEMU VM is set up with User Networking, which doesn't interfere with the host's network stack, and the guest SSH port is forwarded to a random port on localhost. You can forward more ports with the --port option.

Minivirt manages images, which are essentially read-only, reusable virtual machine qcow2 disks; and VMs, with their own copy-on-write disk, which uses the image disk as its backing file. Everything is stored in ~/.cache/minivirt/.

Doctor

The miv doctor command runs a checkup to help with troubleshooting. It checks to see if qemu-system-{arch}, qemu-img, socat and tar are installed, and if /dev/kvm is usable.

Persistent VMs

Create a VM with the create command:

miv create alpine myvm

Start the VM with the terminal attached to its serial console:

miv start myvm

Gracefully stop the VM by sending an ACPI poweroff:

miv stop myvm

Destroy the VM to remove its disk image and other resources:

miv destroy myvm

Inspect the VMs:

miv ps
miv ps -a  # also shows stopped VMs

Graphics

Start the VM in the background and connect a display to it:

miv create alpine myvm
miv start myvm --daemon --display

Log in as root, and run:

setup-xorg-base
apk add xfce4 xfce4-terminal dbus
startx

To make the screen bigger, right-click on the desktop, hover on Applications, then Settings, and click Display. Select another resolution like "1440x900" and click "apply".

Images

Minivirt maintains a database of images identified by their SHA256 checksum. They may have any number of tags.

Show images in the database:

% miv images
5446f671 1.4G ubuntu-22.04
84200bbd 115M alpine-3.15
8ad24d9f 1.4G ubuntu-20.04
c86a9115 114M alpine alpine-3.16

Building an image

Minivirt can build images from recipes, which are YAML files, with a syntax inspired by GitHub Actions workflows. Download any file from the /recipes directory and run:

miv build alpine-3.16.yaml --tag alpine -v

The -v flag directs the output of the build (serial console or SSH) to stdout.

The image is now in the database:

miv run alpine

Other image operations

Commit a VM as an image:

miv commit myvm myimage

Save the image as a TAR archive:

miv save myimage | gzip -1 > myimage.tgz

Later, load the image:

zcat myimage.tgz | miv load myimage

Database maintenance

To make sure the images and VMs are consistent, run a database check:

miv fsck

To remove an image, first untag it. This only removes the tag, not the image itself.

miv untag myimage

The image is removed during prune:

miv prune

Image repositories

Add a remote repository:

miv remote add default https://f003.backblazeb2.com/file/minivirt

Pull an image. {arch} will be interpolated to the machine architecture.

miv pull default alpine-{arch} alpine

To host an image repository, you need an object store (e.g. Amazon S3, Backblaze B2, MinIO, etc). Set the following environment variables:

  • AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY: authentication credentials.
  • AWS_ENDPOINT_URL (optional): if the object store is not hosted on the AWS public cloud, this should point to the appropriate endpoint.

The bucket name is taken from the last part of the remote's URL, e.g. minivirt for the default repository.

Run miv push to upload an image:

miv push default alpine-3.16 alpine-3.16-aarch64

Development

  1. Clone the repository:

    git clone https://github.com/mgax/minivirt
    cd minivirt
    
  2. Create a virtualenv so you don't interfere with gobally-installed packages:

    python3 -m venv .venv
    source .venv/bin/activate
    
  3. Install the repo in edit mode and development dependencies:

    pip install -e '.[devel]'
    
  4. Run the test suite:

    pytest
    pytest --runslow  # if you're not in a hurry
    

Python API

Minivirt is written in Python and offers a straightforward API:

from minivirt.cli import db

alpine = db.get_image('alpine')
myvm = VM.create(db, 'myvm', image=alpine, memory=512)
with myvm.run(wait_for_ssh=30):
    print(myvm.ssh('uname -a', capture=True))

GitHub Actions self-hosted runners

Minivirt comes with a server that launches GitHub Actions runners when a workflow job is queued. Each runner is ephemeral and runs in its own VM.

  1. Install extra dependencies:

    pip install -e minivirt[githubactions]
    
  2. Build an actions runner image:

    miv build recipes/alpine-3.15.yaml --tag alpine-3.15 -v
    miv build recipes/ci-alpine.yaml --tag ci-alpine -v
    miv build recipes/githubactions-alpine.yaml --tag githubactions-alpine -v
    
  3. Run the server. To interact with the GitHub API, it needs a GitHub PAT, and runs git credentials fill to retrieve it. It uses ngrok to listen for webhook events; to avoid the ngrok session timing out, set a token in the NGROK_AUTH_TOKEN environment variable.

    miv -v githubactions serve githubactions-alpine {repo}
    

Get in touch

For feedback, support, and contributions, visit:

Metadata

Release files for minivirt 0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for minivirt 0.1
File Size Uploaded Provenance
minivirt-0.1.tar.gz 24.8 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for minivirt 0.1
File Interpreter ABI Platform
minivirt-0.1-py3-none-any.whl Python 3 none any Details

Total release size: 49.5 kB

Release files / minivirt-0.1.tar.gz

Download URL minivirt-0.1.tar.gz
Size 24.8 kB
Tags Source
SHA-256 checksum
How to use checksums
1a5e4c52985bf08a83091cf4c976cf3641f1c9eb96eae57d0315b7dce04636b6
BLAKE2b-256 checksum
How to use checksums
62ce2a09196e4fe474b9ef00ac32fb21c85f703c467f6d704e4549a4a909df76
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.2.1 CPython/3.10.7 Darwin/21.6.0

Release files / minivirt-0.1-py3-none-any.whl

Download URL minivirt-0.1-py3-none-any.whl
Size 24.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
699b62d91d76d3c88ad1b4bf0f53cbfdb61f952f39963f8ecc07ff8b85b8bc5b
BLAKE2b-256 checksum
How to use checksums
5265d61e5fd508d8b010f0effa03ce362cc487d9d5ab207062700a9b6999f882
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.2.1 CPython/3.10.7 Darwin/21.6.0

Security for minivirt 0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page