Package: minivirt
MinivirtVMs should be easy.Minivirtis a lightweightQEMUmanager that provides a Docker-like user experience. The default image is based onAlpine Linux, which is tiny and fast: 50MB compressed disk image, boots to SSH in second(s).InstallationInstall QEMU and other dependencies.MacOS:brew install qemu socatDebian:apt install qemu-kvm qemu-utils qemu-efi-aarch64 socatAlpine:apk add py3-pip qemu qemu-system-x86_64 qemu-img socat tarArch:pacman -S python-pip qemu-base socatInstallMinivirtand run a checkup.pip3installminivirt
mivdoctorPull an image and start a VM.mivremoteadddefaulthttps://f003.backblazeb2.com/file/minivirt
mivpulldefaultalpine-{arch}alpine# {arch} is automatically replaced with your architecture.mivrunalpineThemiv runcommand will create an ephemeral VM and open an SSH session into it. When you exit the session, the VM is destroyed.Under the hoodThe 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/kvmon Linux).It's possible to interact with the VM in three ways:Serial console: this is the default formiv start.Graphical display: enabled by the--displayargument.SSH:miv runconnects through SSH, using theVagrant well-known SSH key. Also,miv sshcan shell into a running VM.The QEMU VM is set up withUser Networking, which doesn't interfere with the host's network stack, and the guest SSH port is forwarded to a random port onlocalhost. You can forward more ports with the--portoption.Minivirt managesimages, which are essentially read-only, reusable virtual machine qcow2 disks; andVMs, with their owncopy-on-writedisk, which uses the image disk as its backing file. Everything is stored in~/.cache/minivirt/.DoctorThemiv doctorcommand runs a checkup to help with troubleshooting. It checks to see ifqemu-system-{arch},qemu-img,socatandtarare installed, and if/dev/kvmis usable.Persistent VMsCreate a VM with thecreatecommand:mivcreatealpinemyvmStart the VM with the terminal attached to its serial console:mivstartmyvmGracefully stop the VM by sending an ACPI poweroff:mivstopmyvmDestroy the VM to remove its disk image and other resources:mivdestroymyvmInspect the VMs:mivps
mivps-a# also shows stopped VMsGraphicsStart the VM in the background and connect a display to it:mivcreatealpinemyvm
mivstartmyvm--daemon--displayLog in asroot, and run:setup-xorg-base
apkaddxfce4xfce4-terminaldbus
startxTo make the screen bigger, right-click on the desktop, hover onApplications, thenSettings, and clickDisplay. Select another resolution like "1440x900" and click "apply".ImagesMinivirtmaintains a database of images identified by their SHA256 checksum. They may have any number of tags.Show images in the database:%mivimages
5446f6711.4Gubuntu-22.04
84200bbd115Malpine-3.15
8ad24d9f1.4Gubuntu-20.04
c86a9115114Malpinealpine-3.16Building an imageMinivirt can build images fromrecipes, which are YAML files, with a syntax inspired byGitHub Actions workflows. Download any file fromthe/recipesdirectoryand run:mivbuildalpine-3.16.yaml--tagalpine-vThe-vflag directs the output of the build (serial console or SSH) to stdout.The image is now in the database:mivrunalpineOther image operationsCommit a VM as an image:mivcommitmyvmmyimageSave the image as a TAR archive:mivsavemyimage|gzip-1>myimage.tgzLater, load the image:zcatmyimage.tgz|mivloadmyimageDatabase maintenanceTo make sure the images and VMs are consistent, run a database check:mivfsckTo remove an image, first untag it. This only removes the tag, not the image itself.mivuntagmyimageThe image is removed during prune:mivpruneImage repositoriesAdd a remote repository:mivremoteadddefaulthttps://f003.backblazeb2.com/file/minivirtPull an image.{arch}will be interpolated to the machine architecture.mivpulldefaultalpine-{arch}alpineTo 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.minivirtfor the default repository.Runmiv pushto upload an image:mivpushdefaultalpine-3.16alpine-3.16-aarch64DevelopmentClone the repository:gitclonehttps://github.com/mgax/minivirtcdminivirtCreate a virtualenv so you don't interfere with gobally-installed packages:python3-mvenv.venvsource.venv/bin/activateInstall the repo in edit mode and development dependencies:pipinstall-e'.[devel]'Run the test suite:pytest
pytest--runslow# if you're not in a hurryPython APIMinivirtis written in Python and offers a straightforward API:fromminivirt.cliimportdbalpine=db.get_image('alpine')myvm=VM.create(db,'myvm',image=alpine,memory=512)withmyvm.run(wait_for_ssh=30):print(myvm.ssh('uname -a',capture=True))GitHub Actions self-hosted runnersMinivirt 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.Install extra dependencies:pipinstall-eminivirt[githubactions]Build an actions runner image:mivbuildrecipes/alpine-3.15.yaml--tagalpine-3.15-v
mivbuildrecipes/ci-alpine.yaml--tagci-alpine-v
mivbuildrecipes/githubactions-alpine.yaml--taggithubactions-alpine-vRun the server. To interact with the GitHub API, it needs aGitHub PAT, and runsgit credentials fillto retrieve it. It usesngrokto listen for webhook events; to avoid the ngrok session timing out, set a token in theNGROK_AUTH_TOKENenvironment variable.miv-vgithubactionsservegithubactions-alpine{repo}Get in touchFor feedback, support, and contributions, visit:The Discord server.Discussionson GitHub.