Skip to main content
Run the OSS appliance when you want one machine to own the API, local Supabase, Caddy/static console, and the local microVM runtime. This path is separate from hosted private beta and from the hosted fleet topology.

Support Matrix

Use one Ubuntu 22.04 or 24.04 x86_64 bare-metal host with KVM available. The host needs root or sudo access, Docker Engine with Compose v2, Caddy, and a reflink-capable XFS data filesystem mounted at /var/lib/nullspace/data. Keep runtime state, template-control, snapshots, runtime artifact cache, and active rootfs sources on that same filesystem.
SurfaceOSS v0 support
API/runtime ownershipOne nullspace-api process in single_host mode
Durable stateLocal self-hosted Supabase
ConsoleStatic build served by Caddy at /console or console.<domain>
Preview ingressAPI-compatible Caddy ingress
Operator toolnullspace-host on the appliance host
Release sourceChecksummed release manifest artifacts
Unsupported in OSS v0:
  • multi-host scheduling, autoscaling, HA control planes, and hosted edge
  • apps/edge as a required ingress component
  • managed custom preview domains
  • self-hosted Supabase HA
  • hibernate, resume, and fork launch claims until the runtime compatibility gates for snapshot and guest-agent upgrades are complete

Install From A Release Manifest

If you are installing from a source checkout, use the source installer:
git clone https://github.com/catamaran-research/nullspace.git
cd nullspace
sudo ./install-nullspace.sh
It builds the checkout, installs local Supabase and appliance services, starts them, and runs doctor/status/smoke/launch-gate checks. See docs/product/single-host-setup.md in the source checkout for storage, owned-domain, and rerun options. For packaged releases, download or mount a release bundle that contains manifest.json and the nullspace-host operator tool. Install the operator tool first, then converge the appliance from the manifest:
sudo install -m 0755 /mnt/nullspace-release/nullspace-host /usr/local/bin/nullspace-host

sudo nullspace-host install \
  --manifest /mnt/nullspace-release/manifest.json \
  --operator-email you@example.com \
  --evidence-dir /var/lib/nullspace/evidence/install
install renders /etc/nullspace/env, generates local Supabase secrets, renders the Supabase Compose and Caddy configs, applies migrations, seeds the first DB-backed operator API key, fetches and verifies release artifacts, and writes redacted evidence. The first API key is written once on the host:
sudo cat /etc/nullspace/operator-api-key
Keep /etc/nullspace/operator-api-key, /etc/nullspace/env, and /etc/nullspace/supabase.env root-readable only. Service-role keys stay on the server and are not part of the static console build. Enable services after the install has rendered configs and fetched artifacts:
sudo systemctl enable --now nullspace-supabase.service
sudo systemctl enable --now nullspace-api.service
sudo systemctl enable --now caddy.service

Check The Host

Run non-mutating diagnostics first:
sudo nullspace-host doctor --json \
  --evidence-dir /var/lib/nullspace/evidence/doctor

sudo nullspace-host status --json \
  --evidence-dir /var/lib/nullspace/evidence/status
doctor checks host, service, Supabase, artifacts, storage, network, ingress, and Firecracker hardening facts. status reports service liveness, API readiness/degraded state, worker status, runtime capacity, storage pressure, and ingress state. Collect bounded redacted logs when a check fails:
sudo nullspace-host logs --json \
  --evidence-dir /var/lib/nullspace/evidence/logs

Connect With SDK Or CLI

The localhost/no-domain appliance serves API traffic through Caddy on the same origin as the console:
export NULLSPACE_API_KEY="$(sudo cat /etc/nullspace/operator-api-key)"
export NULLSPACE_API_URL=http://localhost

python -m pip install "nullspace-sdk[cli]"
nullspace doctor
Run a sandbox from Python:
from nullspace import Sandbox

with Sandbox.create(template="base") as sandbox:
    result = sandbox.commands.run("echo self-hosted", shell=True)
    print(result.stdout.strip())
Open the static console at:
http://localhost/console
Use the same API key from /etc/nullspace/operator-api-key on the console sign in page.

Preview URLs

Localhost/no-domain mode leaves NULLSPACE_PUBLIC_HOSTNAME unset. It is for private operator-only use and does not issue signed public preview hostnames. Use direct local mappings from the SDK or CLI:
info = sandbox.get_host_info(8080)
print(info.host)
Owned-domain mode requires a DNS name you control, wildcard DNS pointing preview subdomains at the host, Caddy TLS for the apex/console hosts, and these server-side env values in /etc/nullspace/env:
NULLSPACE_PUBLIC_HOSTNAME=nullspace.example
NULLSPACE_EDGE_PUBLIC_BASE_URL=https://nullspace.example
NULLSPACE_EDGE_ACCESS_TOKEN_SIGNING_KEY=replace_with_32_plus_bytes_random_secret
After rerendering ingress with sudo nullspace-host install --repair-env --manifest /mnt/nullspace-release/manifest.json, SDK helpers return signed https://{PORT}-{SANDBOX_ID}.nullspace.example/... and matching wss:// preview URLs through API-compatible ingress. Caddy obtains exact-host preview certificates on demand after asking the loopback-only API TLS gate; a wildcard certificate is not required for OSS single-host mode. See Preview URLs and WebSockets for client behavior.

