Skip to content
On this page

Prepare a Linux environment

Download and prepare Ubuntu on an Apple Silicon Mac with resumable local setup.

The guided Linux path creates an Ubuntu 24.04 ARM64 base image with a local vectis user, pinned SSH access, Git, Docker the pinned Actions runner and its dependencies. It runs through the same local service from desktop, CLI and MCP. It requires an Apple Silicon Mac and the configured Apple helper, which runs the VM through Apple Virtualization. The service converts the downloaded disk itself; qemu-img is not needed.

Choose existing writable directories for base images and disposable VMs. They may be on an external drive. The image volume needs at least 8 GiB free; actual usage grows as software is installed. Virtual disk capacity is separate from allocated host storage.

vectis environment prepare-linux ubuntu-24-arm64 \
  --image-directory /absolute/path/to/images \
  --storage-path /absolute/path/to/vms \
  --cpu 2 --memory-mib 4096 --disk-gib 32 --json
vectis operation wait <operation-id> --timeout 3600000 --json

Use Environments > Prepare a guest image, with Ubuntu 24.04 ARM64 selected, in the desktop. MCP accepts environment.prepare-linux with the same fields. Preparation currently reserves the host for one setup at a time: stop existing VMs and resolve interrupted runners first. Normal VM admission is blocked during preparation; pausing, diagnostics and cancellation remain available.

The service downloads a pinned official Ubuntu QCOW2 image, checks its exact size and SHA-256, converts it to a bootable raw disk, and performs guest configuration locally. The source revision is recorded beside the completed image. Installed package versions are available inside the guest at /etc/vectis/toolchain-versions.txt. No host home directory, Docker socket or GitHub App key is shared with the guest.

Each disposable instance receives its own SSH host key through private, read-only setup media. Images prepared with a shared SSH host key must be prepared again before starting runners; Vectis reports action_required for those images. To keep the same environment ID, and with it the vectis-<id> runner label and existing repository connections, remove the old environment and prepare the new image under that ID. Removing an environment keeps its image files on disk.

Network access

Apple Virtualization NAT does not enforce egress filtering. The guest can reach the internet, the host and the local network. Status reports network: { egress: "open", enforcement: "not_enforced" }, including for existing environments without an explicit setting.

vectis environment configure <id> --egress open --json is accepted. Requesting internet_only fails with egress_unsupported: “Apple Virtualization NAT cannot restrict guest network access yet; this environment can reach the host and local network.” Windows environments support enforced internet-only egress. Inspect effective state with vectis status --json, vectis environment list --json or MCP vectis_status; MCP uses the same configuration command.

What the image contains

A prepared image is a runner host, not a copy of a GitHub-hosted runner. GitHub’s Ubuntu ARM64 image is built by 63 provisioning steps and carries hundreds of preinstalled tools, which does not belong in a disposable VM on a personal Mac. What a workflow actually depends on is narrower, because actions/setup-* actions download toolchains at job time. That is the layer Vectis matches, and it takes the list from the same place GitHub does: the apt section of the Ubuntu 24.04 ARM64 toolset in actions/runner-images.

So a prepared guest has Git, Docker, OpenSSH and the official runner’s dependencies, the archive and shell tools that actions invoke (tar, gzip, xz-utils, zip, unzip, zstd, p7zip-full, jq, curl, wget, rsync, shellcheck), a C and C++ toolchain with the headers native modules build against (gcc, g++, make, pkg-config, autoconf, automake, libtool, libssl-dev, libsqlite3-dev, libicu-dev), and the shared libraries prebuilt binaries link against, libatomic1 among them. Two packages differ from GitHub’s list: p7zip-rar is left out because it is non-free, and netcat and upx are installed under their real package names. The complete inventory of a prepared image is in the guest at /etc/vectis/toolchain-versions.txt.

