1. Start here

1.5 Developer experience: onboarding, migration and operations

Requirements: R8, R9, R11, R13, R14, R16, R18, R23, R24, R25, R26, R29, R30, R31, R33, R34, R22.

The platform as an application team meets it: three journeys - onboarding a new application, migrating one that runs elsewhere, and operating the ones in production - told as use cases. The application contract says what a team has to deliver; this page says what happens around it, from the first request to the day the application is retired. It names no tool: the sections it links to do.

Four rules shape every journey:

  1. The team owns its application and its repository; the platform owns everything cluster-scoped. A team writes plain Kubernetes for its namespace and nothing else (section 1.4); the namespace, identities, roles, routes’ gateway, DNS, certificates and firewall rules are made for it.
  2. Every interaction is a pull request or a request. A change to the application is a pull request in the team’s repository; a change the platform must make for the team (a namespace, an identity, a firewall rule, a secret share) is a request that an agent turns into the platform’s pull request, approved by its risk tier (section 6.1). Nobody, human or agent, changes a cluster by hand.
  3. The platform answers before the team asks. Admission policy and the CI checks tell a team what the contract misses before anything runs; denied flows become proposed rules; the right-sizing, release and operations agents open the pull requests and issues a team used to write itself.
  4. The same experience in every environment and every cloud. dev, acc and prd differ only in who approves and how fast (section 3.2, R25); a second cloud is the same contract under another implementation (section 1.3, R16).

Running it with a small platform team: the non-negotiable rules

The platform is run by a few people for many applications, with the agents of section 6.1 doing the routine. It stays small only if the platform team does no work per application and negotiates nothing per application: every rule is a policy, a timetable or a limit that applies to every application alike and is applied by a check, an agent or admission, never by a person. The rules below are the ones a team accepts at onboarding (R33); a team that cannot meet one is not onboarded, or carries an exception with an owner and an expiry date (exemptions expire). The platform’s time goes into the rules and the exceptions, not into the applications.

Rule What the team accepts If it is not met
The platform’s patch timetable applies to every application. Cells are upgraded on the update policy’s timetable, and every application image is rebuilt and deployed when its base image is patched (patching) No application blocks a cell upgrade or postpones a patch; the team’s answer to both is the contract’s rollout rules and its automated tests A removed API or a failed rebuild is an issue to the team with a date; after it the upgrade goes ahead, and an image past its deadline is refused at admission, so the application cannot scale or restart until the team fixes it
Automated tests are the only gate. The contract check, the team’s own tests in CI and the smoke tests on the per-cell names decide what moves; no release waits for a manual acceptance The team keeps tests that prove its routes work, and main always releasable, because the platform rebuilds and promotes from it without asking An application without tests that pass is not onboarded; a rebuild whose tests fail stops at dev and is the team’s issue
One release train per environment, with its change windows and freezes (section 6.1) No per-team release calendar; an urgent fix takes the train with a shorter soak -
The contract’s rollout rules and backwards compatibility (section 1.4, section 3.4) Two replicas, probes, a disruption budget, graceful shutdown, expand → migrate → contract Refused at admission; a breaking change is rolled back by the SLO gates
No egress, no secrets, no cluster-scoped objects by default (section 1.4) Every destination, secret and identity role is a request or a confirmed proposal Denied by the Firewall or admission, logged
Capacity inside the limits (limits.yaml, section 6.1) Requests and bounds move by pull request within the limits; a limit change is a request to the platform, not a purchase Over a soft limit one human approves, over a hard limit the pull request is rejected
Every application has an owner who answers (section 2.6) The contact and the groups are kept current; the team is on call for its application A namespace whose contact does not answer and whose groups are empty is retired after a notice
Exceptions expire. The stateful cluster, a cloud-pinned service, a policy exemption An exception has an owner, a reason and a date, at most six months; renewal is a new request The exception lapses and its rule applies

The rules are the first page of the onboarding request: the team accepts them before anything is created for it.

What a team has, and what it never has

