nanovms/ops: Building and Running Nanos Unikernels
ops - build and run nanos unikernels
At a glance
- What is it?
- nanovms/ops is a Go CLI for packaging applications into Nanos unikernel images, running them locally, and sending them to supported cloud providers. It suits teams that want a smaller application-specific operating-system image, but its architecture, platform, and networking choices require careful validation.
- Who is it for?
- Adopt nanovms/ops when the application genuinely benefits from a Nanos unikernel and the team can accept provider and architecture constraints. Do not select it for an ordinary Linux deployment that requires a conventional kernel or portable container artifact.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 24 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Packaging Applications as Nanos Unikernel Instances
nanovms/ops targets a specific deployment model: an application and the Nanos operating-system code are packaged into a unikernel instance rather than placed into a conventional container image with a shared userspace kernel. The project describes itself as a tool for creating and running Nanos unikernels, while the repository layout shows Go packages for filesystems, virtualization, networking, providers, protocols, and command handling.
The intended audience includes developers experimenting with edge computing, small service deployments, isolated server workloads, or Nanos itself. Ops also offers a path to cloud deployment across Azure, AWS, Google Cloud, DigitalOcean, IBM, Linode, Oracle Cloud, Vultr, UpCloud, and other provider directories visible in the repository. The README presents this as an alternative to managing Kubernetes-style orchestration for supported cloud targets, although it does not claim that Ops is a general replacement for an application platform.
This distinction matters. Ops does not turn a regular Linux process into a portable artifact by itself. Its output is tied to Nanos and to the architecture selected for that image. Apple M1 and M2 machines can run native ARM ELF applications, but the README warns that images intended for x86 servers must be rebuilt as x86 instead of using the default arm64 image.
The Ops CLI and Nanos Package Flow
The documented first-use path starts with a language runtime package and an application file. In the JavaScript example, the command loads `eyberg/node:v16.5.0`, passes the local `hi.js` file, enables networking, and maps port 8083. The runtime package is placed inside the unikernel, so the host does not need a local Node installation.
That package is the mechanism behind the README's statement that Ops can package, create, and run an application as a Nanos instance. Public packages can be browsed through `repo.ops.city` or listed with `ops pkg list`. Ops also has separate command families for building a bootable image, running an application, creating an instance, and interacting with cloud providers. The repository's `provider/`, `protos/`, and `cmd/` directories correspond to those cloud, remote-service, and CLI concerns.
Local development defaults to user-mode networking, which the README recommends for initial experiments. Ops can also run a local daemon, but that mode is different. The daemon expects elevated privileges through the suid bit so multiple programs can communicate over a class C network. It is not required for `ops run`, `ops pkg load`, or `ops instance create`, and the README says the daemon and `ops instance create` currently share metadata even though that arrangement is expected to change.
Installing Ops and Running an HTTP Server
The README recommends the binary installer for most users. This command downloads and runs the installation script published at `ops.city`:
curl https://ops.city/get.sh -sSfL | shHomebrew is another documented route on macOS. Tap the repository, then install the formula:
brew tap nanovms/homebrew-opsAfter the tap is registered, install Ops with:
brew install nanovms/ops/opsFor the first local workload, create `hi.js` as shown in the README. The server binds to `0.0.0.0` on port 8083 and returns plain text:
const http = require('http');
http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
}).listen(8083, "0.0.0.0");
console.log('Server running at http://127.0.0.1:8083/');Package and run that file with the documented command:
ops pkg load eyberg/node:v16.5.0 -p 8083 -n -a hi.jsThe README identifies the resulting endpoint as `http://127.0.0.1:8083/`. Its expected response is `Hello World` followed by a newline. The `-n` option appears in the networking example, while `-a` associates `hi.js` with the loaded package. The README does not provide a full flag reference here, so their complete semantics should be checked in Ops documentation before adapting the command to a production configuration.
Config Files, Environment Expansion, and Build Commands
Ops accepts a JSON config file for arguments and directories. The README documents `ops run -p <port> -c <file> <app>` as the config-based run form. This makes it possible to keep process arguments and local paths outside the package command, although the supplied documentation does not describe config inheritance, validation, or merge precedence.
Environment interpolation is disabled unless `ops_render_config` is set to `true`. Once enabled, both `${ENV_VAR}` and `$ENV_VAR` forms are supported. The documented invocation can set the variable for a single command:
ops_render_config=true ops run -p <port> -c <file> <app>It can also be exported for the current shell:
export ops_render_config=trueA minimal config contains arguments and directories:
{
"Args":["one","two"],
"Dirs":["myapp/static"]
}The README also shows environment-backed values. String expansion applies to configuration values, not to arbitrary application behavior:
{
"Args":[
"--user",
"${USER}",
"--password",
"$PASSWORD"
],
"Dirs":["myapp/static"]
}Because this mechanism can insert secret values into rendered arguments, users should inspect how generated images or logs handle those values. The supplied documentation does not state whether rendered values are redacted or persisted. The top-level `ops build <app>` command produces a bootable image, while `ops run <app>` is the shorter local execution path.
Cloud Deployment Compared with Local Ops Execution
The practical alternative documented by Ops is a split between local execution and native cloud builds. Local Ops packages and runs the unikernel on the developer's machine, normally through the daemon-less CLI and user-mode networking during initial development. The README recommends cloud builds for production users and directs them to provider-specific documentation.
Cloud execution changes the responsibility boundary. Instead of asking the developer to operate the local runtime or a Kubernetes cluster for the covered path, Ops sends the deployment to the selected cloud provider and lets that provider handle orchestration. The repository contains provider implementations and dependencies for multiple public clouds, but the README excerpt does not document all region selection, image retention, networking, instance sizing, or rollback behavior.
This is not equivalent to claiming that Ops removes every operational task. Credentials, target regions, provider quotas, storage, security groups, instance lifecycle, and image distribution still need to be understood. The documented benefit is a different deployment path: the application remains packaged as a Nanos unikernel, while the selected cloud handles the infrastructure layer. A conventional container workflow would instead leave the Linux kernel outside the artifact and depend on a container runtime and its own orchestrator.
Daemon Privileges, Architecture Constraints, and Failure Cases
Ops is a poor fit when local daemon privileges are unacceptable, application-specific kernels add no value, or the target must run on an ordinary Linux host without Nanos. The daemon requires elevated privileges via the suid bit for its multi-service class C network arrangement. The README says this is unnecessary for single-service commands, so teams should not enable the daemon merely to run `ops run`, `ops pkg load`, or `ops instance create`.
Architecture compatibility is another hard boundary. The README says Ops is built primarily for x86 servers. Native ARM applications run on M1 and M2 systems, but an image built for those machines defaults to arm64. Deploying that image to x86 servers requires rebuilding for x86. Copying an image across incompatible architectures will therefore fail at the image or execution boundary rather than becoming a transparent portability feature.
There is also a documentation inconsistency to resolve before building from source. The README says the program requires Go 1.25.x or greater, while `go.mod` declares `go 1.26.0`. The documented source build installs module dependencies and then builds the `ops` binary:
make depsmake buildThe repository should be checked for its effective Go requirement before relying on source installation. The Makefile also exposes `make test`, but the README excerpt does not describe prerequisites for virtualization, networking, or provider-specific tests. The project is therefore not a self-contained command-line build in every environment.
Release Cadence, Dependencies, and MIT Licensing
The repository is not archived, and its last push was on 2026-09-29. Recent releases include 0.1.47 on 2026-09-22, 0.1.46 on 2026-03-22, and 0.1.45 on 2025-12-19. That activity indicates ongoing code movement, but the version numbers remain below 1.0, so production users should expect their upgrade cost to include checking CLI behavior, configuration handling, provider integration, and image compatibility.
The dependency surface is broad. The visible `go.mod` includes SDKs and clients for Azure, AWS, Google Cloud storage, DigitalOcean, IBM-related infrastructure, Linode, Oracle Cloud, UpCloud, Docker, VirtualBox, gRPC, and other components. Cloud-provider upgrades can therefore affect more than the packaging path. The Makefile supports protobuf and gRPC generation through Buf, while generated files are cleaned from the protocol directories by `make clean`.
The repository is licensed under MIT. That permits use, modification, distribution, and sublicensing subject to the license terms, but the MIT label does not settle cloud-provider terms, runtime package licenses, bundled Nanos components, image contents, or compliance duties. Review the `LICENSE`, `PACKAGES.md`, and the licenses of packages loaded into each image separately. Ops can reduce host operating-system surface in a deployment, but it does not make the application, package, or cloud account license-free.
Editorial conclusion
Adopt nanovms/ops when the application genuinely benefits from a Nanos unikernel and the team can accept provider and architecture constraints. Do not select it for an ordinary Linux deployment that requires a conventional kernel or portable container artifact. Verify the exact Nanos image architecture, package licenses, source-build Go version, cloud networking requirements, and config rendering behavior before deploying.
Frequently asked questions
How do I install nanovms/ops?
The README recommends `curl https://ops.city/get.sh -sSfL | sh` for most users. It also documents Homebrew, package downloads for macOS and Windows, and a source build.
How do I run a first Nanos unikernel with nanovms/ops?
Create a local application file, then use a documented `ops pkg load` command that names a package, maps a port, enables networking, and associates the application. The README's Node example uses `ops pkg load eyberg/node:v16.5.0 -p 8083 -n -a hi.js`.
Does nanovms/ops require a local daemon or Kubernetes?
The daemon is optional for `ops run`, `ops pkg load`, and `ops instance create`. For supported cloud deployments, the README says Ops delegates orchestration to the chosen cloud provider instead of requiring a complex orchestration setup such as Kubernetes.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/nanovms-ops)Was this useful?