How flow Is Actually Used

This is the part worth writing down.

The repo is a flow workspace. All of its automation lives in one .execs/ directory, split by concern rather than by application: utilities, cluster operations, apps, infrastructure, networking, storage, platform, labs, and aggregates. It’s a couple thousand lines of flow YAML, which sounds like a lot until you consider it replaced a pile of shell scripts and a much larger pile of things I used to keep in my head.

Everything composes from a shared library

The single most useful thing I did was pull the repeated parts into a utils namespace and then never write them again. A deploy is a serial composition of references:

- verb: deploy
  name: jellyfin
  tags: [media]
  description: Deploy the Jellyfin media server to Kubernetes using homelab chart
  serial:
    failFast: true
    dir: //apps/media/jellyfin
    execs:
      - ref: install utils:helm-base
        args: [jellyfin, jellyfin]
      - ref: verify utils:deployment
        args: [jellyfin, jellyfin]

Every app looks like that. Adding a new one is a values file, a chart reference, and about six lines of flow. The namespace creation, the repo add, the helm upgrade --install, the rollout wait, the verification. All of it lives in utils and is written once.

Secrets come from the vault, not the repo

Credentials are declared as parameters and resolved at run time. Nothing is committed, and nothing sits in my shell history:

params:
  - secretRef: bring-username
    envKey: BRING_USERNAME
  - envKey: ADDITIONAL_ARGS
    text: |
      --set controllers.main.containers.bring-api.env.BRING_USERNAME=$BRING_USERNAME

A shared create utils:secret executable turns those into Kubernetes secrets during the deploy. There’s no sealed-secrets controller, no SOPS, no external secrets operator. For a single operator, flow’s vault is the whole secret management story.

Aggregates for the things I do together

I rarely want to deploy one media app. flow deploy media-stack runs the six of them in order and then prints the URLs; flow verify media-stack checks all six in parallel. The aggregate is just another executable that references the others.

Operations, not just deploys

The half of the workspace I didn’t expect to write is the diagnostic half: check health, check issues, show overview, show inventory, debug pod, debug service, and export diagnostics. These exist because at 11pm I do not want to remember the right kubectl get incantation across four namespaces. flow check health tells me whether anything is wrong, and that’s the whole point.

There’s also a small labs namespace with httpbin and netshoot behind test dns / test http / exec tcpdump executables, for when something is broken in a way that needs poking at from inside the cluster.

Templates for new services

New apps get scaffolded from flow templates with a form that asks for the app name, namespace, chart repo, and whether it needs ingress, secrets, or monitoring. It emits the values file and the executable stubs. It’s the difference between adding a service being a ten-minute job and a this-weekend job.