The team has The team never has
One repository with one directory per application and one per cell (section 3.3) Write access to the platform repository or the IaC
One namespace per application and zone, with its quota, default-deny policies and the roles of its own three groups (section 2.6) A cluster role, a second namespace’s objects, the list of other tenants
One workload identity per application and zone, with the roles its onboarding named (section 2.5) A key, connection string or certificate it keeps itself
Its own prefix in the zone’s vault, written by its edit and admin members (section 2.5) A pod that reads the vault; the vault’s list of names
One DNS name per environment in the zone’s domain, published from its route (section 3.1) A listener, a certificate, a load balancer or a public IP of its own
A rule file of its named egress destinations, proposed from what the Firewall denied (section 5.1) An egress policy of its own, or a destination by address
Its SLOs, dashboards and alerts next to its manifests, and every log, metric and trace of the environment (section 6.2) Telemetry of another environment; a secret in the telemetry
Its share of the cell’s cost by team and cost centre (section 2.6) A bill for the platform’s fixed part
Read access to its namespace in every environment, a kubeconfig for that namespace alone kubectl apply in acc or prd; break-glass, which is the platform’s

Onboarding a new application

The journey from an idea to the first request served in production. Each step is a use case; the right column says who or what does the work.

Step What the team does What the platform does
1. Place it Walks down the placement list (where does a workload run): a stateless cell for the code, PaaS services for the data, the stateful cluster only with a documented exception (R18). Picks the zone (its country, its exposure) and, for an internal application, a home cell - the slow-speed cell by default Refuses what the list does not allow: a PersistentVolumeClaim in a cell, a cloud-pinned service without an exception, a zone that does not match the exposure
2. Request it Accepts the non-negotiable rules and fills the request template (onboarding agent): application and zone, the three Entra ID groups, team, cost centre and contact, the Azure services it needs and whether any of them needs a secret, its data stores and who owns their restore (section 6.3), arm64 or spot if wanted The onboarding agent turns the request into pull requests: namespace with its labels and quota, workload identity with its roles, vault prefix and secret store, the Flux configuration of the team’s repository, the budget line, the backup posture. A new application is at least a medium tier, so one human approves; the team gets back the names it will use
3. Start from the reference Copies the reference application of section 0.3, builds its own image from the platform’s base image for its runtime with the platform’s build workflow, and adds the tests that will be its only gate. Keeps what the contract asks for: two replicas, probes, a PodDisruptionBudget, graceful shutdown, h2c on the route port, the placement labels (section 1.4) CI runs the contract check on every pull request: manifests against the same rules as admission, the image for h2c and its architectures, the egress names against the zone’s allow-list (section 3.3). A team learns what is missing before anything is deployed
4. First run in dev Merges. Checks the first-run list: the route answers on its name over TLS, /healthz is green behind the gateway, logs and traces appear in the environment’s dashboards with the trace id Flux deploys within minutes; the mutations place the pods on the zone’s nodes of the chosen architecture and capacity (section 2.3); ExternalDNS publishes the name; the gateway probes /healthz
5. Open the egress it needs Expects nothing to be reachable. Reads the egress agent’s proposal - each denied destination with the pod that asked for it and the evidence - and confirms the ones the application needs, or opens the pull request itself The Firewall denies and logs; the egress agent proposes one rule file change per namespace, a human merges it (tier high); the traffic flow pages show the application’s picture (section 5.2)
6. Connect its data Reaches its PaaS services by identity over their open protocols; takes a secret only where the target cannot use Entra ID, as an ExternalSecret under its prefix, written to the vault by an admin member (section 2.5) The onboarding created the services’ private endpoints, the identity’s roles and the Firewall rules for them; the policies refuse a pod that reads the vault itself (R11)
7. Promote Reviews the promotion pull request to acc, then to prd: the digests that ran in dev, the release notes and the risk summary the release agent wrote (section 3.3). Sets its SLO targets and alert routes in Git Signatures verified and images imported at promotion; the release train carries the change through the cells one at a time behind SLO gates (section 3.4); the first application in a prd namespace is a medium tier

The onboarding is done when the application serves its name in prd, its SLOs are measured and its owner is on the cost report. The team never created a cluster-scoped object, a DNS record, a certificate, a role assignment or a Firewall rule - it asked for them, by template or by confirming a proposal.

Adding a team member is a change to the team’s Entra ID groups, not to the platform; adding a second application of the same team repeats steps 1 to 7 with a new namespace in the same repository.

Trying it before dev: previews and sandboxes

