Port a Linux app
Run an existing OCI image on SpaceOS as a Linux service. Describe where it runs
in build.yaml and run.yaml, try it locally, then deploy it to a connected
target.
Linux services run under runc and share the partition’s kernel. The
isolation layers chapter covers
the boundary this gives you and how it compares with a unikernel.
Prerequisites
- An OCI image for your application. Any image that runs under
docker runworks here. - The Space CLI (
space), installed from the Parsimoni tap. Runbrew tap parsimoni-labs/space, thenbrew install space.
Check the image
Run the image with your usual OCI tool first:
docker run --rm ghcr.io/<your-org>/<your-app>:latestSpace CLI resolves service images by OCI reference. Push the image to a registry that both your workstation and your target can reach:
docker push ghcr.io/<your-org>/<your-app>:latestA private registry needs credentials on the target for the reference you name
in build.yaml.
Describe the service
Create a project with a build.yaml that bundles the service image:
schema: space.buildversion: "1"kernel: linuxkit/kernel:6.6.13output: _build/spaceos
partitions: payload: init: pid1 disk: true services: - name: my-app image: ghcr.io/<your-org>/<your-app>:latest runtime: oci isolation: runc healthcheck: type: command argv: ["/bin/sh", "-lc", "test -f /tmp/ready"]build.yaml names what goes into the composition, and it neither picks a
target nor claims target resources.
Then create run.yaml for local execution:
schema: space.runversion: "1"image: _build/spaceos
partitions: payload: memory: 512 apids: [0x010, 0x01F] services: - name: my-app isolation: runc cpus: [0]
relay: from: payload to: [ground]
ground: port: 8080 socket: /tmp/spaceos/ground.sockrun.yaml places the service at runtime: memory,
APID access, relay topology, and the ground
endpoint. Set isolation: runc for a Linux service.
Check both before a build: space sbom decodes the composition, and
space run --dry-run resolves the placement and prints it.
$ space sbom build.yaml | sed -n '1,4p'SpaceOS Software Bill of Materials
kernel linuxkit/kernel:6.6.13 payload pid1$ space run _build/spaceos --runtime run.yaml --dry-run | sed -n '4,7p'partitions: payload 512 MB apids 0x010, 0x01f services: my-app runc cpus 0Run locally
Build and run the composition. The first build creates a development signing identity and prints its fingerprint:
space build . -t my-app:devspace run my-app:dev --runtime run.yamlTo put the workload under the runner, give it a name. The engine starts on first use:
space run --name my-app my-app:dev --runtime run.yamlspace logs my-appspace stop my-appThe runner tracks lifecycle, logs, stats, and status for named workloads.
Deploy to a target
Build a release composition. --release needs a release identity that already
exists: it refuses the development identity, and refuses to create the
profile you name. Generate one first for bench work. Generate a real release
identity where the release is authorized, and name it here.
$ space keys generate releaseGenerated signing identity release ed25519-key: /tmp/space-docs/config/keys/release.ed25519.pem mldsa65-key: /tmp/space-docs/config/keys/release.mldsa65.pem fingerprint: sha256:...space build . -t ghcr.io/<your-org>/my-app:release --release \ --signing-profile release --rollback-index 1Connect the target once, with its
content-addressed profile and its
uplink transport. --endpoint and --cla-address carry the node’s
delay-tolerant network name and the address that reaches
it. Claim
--always-reachable for a bench board on your network. A node reached over
real passes names its orbit with --tle, a
two-line element set, and its ground station with
--station:
$ space target connect sat-42 --profile cm5-dev \> --signing-key ./keys/sat-42.pem \> --endpoint ipn:7.1 --cla-address sat-42.local:4556 --always-reachableGenerated acknowledgement key: /tmp/space-docs/config/keys/target-sat-42-ack.pemtarget/sat-42 connectedInspect the deployment plan before enqueueing it:
space deploy ghcr.io/<your-org>/my-app:release --target sat-42 --planspace deploy ghcr.io/<your-org>/my-app:release --target sat-42space deploy assembles the per-board release and binds the composition to the
target’s board and admission policy. Omit --target when the project declares
a single target.
Unikernel migration criteria
Stay on Linux services while your workload needs a full Linux userland or a large dependency tree. Move to a unikernel when the mission needs:
- A smaller trusted computing base, so less code has to be correct for the system to be secure.
- A faster cold start.
- A stronger isolation boundary between services.