Reaktor DevOps — build, deploy, and operate the graph

Operations direction, 1 October 2026. Operations & Reliability owns monitoring, profiling, load and Shadow verification. Release gates bind to actual artifact vectors and required measurements; Delivery gates defines the evidence. Dev/prod are trust tiers, and isolated Shadow workspaces are dev instances. Later generic staging/preview examples are industry background, not a requirement to add another Reaktor environment tier.

Every team eventually builds the same machine by hand: a way to take code, turn it into running infrastructure across several clouds, ship changes safely many times a day, see what's deployed where, and fix it when it breaks. The industry has good, battle-tested parts for each step — but they are separate parts, stitched together with YAML and shell, and none of them understands your application as a whole. This document is reaktor's answer: the build/deploy/operate plane, designed around one idea — deployment is an operation on the system graph, not a pile of disconnected config.

It is written in four movements: (1) what teams genuinely need from devops; (2) the industry-proven solutions and the lessons from the teams who pioneered them; (3) what those tools still get wrong; and (4) how reaktor's graph-native approach addresses it — with an honest account of what is real today versus designed.

  • Deployment is a graph operation — the app is authored once in Common/KMP; reaktor-devops partitions that one graph across the five runtime targets and ships each via its own path.
  • One typed platform model — a Kotlin Platform declaration generates the Pulumi / wrangler / k8s / Dagger config; you refactor a type, not hand-edit a thousand YAML files.
  • Pipeline-as-code, not YAML — CI/CD is Dagger functions that run identically on a laptop and in CI.
  • Derived and reconciled — what you declared and what's actually running are compared continuously; the gap (drift) is shown, not hidden.
  • Safe self-service — every devops verb is a permission-gated, blast-radius-aware, audited operation on a node, runnable by a person or an agent.

1. What teams actually need from devops

Strip away the tools and devops is a handful of recurring jobs. Naming them is what makes the tool choices — and reaktor's design — legible rather than fashionable.

NeedThe question being askedreaktor's home for it
Provision infrastructure"create the clusters, databases, buckets, topics — repeatably, reviewably"Pulumi materializers from the Kotlin platform model (§5.1)
Manage fleet capacity"give this customer more capacity in this location, or attach their own GCP cluster"GCP/k3s cell operations and reaktor-agent (§5.6)
Build & test"turn source into artifacts and prove they work, fast and reproducibly"Dagger pipeline-as-code (§5.3)
Deploy"ship a change to the right places, in the right order, safely"deployment-as-graph-partition (§5.2)
Environments"give every change a place to run — dev, staging, prod, and a throwaway preview"environments + ephemeral/shadow (§5.4)
Release safely"roll out gradually, watch it, and roll back in one click"progressive delivery + rollback (§5.4)
Secrets"distribute credentials without ever committing them"secrets-management
Observe"know it's healthy; get paged before users do"performance & observability
See & own everything"what exists, who owns it, what's it cost, what depends on it"the system graph + Desktop (§5.6)
Operate"restart, scale, migrate, drain — without SSH and tribal knowledge"gated graph operations (§5.5)
Self-serve"let product devs ship without becoming infra experts"golden paths as graph templates (§4, §5.6)

Two structural truths shape the rest. First, these jobs split into an inner loop (a developer editing and running code locally — must be fast and tight) and an outer loop (committing, building, deploying, operating — must be safe and observable); a good platform makes them feel like one continuum, not two worlds. Second, the modern bar is measured, not vibes-based: the DORA metrics — deployment frequency, change lead time, change-failure rate, and time-to-restore — are how a team knows its devops is actually good.


2. The landscape — what the industry built, and what it learned

The good news for reaktor is that almost every piece here is solved and proven somewhere. The job is to learn from each, not to reinvent it.

Infrastructure as code — declare infra, don't click it. Terraform (and its fork OpenTofu) made "infrastructure in version control, planned and applied" the default; Pulumi (reaktor's choice) does the same but in real programming languages (TypeScript, Go, Java/Kotlin) instead of a bespoke config language, so you get types, loops, and tests; AWS CDK is the same idea for CloudFormation. Crossplane takes a further step — it runs infra as a continuous control loop inside Kubernetes that constantly reconciles declared-vs-actual, rather than applying once on CI. Lesson: declarative + version-controlled + in a real language is the floor; continuous reconciliation (Crossplane, GitOps) is what keeps it true over time.

