Skip to content

Core concepts ​

SpinUP has three primary domain objects. Understanding how they relate makes everything else in the platform obvious.

Application ​

An Application is the deployable unit. It maps 1:1 to a Spin app, which maps 1:1 to a K8s pod.

An Application has:

  • name — DNS-1123 label. Used as the SpinApp CR name, the OCI image name, the K8s Service name.
  • language — one of go, js, ts, rust. Determines which builder image runs.
  • N functions — the HTTP handlers packed into this Application.

Applications are the packing unit: N functions in one Application share one pod, one wasmtime process, and one OCI image.

Function ​

A Function is one HTTP trigger inside an Application. It has:

  • name — DNS-1123 label, unique within the Application.
  • route — Spin route pattern. Wildcard (/...), prefix (/api/...), or exact (/health).
  • source — a JSON blob of {filename: content} pairs. Edited in the Monaco editor, or uploaded as .tar.gz.

When you create an Application, SpinUP auto-creates a first Function with the same name. Multi-function apps add more via POST /api/v1/applications/{id}/functions.

Why the extra layer?

Spin apps already support multiple HTTP triggers. The reason to expose "Application" separately is so the UI can present N functions grouped under one deployable, and so the platform can reason about scheduling (one pod per Application, not per Function). This shipped as Option A on the scaling roadmap.

Build ​

A Build is one attempt to compile + push an Application. It's a row in the builds table plus a K8s Batch Job that materializes it.

Lifecycle:

  1. pending — created, Job not yet scheduled
  2. running — Job's pod is executing spin build + spin registry push
  3. succeeded — image pushed, SpinApp CR patched
  4. failed — Job hit its backoff limit, error text captured

The build ID (a UUID without dashes) is also the OCI image tag: registry/spinup/{app-name}:{buildId}. That gives us immutable, content-addressable deploys.

Only successful builds produce an image. Failed builds retain their pod logs for inspection.

How Applications run ​

Each Application's build produces a SpinApp custom resource. spin-operator translates that into a Deployment + Service. Pods use runtimeClassName: wasmtime-spin-v2 so the containerd-shim-spin runs the WASM component directly (no Node/JVM in between).

Properties:

  • One pod per Application, always running (subject to spec.replicas, default 1)
  • Standard K8s isolation — pod boundaries, ResourceQuotas, NetworkPolicies all apply
  • CPU/memory limits set per-pod via SpinApp.spec.resources
  • Horizontal auto-scaling supported via SpinKube's HPA (SpinApp.spec.enableAutoscaling)
  • Deploy = spin-operator rolls the Deployment; old pod terminates once new pod is Ready

Data model at a glance ​

tenants
   └── applications
         ├── language          (go | js | ts | rust)
         ├── functions[]
         │     ├── route
         │     └── source (files JSON)
         └── builds[]
               ├── status
               ├── imageRef
               └── error

See Database schema for the full column list.

What's not in the model (yet) ​

  • Environments (dev/staging/prod) — one control plane per cluster today.
  • Secrets — plaintext env vars on the SpinApp CR only; a proper secrets integration is queued.
  • Non-HTTP triggers (Redis, cron) — Spin supports them; SpinUP doesn't expose them yet.
  • Version pinning — the latest successful build is always deployed. Manual rollback works by deleting the newer build.
  • Scale to zero + dense packing — see the scaling roadmap.

Alpha — API and chart values may change.