Finally… Forms!
June, 2026
Biggest Migration Yet
After about six weeks of iterating on the Kubernetes database tables, going back-and-forth between the database schema and the web console forms, I’ve finally deployed the migration to Production.
The migration itself actually isn’t that many lines of code, but it was probably the most important one yet. It included the Kubernetes resource definitions at the database level, foreign keys that define how they relate to one another, and unique constraints that (help to) ensure that the data is valid.
The key here is that I’m not trying to replicate the entire Kubernetes API! If I do that, I’m providing little value with the abstraction.
For this first implementation, there are a couple of key assumptions, such as the choice to only support a single internet-facing port on each container.
This means that I can more reliably upsert records, for example, without having to pass around the IDs of intrinsically ephemeral resources. This assumption also means that I can trivially map every public port to an ingress/HTTPRoute without asking the user to configure anything additional.
The underlying Helm Chart, therefore, does not currently support path-based routing to separate services. This is a clear limitation, but it also should be able to remain an implementation detail for the vast majority of my target users.
As far as a user is concerned, they define a container and attach it to some environments. It’ll then be serving traffic at the domain that they attached, via the container port that they specified.
I think I’ll need to support paths in the medium-term, such as for a /metrics endpoint, but I hope that the constraint of a single internet-facing port won’t be much of an issue for the foreseeable future.
Forms
Behold! My stuff…

It’s messy, and it’s a bit buggy… but these relatively simple forms are getting the job done!
I can perform full CRUD on the ports.
I can also attach/detach environments and edit the associated image tag.
Somehow, that’s about it.
It’s funny because I’ve set out to build a simplified, user-friendly wrapper around a Kubernetes cluster, and by hiding all of the complexity, I occasionally underwhelm myself. Behind the scenes, there is an entire orchestration layer, syncing the database values with generated manifests in git, synced to the EKS Cluster with Argo CD.
Yet, on the surface, it’s so deceptively simple that even I start to worry that it does too little.
We’ll just have to see what other people think once they use it. Shouldn’t be long now.
Bugs
Don’t get me wrong… this is absolutely still in Alpha testing.

The guiding light remains “functionality.” I want this to be functional, deterministic, and consistent.
Visual bugs are mildly embarrassing, but they’re not my biggest concern right now.
Once the general application behaviour is ironed-out and I’ve solicited feedback from some other users, I may be re-writing large portions of the UI anyway.
I may nest everything under a top-level /projects/:project_id route, for example, or I may realize that my table-based list views are not compatible with user expectations.
Until the core concepts and functionality are stable, I can live with some rendering issues here and there.
E2E Tests
One of the fantastic things about using simple tools - HTMX and Pico CSS - for the web console is that the resulting HTML is also very simple.
This means that Playwright is much more reliable than it has typically been for me in any other project that I’ve worked on “in my day job.” Rather than dealing with a finicky shadowDOM and rerendering loops, the HTML loads, maybe fetches some more HTML, and that’s it!
If you wanted to “view source,” in the browser, it’s all right there. No
index.jsmounting shenanigans.
Other than needing to pause for some modal animations (until I figure out what event to wait for), the browser-based tests have worked out-of-the-box using the Playwright test generation tools.
By leveraging Dorsal’s top-level Projects, I can also completely isolate each test. Chrome, Safari, and Firefox tests can all run in-parallel, against the same test database, without affecting one-another.
My approach will be to add e2e tests as I add new functionality, starting with the “Containerized Application” forms. Now, with a single mise run e2e, I can verify that I haven’t regressed in the basic form functionality.
I’m using faker.js for generating random test values. This includes not only randomized names but also selecting random options from a dropdown, for example.
Before each test run, I seed Faker.js and print the test seed to standard out. If and when a test run eventually fails, I’ll be able to reproduce the full run with exactly the same steps. This should allow me to find and fix edge-cases.
Alongside these new browser-based tests, I still have API-level tests that verify manifest generation remains consistent. The latter has proven to be tremendously useful over the last month, as I’ve ripped the guts out of my old manifest generation and replaced it with the Kubernetes tables-based values.
As a result, when I eventually deployed this to Production, all of the new features worked first try!
Once I have real users, I’ll add a Staging environment, of course. In the meantime, that’s not worth the cost. Tests and a local development environment will just have to do.
Almost There
It was around this time last year that I first thought I was “almost there,” but I think I’m actually correct this time.
After all, I’m older and wiser now.
Theoretically, I just need to:
- Add OIDC auth for ECR image pushes from Github
- Test with my personal user
- Fix any egregious bugs
- Call on my first alpha user
The OIDC auth for ECR image pushes from Github, at a high level, just means that users will associate a Github repo with their Containerized Application, and that Github repo will be permitted to push to the associated Containerized Application’s ECR repo.
The Github association is already complete, and it has been core to Dorsal for about a year now.
The latter part should be pretty simple… I just need to create an OIDC endpoint in AWS that knows how to provide scoped credentials for ECR.
The flow would be pretty straightforward, the user performs some action on Github, and that triggers a Github Action. The action would build the image (if necessary), authenticate with the OIDC endpoint, and then push the image to the ECR repo.
Dorsal picks up the image from there and ensures that it gets deployed to the EKS Cluster.
July is already filling up, as is August, but I’m trying to commit myself to getting my personal user running a Containerized Application in Dorsal in the next few weeks.
The moment that is working, I’ll need to push myself to get alpha user(s) in the system.
I have an infinite number of ideas for features and functionality, but none of that matters without users.
At the very least, I need to see an alpha user or two in the system before the end of the quarter.
Wish me luck - let’s go!