Smoke And Launch Gate

Run the fast Caddy fixture smoke without creating a microVM:
sudo nullspace-host smoke --ingress-fixture --json \
  --evidence-dir /var/lib/nullspace/evidence/smoke-ingress
Run the live sandbox smoke after doctor/status pass:
sudo nullspace-host smoke --live --json \
  --api-key-file /etc/nullspace/operator-api-key \
  --evidence-dir /var/lib/nullspace/evidence/smoke-live
Before making public launch claims for a release, run the launch gate on a fresh supported host. Mutating install, restart, reboot, live upgrade, and failure-injection stages require the exact confirmation phrase run nullspace launch gate.
sudo nullspace-host launch-gate \
  --mode localhost \
  --manifest /mnt/nullspace-release/manifest.json \
  --console-url http://localhost/console/ \
  --restart-api \
  --restart-supabase \
  --reboot \
  --confirm "run nullspace launch gate" \
  --evidence-dir /var/lib/nullspace/evidence/launch-gate
After the host returns from reboot, resume the same run:
sudo nullspace-host launch-gate \
  --after-reboot \
  --mode localhost \
  --evidence-dir /var/lib/nullspace/evidence/launch-gate
For owned-domain evidence, use --mode owned-domain and --public-hostname nullspace.example.

Back Up, Restore, Upgrade, Roll Back

Create a protected appliance backup archive:
BACKUP=/var/lib/nullspace/backups/nullspace-$(date +%Y%m%d).tar.gz

sudo nullspace-host backup \
  --output "$BACKUP" \
  --evidence-dir /var/lib/nullspace/evidence/backup
Rehearse restore safety before extracting:
sudo nullspace-host restore \
  --input "$BACKUP" \
  --dry-run \
  --evidence-dir /var/lib/nullspace/evidence/restore-dry-run
Mutating restore requires:
sudo nullspace-host restore \
  --input "$BACKUP" \
  --confirm "restore nullspace appliance"
Validate an upgrade manifest before switching releases:
sudo nullspace-host upgrade \
  --manifest /mnt/nullspace-release-next/manifest.json \
  --dry-run \
  --evidence-dir /var/lib/nullspace/evidence/upgrade-dry-run
Apply the upgrade, then run status and smoke:
sudo nullspace-host upgrade \
  --manifest /mnt/nullspace-release-next/manifest.json \
  --evidence-dir /var/lib/nullspace/evidence/upgrade

sudo nullspace-host status --json
sudo nullspace-host smoke --ingress-fixture --json
If the recorded upgrade state supports returning to the previous release:
sudo nullspace-host rollback --json \
  --evidence-dir /var/lib/nullspace/evidence/rollback
Database migrations are not automatically rolled back. Treat DB rollback as a manual operator boundary and restore from a verified backup when a migration must be reversed.

Troubleshooting

SymptomWhat to check
doctor reports KVM or CPU virtualization failuresConfirm the host is x86_64 bare metal with KVM enabled and that the service user can use /dev/kvm.
Storage falls back to byte copyPut NULLSPACE_DATA_DIR, active rootfs sources, template-control, snapshots, and runtime artifact cache on the same reflink-capable XFS filesystem.
Console opens but API calls failCheck sudo nullspace-host status --json, Caddy status, and that the console build uses VITE_NULLSPACE_API_BASE=same-origin for /console mode.
Localhost preview URL is missing signed URL fieldsThis is expected when NULLSPACE_PUBLIC_HOSTNAME is unset. Use direct get_host_info() mappings or configure owned-domain mode.
Owned-domain preview URLs are not issuedConfirm NULLSPACE_PUBLIC_HOSTNAME, NULLSPACE_EDGE_PUBLIC_BASE_URL, and NULLSPACE_EDGE_ACCESS_TOKEN_SIGNING_KEY are set, then rerender with sudo nullspace-host install --repair-env.
Backup warns about missing Supabase dumpConfirm Docker Compose v2 is installed and the local Supabase stack is running, or use the packaged Supabase volumes with the warning recorded in evidence.

Uninstall

Remove installed surfaces while preserving appliance data:
sudo nullspace-host uninstall \
  --preserve-data \
  --confirm "uninstall nullspace preserve data"
Destroy config, data, Supabase state, and evidence only after taking and verifying a backup:
sudo nullspace-host uninstall \
  --destroy-data \
  --confirm "uninstall nullspace destroy data"