A team tests a change before it reaches dev’s main, and tries the platform before its first onboarding, in dev itself (R34) - never in a separate sandbox cluster, which would be a fourth environment that drifts and a place where the contract is not enforced. Both kinds of namespace meet every policy of dev, so what works in them works in prd.

  Preview Sandbox
What A namespace <app>-pr-<n> next to the application’s dev namespace, in the same zone, for one pull request A namespace <team>-sandbox in a zone of dev, long-lived, with the reference application preloaded
Who asks The team, by marking its pull request for preview; nothing else to request The onboarding request, also before the team’s first application
Deployed from The pull request’s branch, read by the dev cells outside the cell branches and the release train, like the placement (section 3.3) The branch the team names, the same way
Shares The application’s identity, vault prefix, Firewall rule file and groups in dev, so a preview reaches exactly what the application reaches and nothing more; the application’s dev data stores, so a schema change in a preview follows expand → migrate → contract like a release Its own identity without roles, its own vault prefix and rule file - a sandbox is an application of its own, and a destination or a service it needs is a request like any other
Published as <app>-pr-<n>.<zone>.dev.<domain> on the zone’s gateway, published only in dev <team>-sandbox.<zone>.dev.<domain>
Observability and cost Labelled as a preview: left out of the application’s SLOs and alerts, in its logs and traces, charged to the team Charged to the team like an application
Lifetime Removed when the pull request closes, or after seven days at most; a team runs at most a few at once (its quota) Until the team asks for its retirement, or when its groups are empty and its contact does not answer
Needs a human Never: no identity, rule or role is created for a preview The onboarding’s tier, once

A preview is not a release: it never reaches acc or prd, is never promoted, and carries no main commit. It gives a team the real gateway, policies, egress control and telemetry for a branch, which is what a local environment cannot.

Migrating an existing application

An application that runs today on a virtual machine, another Kubernetes cluster or another cloud meets the contract for the first time. The journey adds an assessment and a cut-over to the onboarding; the onboarding itself does not change.

1. Assess against the contract

The team lists what the application does today, and every finding falls into one of four classes. The classes decide the effort and the approvals, not the tools:

Class Meaning Examples
Fits as is The contract is met Stateless HTTP service with probes, configuration from the environment, data in a PostgreSQL or MySQL service; a scheduled job or a queue consumer (non-serving workloads)
Change the application A code or manifest change the team makes before onboarding Serves HTTP/1.1 only → serves h2c on the route port (section 4.1); an Ingress → an HTTPRoute; a key or connection string → workload identity (R9); reads a vault with an SDK → an ExternalSecret (R11); session state in memory → in a cache service, so any copy may answer; nodeSelectors and tolerations → the placement labels (R8); a cloud SDK for data → the service’s open protocol (section 1.3); logs to files → JSON to stdout; a single copy of a serving application → two, with a leader election where only one may work (R23); a scheduled job that may not run twice → a CronJob in the home cell
Change where it runs The architecture does not fit a stateless cell Data on local disks or a PersistentVolumeClaim → a PaaS service, or the stateful cluster with an approved exception (R18); a product shipped only as an operator → the stateful cluster; a cloud-specific service → an exception that pins the application to one cloud (section 1.3)
Does not fit The platform does not offer it; the application stays where it is or is redesigned A listener for other namespaces that is not HTTPS on 443 through the gateway (a database protocol served to other applications, a message broker’s port; inside its own namespace an application uses any protocol, R22); a public entry point for an internal workload; a flow into another zone’s pods; a privileged container or host network (section 2.7)

The assessment ends as a list of findings with their class, kept with the application, and the same CI check that onboarding uses turns the second class into failing checks the team works down. Every “change where it runs” finding and every exception goes through the onboarding request, where a human approves it.

2. Prepare the data

Data moves first and separately from the code, because two versions run during any cut-over (compatibility rule): the target PaaS service is created by the onboarding, the data is copied or replicated into it with the service’s own migration tooling while the old application keeps running, and the application is made to work against the new store before it moves. The schema follows expand → migrate → contract; a store that cannot be replicated is moved in a planned window, which is the one downtime the journey allows and the team’s call. Where the data stays in the stateful cluster, its exception, backup posture and restore owner are part of the request (section 6.3).

3. Onboard, then cut over