Preparation updates ca-certificates from the Ubuntu security archive and fails unless it is at least 20260816ubuntu0.24.04.1. That release distrusts the emSign ECC Root CA - G3 root after the October 2026 eMudhra intermediate key compromise, so curl, git, apt and Docker in the guest reject chains through those intermediates. An image prepared before this check keeps its older store. Derive an environment whose setup script runs sudo -n apt-get update && sudo -n apt-get install -y ca-certificates, or prepare a new one.

Deliberately absent: language runtimes and their version managers, databases, browsers and drivers, cloud CLIs, Android and Java SDKs. A job that needs one installs it in a step, or runs in a container with container: in the workflow, which works because the guest runs Docker. Nothing a job installs survives it, since every VM starts from the same base image. To bake shared tools into a new base, derive an environment with environment.provision; the original stays immutable.

Docker container jobs (container:) and service containers (services:) are supported on the Ubuntu environment and covered by the environment validation workflow.

Run x86_64 Linux tools

Enable Rosetta per Linux environment to run x86_64 Linux binaries and Docker images requested with docker run --platform linux/amd64. The Mac must have Rosetta installed, and the Linux image must have been prepared with Rosetta guest support. Older images need re-preparation; enabling the option alone does not update an image. Prepare a new Linux environment and reconnect its repositories if the existing image lacks support.

# Run on the Mac if Rosetta is not installed:
softwareupdate --install-rosetta
vectis environment configure ubuntu-24-arm64 --x64 rosetta --json
# Run inside a job:
docker run --rm --platform linux/amd64 ubuntu:24.04 uname -m
# Disable translation for future instances:
vectis environment configure ubuntu-24-arm64 --x64 off --json

Vectis shares only Apple’s read-only Rosetta runtime with an opted-in Linux guest. A conditional boot unit mounts it read-only at /run/rosetta and registers an x86_64 ELF handler with binfmt_misc. It registers after systemd-binfmt has finished, so the normal boot registration cannot clear the handler. The CF flags preserve binary credentials and keep the interpreter available in container mount namespaces. The mount attempt is the unit condition: a missing tag skips registration without failing the unit, including on Ubuntu kernels that do not expose a sysfs tag file.

Instance startup verifies the guest handler before reporting success. Missing host support reports rosetta_not_installed or rosetta_unsupported; a missing or disabled guest handler reports rosetta_guest_unprepared with the next step “Prepare the Linux environment again”. Translation does not provide x86_64 libraries for standalone dynamically linked binaries; the binary needs compatible libraries, for example inside an amd64 container.

The runner and guest kernel stay ARM64. Jobs requesting X64 labels still do not match, and setup-* actions still install ARM64 tools by default. Rosetta does not change label matching or workflow migration.

The setting applies to future instances. Like other environment configuration changes, enabling or disabling it changes environmentRevision, invalidates previous environment evidence and prevents preparing runner leases from completing against the old revision. Those attempts need cleanup and a fresh lease.

Cancel and resume

vectis operation cancel <operation-id> --json
vectis operation get <operation-id> --json
vectis environment resume <setup-id> --json

A stopped or failed setup reports action_required and its durable setupId. Partial downloads are retained and verified on continuation. A cancelled or failed guest configuration restarts from the clean downloaded image. Previously completed stages do not require manually recreating keys or directories.

After a service crash, the previous guest must have verifiable process-exit evidence before its disk can be rewritten. Vectis will not kill an unrelated process or assume that a stored PID still belongs to it. The private preparation.log remains in the operation’s reported image directory for diagnosis.

Completed setup registers the environment automatically. It does not connect a repository or prove a GitHub job has run. Continue with Repositories and labels, then Runners and jobs.

New preparations retain the pinned runner under $HOME/.vectis-runner/<version> as the guest user and record it in Environment.image. Jobs use it directly when the recorded version matches. Environment provisioning requires passwordless sudo for Linux identity cleanup and checks it before any runner download or setup script upload. Older images without it fail with provision_requires_guest_sudo; prepare a new Linux environment with this Vectis version, then provision from it. Shutdown uses the hypervisor and does not require guest sudo.