A Pulumi provider for building and deploying unikernel images using NanoVMs.
Looking to use this provider? See the complete documentation for:
- Installation instructions for all languages (TypeScript, Python, Go, C#)
- Usage examples and code samples
- Configuration guide
- API reference
Important: Until this provider is officially published to the Pulumi Registry, you need to install the provider plugin binary separately from the language SDK.
# Quick install - downloads and installs the plugin for your platform
# Download install_plugin.sh
curl -sL https://raw.githubusercontent.com/tpjg/pulumi-nanovms/main/install_plugin.sh -o install_plugin.sh
chmod +x install_plugin.sh
./install_plugin.sh
# Or manually specify a version
./install_plugin.sh 0.1.5-rc1The script automatically detects your platform (macOS/Linux, amd64/arm64) and installs the plugin to ~/.pulumi/plugins/.
# Node.js/TypeScript
bun install @tpjg/nanovms
# Python
pip install tpjg-pulumi-nanovms
# Go
go get github.com/tpjg/pulumi-nanovms/sdk/go/pulumi-nanovms
# .NET
dotnet add package Tpjg.PulumiNanovmsThis section is for those who want to contribute to or modify the provider itself.
pulumi-nanovms/
├── provider/ # Provider source code (Go)
│ ├── main.go # Entry point, provider setup
│ ├── image.go # Image resource implementation
│ ├── instance.go # Instance resource implementation
│ ├── utils.go # Helper utilities
│ ├── schema.json # Generated Pulumi schema
│ ├── build-sdk.sh # SDK generation script
│ └── go.mod # Go dependencies
├── sdk/ # Generated SDKs (auto-generated, do not edit)
│ ├── nodejs/ # TypeScript/JavaScript SDK
│ ├── python/ # Python SDK
│ ├── dotnet/ # .NET SDK
│ └── go/ # Go SDK
├── examples/ # Example Pulumi programs
│ ├── nodejs/ # TypeScript examples
│ ├── go/ # Go examples
│ └── application/ # Sample applications to deploy
├── tests/
│ └── integration/ # Integration test Pulumi program
├── docs/ # Provider documentation
│ ├── _index.md # Main documentation page
│ └── installation-configuration.md
├── .github/
│ └── workflows/ # CI/CD pipelines
│ ├── test.yml # CI workflow (PRs and commits)
│ ├── release.yml # Release workflow (tags)
│ └── integration-test.yml # Integration tests (manual trigger)
├── test-locally.sh # Local CI testing script
└── test-integration-local.sh # Local integration testing script
-
Go 1.25+ - Provider is written in Go
go version # Should be 1.25 or later -
Pulumi CLI - For schema generation and SDK building
pulumi version
-
NanoVMs ops CLI - The underlying tool for building unikernels, includes qemu (and its tools such as
qemu-img)brew install nanovms/ops/ops # macOS # or curl https://ops.city/get.sh -sSfL | sh # Linux
-
Language SDKs (for building and testing SDKs):
- Bun (or Node.js 18+)
- Python 3.11+
- .NET 6.0+ (optional)
The version will be automatically extracted from git tags: The provider generates SDKs for 4 languages using Pulumi's code generation:
cd provider
./build-sdk.shThis script:
- Builds the provider binary
- Generates the Pulumi schema (
schema.json) - Generates SDKs for all languages
- Initializes the Go SDK module
Important: The sdk/ directory is auto-generated. Don't edit SDK files directly - modify the provider source instead.
-
Make changes to provider code in
provider/ -
Rebuild and regenerate SDKs:
cd provider ./build-sdk.sh -
Test with an example:
cd examples/nodejs bun install bun run build # Review the TypeScript compilation for errors
Test the provider with a real Pulumi program:
cd examples/nodejs
bun install
export DO_TOKEN="your-token" # the example is using DigitalOcean
pulumi stack init dev
pulumi upRun the test suite locally:
./test-locally.shThis validates:
- Provider builds successfully
- Schema generation works
- All 4 SDKs generate and compile
- Example projects build
Integration tests verify the full end-to-end workflow by actually building and deploying a unikernel.
Run integration tests locally:
./test-integration-local.shThis comprehensive test:
- Builds a sample application (
examples/application/example) - Builds the provider and generates SDKs
- Runs the integration test Pulumi program (
tests/integration) - Creates a unikernel image and deploys it with QEMU
- Tests that the HTTP server responds correctly
- Cleans up all resources
The test program: tests/integration/ contains a Pulumi program similar to the examples but configured for local testing with the onprem provider.
Prerequisites for integration tests:
- ops CLI installed (
brew install nanovms/ops/ops) - QEMU installed (included with ops)
- Pulumi CLI
- disk space for kernel and images
Why not in CI?
Integration tests require QEMU with KVM acceleration for reasonable performance. GitHub's free ubuntu-latest runners don't support hardware virtualization, which would make tests extremely slow.
Integration tests run locally where KVM/HVF acceleration is available.
Running integration tests in CI:
A manual GitHub Actions workflow is available for integration testing:
- Go to the repository on GitHub
- Click Actions → Integration Test
- Click Run workflow
- Wait ... (slow due to software-emulated QEMU)
This workflow runs the same test as ./test-integration-local.sh but in GitHub's CI environment without hardware acceleration. Use it for:
- Pre-release validation
- Verifying changes to the provider core
- Testing before major releases
- Create a new file in
provider/(e.g.,volume.go) - Implement the resource following the pattern in
image.goorinstance.go - Register it in
main.go:return infer.NewProviderBuilder(). WithResources( infer.Resource(&Image{}), infer.Resource(&Instance{}), infer.Resource(&Volume{}), // New resource ). Build()
- Rebuild and regenerate SDKs:
./build-sdk.sh
- Edit the resource file (e.g.,
provider/image.go) - Update input/output types and methods
- Rebuild:
cd provider && go build - Regenerate SDKs:
./build-sdk.sh - Test with examples
Versions are managed through git tags:
- Development: Uses
0.1.0or git tag if available - CI/CD: Automatically extracts version from git tags
The version is injected at build time into:
- Provider binary (
main.Version) - Schema (
schema.json) - All SDK packages
-
Ensure everything is committed and pushed:
git status # Should be clean -
Tag the release:
git tag v0.2.0 git push --tags
-
GitHub Actions automatically:
- Builds provider binaries for all supported platforms (Linux, macOS × amd64/arm64)
- Note that Windows binaries are not supported yet, due to limitations in the 'ops' source code, especially related to onprem and qemu.
- Generates all SDKs with the tagged version
- Creates a GitHub release with binaries attached
-
Monitor the release:
- Go to GitHub → Actions → Release workflow
- Check for any failures
- Verify GitHub Release is created
Publishing to package registries requires secrets to be configured:
-
Add secrets to GitHub repository (Settings → Secrets → Actions):
NPM_TOKEN- npm access tokenPYPI_TOKEN- PyPI API tokenNUGET_API_KEY- NuGet API key
-
Enable publishing in
.github/workflows/release.yml:- Uncomment the
bun publish,twine upload, anddotnet nuget pushlines
- Uncomment the
-
Tag and release as normal - SDKs will be published automatically
- Triggers: Push to main, Pull Requests
- Purpose: Validates builds and SDKs
- Runs: Provider build, SDK generation, example compilation
- Triggers: Git tags matching
v*.*.* - Purpose: Creates releases and publishes SDKs
- Jobs:
build-provider- Multi-platform binary buildsgenerate-sdks- SDK generation and publishingcreate-release- GitHub release creation
The provider uses a custom fork of the NanoVMs ops library:
replace github.com/nanovms/ops => github.com/tpjg/opsThis fork includes modifications needed for the Pulumi provider integration to properly detach background processes. A PR to the upstream ops library is submitted to merge these changes.
- Follow standard Go conventions (
gofmt) - Use the
pulumi-go-providerframework patterns, see Pulumi Provider SDK - Keep resources focused and single-purpose
SDK generation fails:
- Ensure Pulumi CLI is installed and up to date
- Check that the provider binary builds successfully
- Verify
schema.jsonis generated correctly
Provider build errors:
- Check Go version (must be 1.25+)
- Run
go mod tidyinprovider/ - Verify the ops fork is accessible
Example projects fail:
- Rebuild SDKs:
cd provider && ./build-sdk.sh - Reinstall dependencies in example:
cd examples/nodejs && bun install - Check that binary paths in examples are correct
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests (
./test-locally.sh) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
See LICENSE file for details.
- Open an issue on GitHub
- Check existing documentation in
docs/