The application is onboarded like a new one (steps 1 to 7 above), in dev first with test data, then in acc and prd next to the running old one, under its platform name. The cut-over moves the clients, and follows the same shape as the migration between cells:

  1. Deploy the application in prd on the platform; nothing points to it yet.
  2. Test it on its platform name over the real TLS path, with the migrated data.
  3. Switch the clients: the old name becomes an alias of the platform name, or the clients are reconfigured, in the order the team chooses (a share of the clients first where the clients allow it).
  4. Drain the old deployment: wait until it sees no requests, keep it ready to take the name back.
  5. Remove the old deployment and its data after the soak; it was the rollback until then.

Rollback at any step before the last is moving the name back; the data store is already the new one, so the old application must be able to read it, which is what step 2 of the preparation made sure of.

4. Moves inside the platform

Once onboarded, an application moves without a second migration project:

Move How
To another cell (internal zones) A pull request to the team’s placement; the migration workflow does the rest without downtime (section 3.1)
To another zone A new onboarding in the target zone and a retirement in the source: the zone is the namespace, the identity, the vault prefix and the country of the data, none of which moves
From internal to external exposure, or back The same, in the other exposure’s clusters and subscription (section 3.1); an internal application that an external one must call is published like any other public name
To another cloud The same repository and manifests against another implementation of the contract (section 1.3); the data moves with the service’s own tooling, the exceptions list says what does not

Operating applications in production

What a team does with an application once it is in prd, and what the platform does for it. The platform’s own operations - upgrades, capacity, policy, incidents of the platform - are the agents’ (section 6.1); here is what the team sees of them.

Releasing a change

A merged pull request in the team’s repository is a release candidate; the promotion pull requests carry it to acc and prd, and the release train moves it through the cells one at a time behind SLO gates (releases cell by cell). The team sees the train on its pull request: which cell runs the new version, the test results on the per-cell names, the soak, and a rollback with its reason when a gate fails. Consequences the team plans for:

  • A change rides a train with other teams’ changes; a release is not instantaneous. An urgent fix takes the same path with a shorter soak, and a team that needs to stop a rollout asks for the halt - a safe action that needs no pull request (section 6.1).
  • Two versions run at once during the train, so every change is backwards compatible with the previous one; the compatibility rule is the team’s, not the platform’s to check.
  • Switching an application off is replicas: 0 in Git; it is switched on the same way. Nothing in between is allowed (R23).

Patching: base images and dependencies

Patching is the one change that must reach every application without anyone asking every team, so the platform drives it and the team’s tests gate it:

  • Base images are the platform’s. For every supported runtime and version (a .NET LTS, a Java LTS, Node LTS, Python, a static base for Go and the like) the platform keeps one base image in the environment’s registry, built from the vendor’s image, scanned and signed. Every application image is built from one of them by the platform’s build workflow in the team’s repository, and records its base, so the registry knows every image per base.
  • A vendor patch rebuilds the fleet. When the vendor publishes a patch, the platform rebuilds the base, then the base image agent rebuilds every application on it from the current commit of its main: the same source, a new base layer, the team’s tests and the contract check in CI, a signed image. Each rebuild is a pull request in the team’s repository that sets the new digest for dev and merges itself when the tests pass (tier low); the release agent’s promotion pull requests carry the digest to acc and prd the same way (tier low: a digest that passed every gate in acc). The patch train runs on the automated gates alone - the tests, the smoke tests on the per-cell names, the SLO gates and a shorter soak - inside the change windows; a breach rolls the cell back like any release (section 3.4).
  • The timetable is the update policy’s: a routine patch rides the next train in each environment; a fix for an actively exploited vulnerability takes the emergency fast track of section 3.5 - the waiting times skipped with the platform owner’s approval, still dev → acc → prd and one cell at a time. A runtime version that reaches its end of life is retired on a date; the team moves to the next supported base before it, under the same deadline rule.
  • Dependencies inside the image (packages the application brings) are the team’s to update; the registry’s scan reports them with the same deadlines, and a critical one past its deadline is treated like a stale base.
  • What the team sees: a pull request it did not write that merges itself, an issue with a date when a rebuild or its tests fail, and the share of its images on the newest base in the compliance report. It does nothing else.
  • What the team owes: an image built from the platform’s base with the platform’s workflow, a main that always builds and passes its tests, and tests that prove its routes - because nothing else stands between a rebuilt image and prd.

