I'm borrowing Shrek's explanation of ogres for Kube.ist, my Kubernetes platform. A platform has layers too, although mine have rather more YAML.

Replacing an application shouldn't mean changing the network beneath it. Installation needs an order too. Dependencies first. I've been calling both arrangements layers, but one asks what has to be ready first and the other asks what should survive when the thing above it changes.

The polder system takes its name from Dutch polders, where drainage and water defences make land usable. I like that picture for a platform. Prepare the ground before building on it, then keep looking after that ground while the things above it change.

The polder system separates what I can replace together into three layers:

LayerWhat belongs there
PolderPersistent data and the underlying networking
ClusterProcessing resources and the Kubernetes cluster
ApplicationThe applications running on that cluster

The polder gives the processing layer something lasting to build on, so I can replace the cluster while keeping its data and network underneath it. An application can change much more often. The useful boundary is the resource's lifecycle. What survives replacement? A database disk doesn't acquire the application's lifecycle because the application happens to use it. My rule is never to mix resources with different lifecycles in one apply or deployment process, so I keep the layers' configuration and apply steps separate. The cluster can consume the network and storage identifiers from the polder without taking over their lifecycle.

Put the application and network in one enormous plan, and a replacement or deletion can reach something I meant to leave alone. I came to update an app. Now the networking is gone.

Numbering the installation layers

I picture a platform installation as numbered folders. Foundations first, applications later. I can look at the plan before running anything, which is a large part of its appeal.

Take a controller and a resource it understands. The resource uses a new Kubernetes kind, so its definition must exist before I create it, and now even two components have brought a third thing to put in order.

Storage for the controller, a hostname for its dashboard, a certificate to ask for.... The list keeps growing. Some things can start together while others wait. For Kube.ist I'm trying to work out how much of that arrangement the numbers can usefully carry.

For all the Kubernetes involved, I'm fond of an ordinary directory listing, I can open it, have a look and leave a few gaps between the numbers. A pleasantly low-tech place to begin.

Here's a small illustrative platform. The arrows mean “must be available before”. They don't mean “sends traffic to”:

resource definitions → controller → application resource
network entry point ──────────────→ public route
certificate controller ───────────→ certificate

I'd start with everything that has no unmet prerequisites. Put those things in the first layer, cross them off the remaining items' prerequisite lists, then repeat with whatever is now ready, which is a layered topological sort. A cycle leaves a group waiting for one another. Nobody can start. That's a useful reason to refuse my plan. For a small installation, I can do that sorting myself and write down the answer:

000-foundations/
100-controllers/
300-applications/
500-public-access/

The gap between 100 and 300 leaves room for something to sit between them. Renumbering the world to insert one directory has limited recreational value.

Looking at 300-applications I can see where I've put it, but which controller does an application need, and could it have started in an earlier layer? I might remember why it's there until I come back to it months later. Another reader has to go through the files.

I can number a few components myself. As the platform grows I'd like to write down the dependencies too. Then the graph can tell us why an application has to wait, instead of leaving us to work backwards from its folder number.

What makes the installer wait?

The installer has to read the folder name for it to influence deployment. What does it actually read? If it reads an annotation, that controls the order:

metadata:
  annotations:
    argocd.argoproj.io/sync-wave: "100"

Argo CD orders resources within a sync phase by wave number, lowest first. It uses sync and health status to decide whether to progress, so the resource's health assessment becomes part of the installation order.

An Application's place in the order doesn't tell me whether the resources it creates are ready. What does the next component need? The health check has to answer that. The wave documentation describes how Argo CD uses those results.

An application needs the controller's available API and a reconciliation loop that can do the work. Sending the manifest hasn't supplied either immediately. Has the installer finished sending files? That answers a different question.

I could put a pause between the folders and call that ordering, but I'd find it hard to explain why the pause was long enough. What I actually need is a check. Does the resource definition exist, or is the controller ready? The waiting layer needs that answer. My favourite number of seconds won't do.

Suppose the repository contains this:

100-controllers/catalogue.yaml

And the file carries this:

argocd.argoproj.io/sync-wave: "300"

The directory tells a human one story and the annotation tells the deployment controller another, but both are valid strings so neither has to produce an error.

The folder and the wave disagree, and the wave wins.

I'd keep the dependencies and generate the installation layers from them, including the ordering the installer reads. Then I have one plan instead of two disagreeing copies. Each lifecycle still has a separate apply. I can keep reading an ordinary directory listing, with a reason beside anything that has to wait.

The numbers were meant to make this easier to read, and I'd be annoyed if my improved installer hid the order from me.

Shrek's explanation of layers. Watch on YouTube.

This entry came over from blog.haddon.me. I’ve tidied up the grammar and flow, bringing it closer to how I write now. I used AI to help with the grammar, structure and flow. I have dyslexia, and my writing often comes out as a rambling scrawl, so it can be hard to see what I was actually working on at the time. I think the tidied versions are easier to digest and more interesting to read. The artefacts got the same tidy-up while I was at it.