{"id":21803520,"url":"https://github.com/aibor/virtrun","last_synced_at":"2026-02-21T17:05:24.346Z","repository":{"id":176156833,"uuid":"655076157","full_name":"aibor/virtrun","owner":"aibor","description":"Run binaries in a minimal system using QEMU. Supports go tests.","archived":false,"fork":false,"pushed_at":"2026-02-14T23:50:15.000Z","size":77624,"stargazers_count":15,"open_issues_count":1,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-15T06:49:07.017Z","etag":null,"topics":["go","golang","linux","testing","virtualization"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aibor.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-06-17T20:09:49.000Z","updated_at":"2026-02-14T22:16:36.000Z","dependencies_parsed_at":"2024-01-07T14:45:13.383Z","dependency_job_id":"3d9eda26-8e5a-4f52-a90b-d79309a4eb4f","html_url":"https://github.com/aibor/virtrun","commit_stats":null,"previous_names":["aibor/go-pidonetest","aibor/pidonetest"],"tags_count":50,"template":false,"template_full_name":null,"purl":"pkg:github/aibor/virtrun","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aibor%2Fvirtrun","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aibor%2Fvirtrun/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aibor%2Fvirtrun/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aibor%2Fvirtrun/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aibor","download_url":"https://codeload.github.com/aibor/virtrun/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aibor%2Fvirtrun/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29688219,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T15:51:39.154Z","status":"ssl_error","status_checked_at":"2026-02-21T15:49:03.425Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang","linux","testing","virtualization"],"created_at":"2024-11-27T11:44:42.288Z","updated_at":"2026-02-21T17:05:24.341Z","avatar_url":"https://github.com/aibor.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--\nSPDX-FileCopyrightText: 2024 Tobias Böhm \u003ccode@aibor.de\u003e\n\nSPDX-License-Identifier: GPL-3.0-or-later\n--\u003e\n\n# virtrun\n\n[![PkgGoDev][pkg-go-dev-badge]][pkg-go-dev]\n[![Go Report Card][go-report-card-badge]][go-report-card]\n[![Actions][actions-test-badge]][actions-test]\n\nvirtrun allows you to run a binary in an isolated QEMU guest Linux system. It\nsupports running go test binaries via `go test -exec`.\n\n## Quick Start\n\nTo use virtrun as a Go tool for executing tests in your project:\n\n```console\n$ go get -tool github.com/aibor/virtrun@latest\n$ export VIRTRUN_ARGS=\"-kernel=/boot/vmlinuz-linux\"\n$ go test -exec \"go tool virtrun\" ./...\n```\n\n## Supported architectures:\n- amd64 (x86_64)\n- arm64 (aarch64)\n- riscv64\n\n## Requirements\n\n### QEMU\n\nQEMU must be present for the architecture matching the binary. By default, the\nfollowing QEMU binaries are used:\n- `qemu-system-x86_64`\n- `qemu-system-aarch64`\n- `qemu-system-riscv64`\n\nThe architecture of the binary determines which QEMU binary is used. You can\noverride the default choice with the `-qemuBin` flag.\n\n### Linux Kernel\n\nThe kernel must be compiled with support for running as a guest system.\nSpecifically, it must include support for a serial or virtual console. All\nrequired features must be compiled directly into the kernel. Additional kernel\nmodules can be loaded for functionality required by the binary itself using the\n`-addModule` flag.\n\nYou must provide the absolute path to the kernel using the `-kernel` flag.\nEnsure the kernel matches the architecture of your binaries and the QEMU binary.\n\nVirtrun supports different QEMU I/O transport types. The required transport type\ndepends on the kernel and QEMU machine type used. By default, virtrun\nautomatically selects the most likely correct I/O transport. You can manually\nset the transport type using the `-transport` flag:\n- For amd64, `pci` is usually the correct choice.\n- For arm64 and riscv64, `mmio` is typically used.\n- `isa` can be tried as a fallback if there is no output.\n\nThe Ubuntu generic kernels work out of the box and include all necessary\nfeatures.\n\n\n## Installation\n\n### Pre-built binaries\n\nEach release provides pre-built binaries that can be downloaded from the\nrelease page and used directly.\n\n### go install\n\n```console\ngo install github.com/aibor/virtrun@latest\n```\n\n### As go tool\n\nIf virtrun is used for go tests in your project, it can be installed as go tool:\n\n```console\n$ go get -tool github.com/aibor/virtrun@latest\n```\n\n\n## Usage\n\nBy default, virtrun provides a simple init program that sets up the guest system\nand executes the given binary. The binary will be a direct child of PID 1.\n\nAll arguments after the binary are passed to the guest's `/init` program. The\ndefault init program forwards them to the binary.\n\nUsage: `virtrun [flags...] binary [args...]`\n\n### Flags\n\nFlags to virtrun can either be passed directly as arguments, via environment or\nvia local file.\n\n#### Environment Variable\n\nYou can also pass all flags through the `VIRTRUN_ARGS` environment variable:\n\n```console\n$ export VIRTRUN_ARGS=\"-kernel /boot/vmlinuz-linux\"\n$ virtrun /path/to/some/binary_to_run\n```\n\n#### Configuration File\n\nFlags can also be read from a local file named `.virtrun-args`, which should\ncontain one argument per line. Environment variables can be used and will be\nexpanded. Flags from the local file take precedence over flags from the\nenvironment. Note that with `go test`, virtrun's working directory is the\ndirectory of the tested package. This allows you to set individual virtrun flags\nfor a Go package.\n\n```console\n$ cat .virtrun-args\n-kernel=$TEST_KERNELS/vmlinuz\n-addModule=$TEST_KERNELS/veth.ko.zst\n-smp=4\n```\n\n### Standalone Mode\n\nIn Standalone mode, the given binary is executed as `/init` directly. For this\nto work, your binary must perform any necessary system setup. The only essential\nrequired task is to communicate the exit code on stdout and shut down the\nsystem.\n\nThe sub-package [sysinit](https://pkg.go.dev/github.com/aibor/virtrun/sysinit)\nprovides helper functions for these tasks.\n\nYou can build a simple init using `sysinit.Run`, which is the main entry point\nfor an init system. It runs user-provided functions and shuts down the system on\ntermination. For an example, see the \n[simple init program](internal/initramfs/init/cmd/main.go) that is embedded in the\nvirtrun binary and used as the init in the default wrapped mode.\n\nStandalone mode is enabled by using the `-standalone` flag.\n\n### Examples\n\nThe following examples assume virtrun is installed in a directory that is in\n`$PATH`.\n\nLet's use `env` as our main binary to demonstrate simple invocation and default\nenvironment variables:\n\n```console\n$ virtrun -kernel /boot/vmlinuz-linux /usr/bin/env\nHOME=/\nTERM=linux\nPATH=/data\n```\n\nLet's use `ip` to inspect the guest's network stack. The loopback interface is\ninitialized by the init program:\n\n```console\n$ virtrun -kernel /boot/vmlinuz-linux /usr/bin/ip address\n1: lo: \u003cLOOPBACK,UP,LOWER_UP\u003e mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000\n    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n    inet 127.0.0.1/8 scope host lo\n       valid_lft forever preferred_lft forever\n    inet6 ::1/128 scope host proto kernel_lo\n       valid_lft forever preferred_lft forever\n```\n\nYou can add additional files to the guest system using the `-addFile` flag,\nwhich can be specified multiple times. These files are added to the `/data`\ndirectory. It does not preserve any directory structures. If this is needed,\nconsider adding an archive and unpack it in the guest.\n\nThe `PATH` environment variable is set to the `/data` directory, making it easy\nto invoke binaries. Required shared libraries are also collected and added\nto the default library directory.\n\nThe `tree` binary can be used to inspect the guest's file system. Let's add bash\nas an additional file and print the resulting guest file system content:\n\n```console\n$ virtrun -kernel /boot/vmlinuz-linux -addFile /usr/bin/bash /usr/bin/tree -x\n.\n|-- data\n|   `-- bash\n|-- dev\n|-- init\n|-- lib\n|   |-- ld-linux-x86-64.so.2\n|   |-- libc.so.6\n|   |-- libncursesw.so.6\n|   |-- libreadline.so.8\n|   `-- modules\n|-- lib64 -\u003e /lib\n|-- main\n|-- proc\n|-- root\n|-- run\n|-- sys\n|-- tmp\n`-- usr\n    `-- lib -\u003e /lib\n```\n\n### Using with `go test -exec`\n\nVirtrun can be used to run Go tests in a clean and isolated environment. It also\nallows testing for different architectures or kernels. You can use virtrun with\nthe Go test's `-exec` flag by passing the complete virtrun invocation as a\nstring to this flag. Virtrun will be invoked for each test binary.\n\nSince Go test changes into the package directory when running tests, you must\nuse absolute paths for any file paths passed to virtrun via flags (`-kernel`,\n`-addFile`, `qemuBin`, etc.).\n\n#### Installed as go tool\n\n```console\n$ go test -exec \"go tool virtrun -kernel /boot/vmlinuz-linux\" .\n```\n\n#### Installed in `$PATH`\n\n```console\n$ go test -exec \"virtrun -kernel /boot/vmlinuz-linux\" .\n```\n\n#### Not Installed in `$PATH`\n\n```console\n$ go test -exec \"/go/bin/virtrun -kernel /boot/vmlinuz-linux\" .\n```\n\n#### Running Cross-Compiled Tests\n\nThe kernel architecture must match the binary's architecture.\n\n```console\n$ export VIRTRUN_ARGS=\"-kernel /absolute/path/to/vmlinuz-arm64\"\n$ GOARCH=arm64 go test -exec virtrun .\n```\n\n#### Supporting Go Test Flags\n\nVirtrun supports some Go test flags that set output files, such as coverage or\nresource profile files. It uses virtual consoles to write the content from the\nguest system back to the host:\n\n```console\n$ go test -exec virtrun -cover -coverprofile cover.out .\n```\n\n#### Debugging\n\nFor debugging, use virtrun's flags `-verbose` and `-debug` together with Go\ntest's flag `-v`:\n\n```console\n$ go test -exec \"virtrun -verbose -debug\" -v .\n```\n\n\n## Internals\n\n### Exit Code Communication\n\nVirtrun wraps QEMU and runs an init program that executes the binary and\ncommunicates its exit code via a defined formatted string on stdout. This string\nis parsed by virtrun. All other output on stdout is printed directly as-is.\n\n### File Output\n\nFor writing to files on the host (such as Go test profiles), a dedicated virtual\nconsole is established for each file. To accommodate any binary data\ntransmission, the data is written in base64 encoded format to the serial\nconsole.\n\n### Architecture Detection\n\nThe architecture of the main binary determines the default settings. The QEMU\nexecutable, machine type, and transport type are set based on the main binary's\narchitecture unless explicitly specified by flags. KVM is enabled if present,\naccessible, and not explicitly disabled. See `virtrun -help` for all available\nflags.\n\n### Workflow\n\n- **Host**\n  - Determine the architecture from the provided binary.\n  - **Build initramfs**\n    - Select the init program that matches the architecture.\n    - Gather shared libraries required by the binary and any additional files.\n    - Create an initramfs virtual file system containing:\n      - `/init`: The init program.\n      - `/main`: The provided binary.\n      - `/data`: All additional provided files.\n      - `/lib/modules`: Modules in the specified order.\n      - `/lib`: Shared libraries required by the binaries.\n      - `/run`: Runtime directory.\n      - `/tmp`: Temporary files directory.\n    - Write the initramfs into a temporary cpio archive file.\n  - **Prepare QEMU command**\n    - Select the QEMU binary, machine type, and transport type based on the\n      required architecture.\n    - Rewrite go test flags, replacing file paths with serial consoles that the\n      guest writes into. The host forwards the data into the actual files.\n    - Open any optional additional output files.\n    - Set up base 64 encoded serial console output parsers to parse and write\n      any output.\n\n- **Guest**\n  - **Initialize system**\n    - Mount special filesystems.\n    - Load kernel modules.\n    - Initialize the loopback network interface.\n    - Set up output pipes to the host via serial consoles.\n  - Execute the provided binary.\n  - Communicate the binary's exit code.\n  - Shut down the system.\n\n- **Host**\n  - Close any optional additional output files.\n  - Remove the initramfs archive.\n  - Exit with the guest's binary exit code.\n\n```mermaid\n sequenceDiagram\n    actor User\n    box virtrun\n    participant Host\n    participant Guest\n    end\n\n    User-\u003e\u003eHost: Call with binary and kernel\n    Host-\u003e\u003eHost: Create initramfs\n    Host-\u003e\u003eGuest: Launch QEMU with initramfs\n    Guest-\u003e\u003eGuest: Initialize system\n    Guest--\u003e\u003eUser: Start sending stdout/stderr\n    Guest--\u003e\u003eHost: Start sending optional file outputs\n    Guest-\u003e\u003eGuest: Execute provided binary\n    Guest-\u003e\u003eHost: Communicate binary's exit code\n    Guest-\u003e\u003eGuest: Shut down system\n    Host-\u003e\u003eHost: Remove initramfs archive\n    Host-\u003e\u003eUser: Exit with binary's exit code\n```\n\n[pkg-go-dev]:           https://pkg.go.dev/github.com/aibor/virtrun\n[pkg-go-dev-badge]:     https://pkg.go.dev/badge/github.com/aibor/virtrun\n[go-report-card]:       https://goreportcard.com/report/github.com/aibor/virtrun\n[go-report-card-badge]: https://goreportcard.com/badge/github.com/aibor/virtrun\n[actions-test]:         https://github.com/aibor/virtrun/actions/workflows/test.yaml\n[actions-test-badge]:   https://github.com/aibor/virtrun/actions/workflows/test.yaml/badge.svg?branch=main\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faibor%2Fvirtrun","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faibor%2Fvirtrun","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faibor%2Fvirtrun/lists"}