Knowing that it works

The team’s SLOs, dashboards and alerts live next to its manifests and are applied with them (section 1.4). The default SLOs - availability and latency at the zone’s gateway - exist from the first run; the team sets the targets and adds what the gateway cannot see (queue lag, business events). Alerts are routed by the namespace’s team label (section 2.6), and every member of the environment’s Grafana stack sees everything in it (R31), so a team debugs a call across applications with one trace id, and keeps secrets and personal data out of what it logs (what must not be sent). The same place shows the application’s cost per month by team and cost centre, and its requests against its use.

Being on call

An alert on the application reaches the team; an alert on the platform reaches the platform. The line between the two is the contract: the gateway, nodes, network, DNS, certificates, Firewall, the vault and the cluster are the platform’s, the pods and their behaviour are the team’s. The operations agent triages every alert first, correlates it with releases, platform changes and cell health, and files an issue with the evidence - to the team when the cause is in its application, to the platform otherwise (section 6.1), so a team on call starts from a diagnosis, not from a dashboard. In prd a team reads its namespace and changes it only through Git; there is no shell into a pod of prd, and break-glass is the platform’s, followed by a pull request that brings Git back in line.

Changing the application’s resources, access and reach

Every day-2 change of an application is a pull request, and most of them are proposed before the team asks:

The team wants to The team does Tier
Scale on load Sets HPA bounds in Git; the VPA resizes requests in place, and the right-sizing agent proposes the baseline (pod right-sizing) Low
Use spot or arm64 nodes Changes the pod labels (R8) Low
Reach a new destination Confirms the egress agent’s proposal, or opens the rule file pull request (section 5.1) High (a Firewall rule)
Add or rotate a secret Writes it to its vault prefix as an admin member; the ExternalSecret follows (section 2.5) None: no pull request to the platform
Give a person access Changes its Entra ID groups (section 2.6) None
Use a new Azure service A request: the identity’s roles, the private endpoint, the Firewall rule Medium or high
Call another application of its zone Its published name in the caller’s rule file, like any destination (section 2.6) High (a Firewall rule)
Publish a second name A second HTTPRoute in the zone’s domain; DNS follows (section 1.4) Low
Move to another cell A pull request to its placement (section 3.1) Low

Living with the platform’s changes

The platform changes under the application without asking it, and the team notices only what concerns it:

  • Cluster upgrades, node images and base images are invisible: a cell is drained, upgraded and returned; an internal application is migrated out of the cell and back (section 3.5). What the team owes is the contract’s rollout rules, which make the drain harmless.
  • A removed Kubernetes API or a new policy reaches the team as an issue from the upgrade or compliance agent with the manifests concerned and a date (section 6.1); a policy starts in audit, so the team sees its finding before it is denied (section 2.7).
  • A cell failure fails the internal applications of that cell over to another cell and leaves a drained external cell out of the answers; the team is told, and does nothing (section 3.1). A region loss is the platform’s recovery; the team’s part is the restore of its own data stores, named at onboarding (section 6.3).
  • Cost is reported, not negotiated: the capacity and cost agent files an issue when an application’s requests are far from its use or its spot share could grow; the team answers with a pull request or a reason.

Retiring an application

The last use case is the first in reverse: the team removes the application’s directory and its cell directories, and asks for the retirement. The request removes, in order, the DNS name, the Flux configuration, the namespace and its roles, the Firewall rules and the traffic flow page, the identity and its roles, the vault prefix (recovered for the vault’s retention, then purged) and the budget line; the backups of its data stores stay for their retention and are the last thing to go. The cost report shows the team’s share going to zero.

The same journey in every environment

  dev acc prd
Previews and sandboxes Yes, in the dev cells (previews and sandboxes) No No
Who deploys Flux from the team’s merge, within minutes The promotion pull request, merged by tier The promotion pull request, merged by tier, inside a change window
Who approves a new application The tier: one human One human One human; a Firewall rule or an identity change two
Human access to the namespace Read, and the roles of the team’s groups Read Read
The data The team’s test data The team’s test data Production
What differs Nothing in the contract: smaller pools, a Basic registry on its public endpoint (section 3.2) Nothing Three cells instead of two

OneK8s – a design under review; country codes and IP ranges are examples.Source of this pageComments and issues