box
Manage VirtualBox virtual machines from the command line.
box imports an Ubuntu cloud image as a VirtualBox machine, seeds it with
cloud-init (a passwordless-sudo box user, your SSH key, a static host-only
address) and gets you a shell in it — one command per lifecycle step, no
VBoxManage incantations.
It is a Python port of the box vbox provider from
gitlab.com/xtec/box, packaged on its own so it
installs with uv/pip instead of a Rust toolchain. Sibling of
wslx, which ports the WSL provider the
same way. Machines created by either tool are interchangeable: same config
directory, same cloud-init, same box user, same SSH key.
Install
uv tool install boxctl
or run it without installing:
uvx --from boxctl box list
pip install boxctl works too. Python 3.11+.
The distribution is named boxctl because box is taken on PyPI; the
command it installs is box. A boxctl alias is installed alongside it in
case something else on your PATH already answers to box.
Use
box create alfa # import a new Ubuntu machine named alfa
box ssh alfa # start it if needed, wait for sshd, open a shell
box list # every machine box made, with state and address
box stop alfa # ACPI power button, wait for poweroff
box start alfa -c 4 -m 4096 # boot headless with 4 CPUs and 4 GB RAM
box resize alfa 40 # grow the disk to 40 GB, and the partition inside it
box delete alfa # power off, unregister, delete the disks
create, start, stop and delete take any number of names:
box create alfa beta gamma
Images
box create alfa --image ubuntu # default
box create alfa --image coreos
box create alfa --image fedora
| Image | Source | Provisioning |
|---|---|---|
ubuntu |
Ubuntu 22.04 cloud OVA, downloaded and cached | cloud-init seed ISO |
coreos |
Fedora CoreOS 37 OVA, downloaded and cached | Ignition guest property |
fedora |
<config>/ova/fedora-37.ova, supplied by you |
none |
Fedora Cloud publishes no VirtualBox appliance, so there is nothing for box to download and no network layout it can rely on; drop your own OVA at that path and box will import it as-is.
What a machine looks like
- user
box, passwordpassword, passwordlesssudo, your~/.ssh/id_ed25519.pubinauthorized_keys - a NAT adapter for outbound traffic (
enp0s3) and a host-only adapter (enp0s8) with a static address, sobox sshworks without DHCP leases - hostname set to the machine name
- snapd disabled, floppy controller blacklisted, cloud-init disabled after the first boot
Addresses
Host-only addresses are allocated by box from 192.168.56.15 upward and stored
in the machine's own VirtualBox extra data, so they survive reboots. The range
stops at .100: VirtualBox's host-only interface is .1 and its DHCP server
hands out .101–.254.
Where things live
| What | Where |
|---|---|
Machines (.vbox, disks, seed.iso) |
~/.config/box/virtualbox/<name>/ |
| Downloaded OVA appliances | ~/.config/box/cache/ |
| Locally supplied appliances | ~/.config/box/ova/ |
| SSH key pair | ~/.ssh/id_ed25519 |
On Windows the config directory is %APPDATA%\box instead. box delete
removes a machine's folder; the SSH key and the OVA cache are left alone.
box list only shows machines box created — it owns a directory per machine,
and that listing is what it walks. A directory whose machine VirtualBox no
longer knows about (deleted from the GUI, say) is pruned as a side effect.
Requirements
VBoxManage on PATH — on Windows box also honours the installer's
VBOX_MSI_INSTALL_PATH. box ssh and box resize additionally need an ssh
client. The package installs and imports anywhere so it can be developed and
tested on a machine with no VirtualBox at all.
Development
uv sync --all-groups
uv run pytest
uv run ruff check
The test suite needs neither VirtualBox nor a network: everything that shells
out is covered through its pure parts — VBoxManage output parsing, host
address allocation, cloud-init and Ignition rendering, seed ISO structure, key
generation — with subprocess stubbed at the boundary.
Differences from the Rust box vbox
Behaviour is otherwise a faithful port.
box stopgives up with an error instead of pressing the ACPI button forever when a guest ignores it.VBoxManageoutput parsing keeps values containing=(rec_screen_opts), which the Rust port's regexes silently dropped.- The generated private key always uses LF line endings, on Windows too. The
Rust port wrote CRLF and then had to
sedthe CRs out again after copying the key into a machine. - The netplan file box writes declares
version: 2, which netplan requires. - The seed ISO is built with
pycdlibrather than a hand-rolled ISO 9660 writer, and carries Joliet and Rock Ridge so the guest seesuser-datarather than its 8.3 alias. box ip <name>is new;box updateis gone, sinceuv tool upgrade boxctldoes that job.
License
AGPL-3.0-only. © David de Mingo.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file boxctl-0.1.0.tar.gz.
File metadata
- Download URL: boxctl-0.1.0.tar.gz
- Upload date:
- Size: 30.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b3665f3e171ab61a0881366f5898f891faaf640a53a11712192296a88624182d
|
|
| MD5 |
fafe0b2d9a02a6f8595e74e707afbfa9
|
|
| BLAKE2b-256 |
da526bb6fcebd54f154dd8a05821f2ee1a8e1bd8c2731eb595c36a7931d6bc56
|
File details
Details for the file boxctl-0.1.0-py3-none-any.whl.
File metadata
- Download URL: boxctl-0.1.0-py3-none-any.whl
- Upload date:
- Size: 33.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b5eb25e32020f854266764d267cf179dbcba31e326bd9e9dbeb2b071a9796bf0
|
|
| MD5 |
f6c15c8bda37b1eb912d7043ca7b1034
|
|
| BLAKE2b-256 |
dd9833c904f83d3979dddae7ed8b356334b8843ba63c9d446ef409f7824a44f1
|