Painless VM management from the CLI
Project description
Minivirt
VMs should be easy.
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
-
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
- MacOS:
-
Install Minivirt and run a checkup.
pip3 install minivirt miv doctor
-
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
-
Clone the repository:
git clone https://github.com/mgax/minivirt cd minivirt
-
Create a virtualenv so you don't interfere with gobally-installed packages:
python3 -m venv .venv source .venv/bin/activate
-
Install the repo in edit mode and development dependencies:
pip install -e '.[devel]'
-
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.
-
Install extra dependencies:
pip install -e minivirt[githubactions]
-
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
-
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 theNGROK_AUTH_TOKEN
environment variable.miv -v githubactions serve githubactions-alpine {repo}
Get in touch
For feedback, support, and contributions, visit:
- The Discord server.
- Discussions on GitHub.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.