CI/CD — turn commits into running software. GitHub Actions / GitLab CI / Jenkins / CircleCI are the workhorses, all fundamentally YAML pipelines. Dagger (reaktor's choice) is the newer idea: pipelines written as code (typed functions in your own language), executed in containers, content-addressed and cached, so the same pipeline runs identically on a laptop and in CI — killing "works on my machine" and "push-and-pray to debug the YAML." Lesson: pipeline-as-code that runs identically everywhere beats YAML you can only test by pushing.

GitOps & continuous delivery — git as the source of truth. Argo CD and Flux pioneered "the desired state lives in git; a controller continuously reconciles the cluster to match it" (Weaveworks coined GitOps). Netflix's Spinnaker pioneered multi-cloud continuous delivery at scale; Argo Rollouts / Flagger add progressive delivery (canary, blue-green). Lesson: a reconciling controller — not a fire-and-forget deploy script — is how you get drift detection, automatic healing, and a clear audit trail.

Orchestration — where the workloads run. Kubernetes won; k3s (reaktor's choice) is the lightweight, single-binary distribution ideal for small clusters and the edge; Nomad is the simpler alternative. Lesson: k8s is the substrate, but raw k8s is too much surface for product developers to hold — which is the whole reason the next category exists.

Platform engineering & internal developer platforms (IDPs) — hide the complexity behind a paved road. This is the most important category for reaktor, because it is the same ambition. Spotify Backstage is the canonical developer portal: a software catalog of components, systems, APIs, and resources, with ownership, docs, and "software templates" that scaffold a new service the blessed way. Humanitec, Port, Cortex, and OpsLevel are platform orchestrators / catalogs in the same space; Score is a workload spec; Lyft's Clutch is an internal ops UI. The movement behind them — Team Topologies' "platform as a product" and the idea of a paved road / golden path — says: the platform team's job is to reduce the cognitive load on product teams by offering a well-lit default path, not to gatekeep. Lesson: the winning shape is a catalog + ownership + golden-path templates + self-service operations — a product for your own developers.

The developer-experience bar — what "good" feels like. Heroku set it two decades ago with git push heroku and the 12-factor app (config in the environment, stateless processes, logs as streams); Vercel and Netlify set the modern bar with git-push → automatic preview deployment for every pull request; Fly.io, Render, and Railway chase the same "deploy with one command" feel; Cloudflare (reaktor's edge) offers wrangler deploy and Workers-for-Platforms. Lesson: the gold standard is "I changed code, and a working, isolated environment appeared — with no infra ceremony."

Case studies worth internalizing. Netflix — the "paved road": a supported, opinionated default path teams are free to leave (but then own the consequences), plus Spinnaker for delivery. Spotify — golden paths + Backstage, born from the pain of hundreds of teams each doing devops differently. Google — Borg → Kubernetes, and SRE (error budgets, SLOs, toil reduction) as the operating discipline; DORA (from Google's research) as the measurement. Amazon — "you build it, you run it" (Werner Vogels): the team that writes a service operates it, which only works if the platform makes operating easy. The common thread across all of them: invest in a platform so product teams move fast safely — exactly reaktor's premise, one layer deeper.


3. What today's tools still get wrong

Each tool above is good at its slice. The pain is in the seams — and the seams are where reaktor lives.

  1. Fragmentation and the glue tax. IaC (Terraform), CI (Actions), the catalog (Backstage), flags (LaunchDarkly), and observability (Grafana) are separate systems with separate models, wired together by hand. No single tool holds the whole picture, and keeping the glue working is the team's unpaid second job.
  2. Untyped config sprawl. A system's real truth is scattered across thousands of YAML and HCL files — k8s manifests, Terraform, Actions workflows, catalog-info.yaml — none type-checked and none connected to the running code. A misindented line ships a broken config at 2 a.m.
  3. The catalog drifts from reality. Backstage's catalog is hand-declared YAML, so it rots — the same failure that sank a generation of CMDBs. A portal that's confidently wrong is worse than none.
  4. Nobody reconciles the whole system. Argo reconciles the cluster, Crossplane reconciles cloud infra, Terraform plans its own state — but no tool reconciles declared-vs-observed across code, infrastructure, data, and ops together, or computes blast radius across all of them.
  5. DevOps is a different language from app development. Shipping a feature means context-switching from Kotlin to HCL to YAML to bash. That gap is a big part of why the "platform team vs product team" wall exists.
  6. Agents can't operate it safely. There is no typed, queryable, permission-gated model of the system for an AI agent to reason about and act on; agents are left poking at CLIs and hoping.
  7. The inner and outer loops are disconnected, and previews are bolted on. Local development and production deployment use different models, and ephemeral/preview environments are an add-on rather than a first-class concept.
  8. Deployment doesn't understand the application. Terraform and k8s deploy resources; they do not know that "ship the new chat reply" is one logical change spanning a Cloudflare Worker, a Spring handler, and a D1 migration. So one feature becomes several disconnected deploys whose ordering you have to get right by memory, and blast radius is guesswork.

That last point is the deepest, and it's the one reaktor is uniquely placed to fix — because reaktor already has the application's whole structure as a graph.


4. Reaktor's bet — deployment is a graph operation

reaktor authors an entire product as one graph in Common/KMP — screens, services, actors, data, and their wiring. The runtime then runs partitioned across five targets: Common/KMP, the JVM server (Spring on k3s/GCP), the JS edge (Cloudflare Workers / DO / D1 / R2 / KV), JS web (Kotlin/JS or WASM), and JS Hermes (the React Native shell). DevOps, in reaktor, is the act of taking that one graph and partitioning it across those targets at deploy time — and because the graph is a real, typed object, the platform can do things no YAML pipeline can:

  • It knows what a "change" actually is. A change to chat touches the chatServer Worker, a Spring handler, and a D1 table — reaktor sees those as one connected sub-graph and deploys them as one logical unit, in dependency order, instead of three commands you sequence by hand.
  • It computes blast radius from the edges. Because every dependency is an edge, "what could this deploy break?" is a graph traversal, not a guess — and it gates the operation accordingly.
  • It inserts the plumbing automatically. When the partition cuts an edge between two targets (a call that now crosses from a Worker to the JVM), reaktor inserts a ServiceNode proxy (a network hop) or a NativeNode proxy (an FFI hop) at the cut — the same mechanism described in reaktor-server and reaktor-bus-mesh.

The shape, end to end:

   Common/KMP graph  (one app, authored once)
            │  reaktor-devops: partition by target  (graph coloring + blast radius)
            ▼
   ┌──────────────┬───────────────┬──────────────┬───────────────┐
   ▼              ▼               ▼              ▼               ▼
  JVM/k3s       JS edge          JS web        Hermes/RN       (Common shared)
  container →   Workers /        Pages /       R2 version
  Artifact Reg  D1/DO/R2/KV      R2 CDN        manifest
  → k3s deploy  → wrangler       → CDN         → OTA            Fastlane/Firebase
            │
            ▼  every artifact ships through ↓
   Dagger pipeline-as-code  (build · test · deploy — same locally & in CI)
            │
   Pulumi materializers  provision the infra each target lands on (k3s, CF, GCP, Supabase)
            │
   reaktorDesktop Deploy pane  shows the topology, runs gated ops, surfaces drift

80/20: reaktor owns the typed platform model, the graph partition, the blast-radius computation, and the gated operations; the executors — Pulumi, Dagger, wrangler, k3s, GCP, Fastlane — do the actual provisioning and shipping. reaktor is the brain that understands the application; the proven tools are the hands.


5. The design, component by component

5.1 One typed platform model → materializers

Rather than maintain Pulumi programs, wrangler.json files, k8s manifests, and Dagger scripts as independent sources that drift apart, reaktor's direction is Kotlin-first: a single Platform declaration is the source, and materializers emit each downstream config from it.

BestBudsPlatform.kt
  ├─ PulumiMaterializer    → Cloudflare / GCP / Supabase / Grafana / k8s resources
  ├─ WranglerMaterializer  → per-worker config, bindings, migrations (the predeploy merge)
  ├─ KubernetesMaterializer→ k3s manifests / Helm values
  └─ DaggerMaterializer    → the build/test/deploy pipeline

This is the same lesson dbt and Pulumi teach — generate the config from one typed model you can refactor and test, don't hand-maintain the sprawl. Status: Pulumi, run as a CLI subprocess from the Kotlin tool provider, and the Dagger module are real and used directly today; the unifying Kotlin Platform model that drives all four materializers is designed.

5.2 Deployment = partition the graph

The core operation. Given the compile-time GraphManifest, reaktor-devops colors each node by its runtime target (a partitioning pass over the graph, computing blast radius), inserts the proxy nodes at the cut edges (§4), and emits a per-target artifact, each shipped by its own path:

TargetArtifactShips via
JVM server (Spring)container imageArtifact Registry → k3s rolling update
JS edge (Workers)worker bundles + bindingswrangler (Workers-for-Platforms), the predeploy jq binding-merge
JS webstatic bundlePages / R2 CDN
Hermes / RNJS bundleR2 version manifest → over-the-air update
native KMP—"native release required" → Fastlane / Firebase App Distribution

Cloudflare Tunnel exposes the private k3s services to the edge without public IPs. The win restated: a single feature spanning a Worker + a Spring handler + a D1 migration is one partitioned deploy, ordered and blast-radius-checked, not three hand-sequenced commands. Status: partitioning remains designed; today the worker fleet and server operations are discoverable and safely gated in Reaktor Desktop, while their cross-target ordering is still coordinated by the checked-in Dagger/recipe layer.

5.3 Dagger — the pipeline as code

The build/test/deploy pipeline is Dagger: typed functions, containerized, content-addressed and cached, so a pipeline runs identically on a laptop and in CI (no more debugging YAML by pushing commits). Because there's no JVM Dagger SDK, the module is TypeScript driven via the dagger CLI (which runs it containerized); everything else in the control plane stays JVM-native. In-pipeline testing — Maestro flows, contract/replay tests, a device lab — runs here, so a change is proven before it partitions out. Status: real and in use.

5.4 Environments, GitOps & safe releases

  • Environments — dev / staging / prod, plus ephemeral previews: a preview environment is just the graph partitioned and materialized into a scoped, throwaway copy (the Vercel-preview idea, graph-native). The shadow dev environment is the production-mirroring version of this.
  • GitOps reconciliation — the desired state (the Kotlin model / Pulumi) is continuously compared to what's actually running (live Cloudflare / GCP / k3s APIs); the gap is drift, surfaced in the unified system graph as a first-class state, not a silent lie. This is the Argo/Crossplane lesson, applied across the whole system rather than one slice.
  • Progressive delivery & rollback — a canary is a graph-version GraphPatch routed to a fraction of traffic by a Selector/RouteNode (the mechanism in reaktor-server §9.10); rollback re-points to the previous graph version. Feature flags ride the capability system.

5.5 Operations on the graph — safe, audited, agent-runnable

Every devops verb — deploy, roll back, restart, scale, drain, run a migration — is a typed GraphOperation attached to the node it acts on, not a CLI invocation. Each operation is:

  • permission-gated by AuthContext + capability + a SafetyClass (a destructive prod operation needs more than a staging read);
  • blast-radius-aware — the graph traversal computes what's affected and shows it before you confirm;
  • audited — who ran what, against which node, when, with what result;
  • agent-runnable — because it's a typed, gated operation over a queryable graph, an agent can propose and (within its permissions) execute it through MCP, with the same gates a human hits.

This is what makes self-service safe: a product dev (or an agent) can ship and operate without root access or tribal knowledge, because the guardrails are in the operation, not in a human gatekeeper.

5.6 GCP fleet operations — capacity and customer-owned cells

The DevOps plane also owns where capacity comes from. There are two consumer-facing modes and both must compile down to the same graph operations:

ModeCustomer experienceReaktor operation
Reaktor-managed GCP"I need capacity in asia-south1 / europe-west1; create and run it for me; bill me through Reaktor."Cloud.CreateCell, Cloud.AddNode, Cloud.ResizeNodePool, Cloud.DrainNode under Reaktor-owned GCP billing
Customer-owned GCP"Here is my GCP project/cluster; manage it for me, but I pay GCP directly."Cloud.AttachCell, Cloud.RegisterAgent, Cloud.Reconcile, Cloud.DeployGraphPatch under delegated policy

This should not become a pile of gcloud scripts. It is the same gated-operation model as deploy:

TenantPolicy + capacity request
  -> graph placement pass
  -> dry-run plan with budget/location/blast radius
  -> approval gate
  -> signed cell plan
  -> reaktor-agent applies inside the cell
  -> observed facts and audit events return to the graph

For Reaktor-managed cells, the platform can provision the GCP VM/node pool itself and bill the consumer through Reaktor. For customer-owned cells, the platform starts in Observe mode, then the customer explicitly grants Deploy, Capacity, or Managed mode. The executor is still GCP/k3s; the product surface is a Reaktor graph operation.

5.7 reaktorDesktop — the developer portal

The surface for all of the above is the Deploy pane in reaktorDesktop. It is a Backstage-class portal — catalog, provenance, golden-path recipes, exact-command plans, and run history — projected from the real BestBuds/Reaktor workspace instead of UI fixtures. Today it can filter real targets, classify and gate release operations, require typed production confirmation, execute through the shared supervised process layer, and retain output. A destructive Cloud API plan needs a second approval from a different approver. Live graph blast radius, authenticated approvers, deployment drift, rollout watches, verification and rollback remain the build target (codex-roadmap); until those facts exist the pane reports configuration as unknown rather than healthy.


6. Relationship to Backstage & Crossplane — what we adopt, what we build

Two systems are close enough to reaktor-devops to be worth an explicit position, because reaktor is building native equivalents rather than adopting them wholesale.

Spotify Backstage is the reference design for reaktorDesktop's portal: its System / Component / Resource / API catalog, ownership model, software templates (scaffolder), and plugin architecture are exactly the right shape, and reaktor's node families mirror them. Adopted precedents: the entity + ownership model, golden-path templates, the plugin/pane structure, and the discipline of "platform as a product." Reaktor-specific design: Backstage's catalog is hand-declared YAML that drifts from reality; reaktor's graph is derived from the running code, so it stays fresh by construction. Backstage is also a separate TypeScript/React portal; reaktorDesktop is the workbench tied to the live app graph and agents. Interop option: reaktor can act as a Backstage entity provider for orgs that already run Backstage, rather than replacing it.

Crossplane is the reference for the reconcile-and-surface-drift mechanism — a control loop that constantly diffs declared-vs-actual. Adopted precedents: the pattern (continuous reconciliation, drift as a first-class state), which reaktor implements through the system-graph importers plus the Kubernetes manifests it already applies (ClickHouse and Memgraph as plain StatefulSets, without operators). What we keep instead: Pulumi for run-based IaC (already chosen, and far stronger than Crossplane for the Cloudflare-heavy stack). When to revisit: if reaktor expands into broad multi-cloud/GCP infra and wants self-service Compositions, Crossplane becomes a serious candidate for that slice.

The through-line: both validate reaktor's two devops bets — a derived, ownership-aware catalog and continuous desired-vs-observed reconciliation — and reaktor's edge in each is the same: the model is derived from the running system, not hand-declared YAML that drifts.


7. What we cover, and what we don't

reaktor-devops is the orchestrating brain, not a replacement for every proven executor. The deliberate scope:

  • We own: the typed platform model, deployment-as-graph-partition + blast radius, the gated-operation layer, the developer portal, and the desired-vs-observed reconciliation across the whole system.
  • We orchestrate, not replace: Pulumi (IaC), Dagger (CI/CD), wrangler + k3s + GCP + Fastlane (the deploy targets), GCP cell capacity operations, customer-owned cluster attachment, and Grafana / OTel (observability — owned by performance & observability). reaktor drives them; it doesn't rebuild them.
  • We don't attempt (the conscious 20%): arbitrary AWS/Azure abstraction, a general-purpose CI marketplace, or a metrics/logs backend of our own. GCP is the concrete fleet provider; Cloudflare and Supabase remain product/runtime providers where the graph needs them.

The point mirrors the rest of reaktor: cover the path this set of apps actually walks, exceptionally well and coherently, rather than building a worse version of every devops tool.


8. Build plan

PhaseDeliverableLeans on
O0 — Inventory & driftlift WranglerInventoryProvider + Pulumi/k3s/Supabase introspection into the system graph; show desired-vs-observed driftunified-system-graph, cloud-pane (real bits)
O1 — Dagger consolidationmove the remaining n8n workflows (build/deploy/k8s-sync) into the Dagger module; one pipeline, run locally or in CIDagger (real)
O2 — Platform modelthe Kotlin Platform declaration + the Pulumi/wrangler/k8s/Dagger materializersPulumi-Java (real)
O3 — Partition & deploythe graph-partition pass (target coloring, proxy insertion, blast radius) → per-target artifacts → single-command deployGraphManifest, O2
O4 — Gated operationsdeploy / rollback / migrate / scale as GraphOperations with RBAC + blast radius + audit; MCP exposurereaktor-agentic-tooling, security-plan
O5 — GCP cell fleetCloudCell inventory, Reaktor-managed capacity requests, create/resize/drain node operations, budget/location guardscloud-control-plane, O2–O4
O6 — Customer-owned GCP attachGcpCellConnection, reaktor-agent registration, Observe → Deploy/Capacity/Managed mode upgrades, revoke flowcloud-control-plane, auth, secrets
O7 — Deploy panethe live, graph-backed Deploy pane in reaktorDesktop; golden-path templates; ephemeral previews; fleet/cell operationsengine workbench, O3–O6
O8 — Progressive deliverycanary as a graph-version GraphPatch; automated rollback on SLO burnreaktor-server §9.10, perf-observability

O0–O1 are reachable now on real foundations; O2–O3 are the unlock that makes "deployment is a graph operation" concrete; O5–O6 make cloud capacity itself part of the same operation model.


9. Open decisions

  • GitOps engine: build vs adopt. Implement reconciliation as reaktor system-graph importers (one model, full control) or run Argo CD / Flux for the k3s slice and feed their state into the graph (proven, but a second control loop).
  • Where the platform model lives. A standalone reaktor-devops module consumed by each app, vs a per-app Platform.kt that imports shared materializers. Likely the latter, with shared materializers in the module.
  • Ephemeral-preview scope. How much of the stack a preview environment spins up (full shadow vs Workers + a scratch D1/Supabase branch) — cost vs fidelity, shared with shadow-dev.
  • Crossplane revisit trigger. The concrete threshold (multi-cloud breadth / self-service Compositions demand) at which Crossplane earns a place beside Pulumi.
  • n8n cutover ordering. Which workflows move to Dagger first, and how to run both during the transition without double-deploys.

10. References

Verified June 2026.

Infrastructure as code & GitOps

CI/CD & pipelines

  • Dagger — dagger.io (pipeline-as-code, containerized + cached). GitHub Actions — docs. Spinnaker (Netflix multi-cloud CD) — spinnaker.io.

Orchestration

Platform engineering & internal developer platforms

Developer experience & method


See also

  • Reaktor Cloud Control Plane — the operator view: GCP cells, Reaktor-managed capacity, customer-owned GCP attachment, resource inventory, Pulumi/Dagger execution, and console deep-links.
  • Secrets Management — GCP Secret Manager, k3s External Secrets, GitHub WIF, and the wrangler sync the pipeline depends on.
  • Performance & Observability — the health, SLOs, and burn-rate alerts that gate progressive delivery.
  • Reaktor Unified System Graph — the graph this plane partitions, reconciles, and operates on; desired-vs-observed and drift.
  • Reaktor Server — ServiceNode transport proxies, multi-region placement, and the §9.10 progressive-delivery mechanism.
  • Shadow Dev Environment — production-mirroring ephemeral environments as a deploy operation.
  • CLI - Use before building or structuring the reaktor CLI, choosing a CLI framework (Clikt vs oclif vs picocli), or mapping the npm scripts / task catalog onto commands; pairs with reaktor-devops (the catalog) and reaktor-cloud-pane (the runners).
  • Cloud Control Plane - Use when building the operator view over product infrastructure, GCP cells, fleet capacity, and tool execution.
  • Reaktor Secrets - Use before moving, rotating, distributing, caching, or materializing secrets.
  • Shadow Dev - Use before building production-like test and replay environments.
  • Perf and Observability - Use before optimizing or adding instrumentation.
  • Auth Architecture - Use before changing reaktor-auth or request authorization boundaries.