In this series
- Why I modernized my homelab instead of upgrading it
- Making a homelab repository agent-friendly
- Building the new K3s foundation
- Proving the entire platform with one blog
- Handing PostgreSQL over to Amp
- Modernizing an application, not merely moving it (this post)
Rewriting the application also meant finding a safe place to test it and putting authentication in front of it.
At the end of Part V, the next job was to bring Baby Tracker into the new cluster. However, simply moving the existing application was not enough. It was still running on React 16 with an old Create React App build, so I would have ended up with a new Kubernetes cluster running the same old application. That was exactly the kind of like-for-like upgrade I wanted to avoid in Part I.
I asked Amp what was possible if we modernized the application properly. The answer was not just a new frontend. I also needed somewhere private to test it, a better authentication setup and a faster way to deploy changes to beta.
Rewriting Baby Tracker
I first mentioned Baby Tracker in my original homelab post. It was an attempt to keep family data within my home without using any external service. It was also a project for me to learn how to build a single-page application. I built the whole thing manually, long before I started using AI coding agents.
The application was still useful, but the technology behind it had become difficult to maintain. Putting the same build into a new container would not fix that, so Amp rebuilt the frontend with React 19, Vite and TypeScript.
This was not about using the newest framework just for the sake of it. The new code is easier to understand, test and change. More importantly, the next update should not require me to first remember how an old ejected Create React App build works!
Now I had a rewritten application, but code sitting in a repository does not prove that it works. I needed to deploy it to the real cluster and test it without making an unfinished family application public.
Testing it without making it public
In Part IV, I created a LAN-only beta route while testing the new platform with this blog. Baby Tracker was where this beta setup became really useful.
Beta has its own workloads, configuration and data boundary. Its route is only available from my home network, so I can build and deploy the application from a machine at home, open the beta site over HTTPS and test it on the real cluster. Nothing needs to be exposed to the public internet.
This gives me a safe place to find a broken frontend, a wrong environment variable or a bad authorization rule before production is involved. It is much more useful than testing the application only on my development machine because it uses the same Kubernetes platform as production.
The first important test was authentication. The old application did not have its own sign-in flow, so I did not want to copy that design into the rewrite.
Rewiring authentication
The new frontend signs users in with Auth0. Requests to the API then pass through Oathkeeper, which checks the Auth0 token before the request reaches the API. If there is no valid token, the GraphQL request is rejected with a 401 response.
I am also using Auth0 for other applications in the homelab. Home Portal signs users in directly with Auth0, Matrix uses Matrix Authentication Service, Frigate sits behind oauth2-proxy and Grafana uses generic OAuth with a local login kept for emergencies. The blog remains public. They share the same identity provider, but each application still has its own access rules.
The Auth0 CLI is also a surprisingly good match for working with an agent. Amp can start the device authorization flow from the terminal, then I approve the displayed code in my browser. The code expires if I do nothing, so it is much better than giving an agent a long-lived client secret. It is both easy to use and time-limited, which is a good balance for this kind of work. The CLI session created afterwards is separate, so I still only use it on a trusted machine.
At this point the rewritten application and authentication were working in beta. However, deploying every small change was still too slow.
Speeding up beta deployment
Initially, every change had to go through GitHub Actions to build an image, then Flux had to deploy it to beta. The median CI time to publish an image was 225 seconds, and that did not include the rollout or any testing. Waiting almost four minutes for an image makes a small frontend change feel very expensive.
I asked Amp what could make beta development faster while keeping production deployment in Flux. I did not ask for a particular tool. Amp suggested Skaffold, which rebuilds only what changed and deploys it directly to the LAN-only beta environment.
This faster path does not create a separate version of the application. Beta and production use the same shared Kubernetes manifests with separate overlays, so the two environments stay aligned. Skaffold handles beta feedback while Flux remains the only production deployment path.
Skaffold handles the quick beta loop, while production deployment remains with Flux.
The full beta deployment and verification took 78 seconds. The applications became Ready on the Raspberry Pi workers, the real beta pages loaded, authentication was checked and production remained unchanged.
The two timings cover different work: 225 seconds was only the CI image build, while 78 seconds included the full ARM64 beta deployment and verification.
The timings cover different work, so they are not a direct benchmark. What matters is that I can now make a change, deploy it to the real beta hardware and test it while I still remember what I changed!
Production deployment remains deliberately slower. It still needs an immutable image verified in beta, my approval and a reviewed Flux change. The faster feedback loop does not weaken that process.
Baby Tracker is now more than an old application moved into a new container: its frontend, authentication and beta workflow were modernized together, and Amp’s Skaffold suggestion made that workflow fast enough to use. It was a good first test because it is still a fairly simple CRUD SPA. The next application is much harder! Part VII takes the same approach to my home surveillance platform, with multiple live camera streams, recording and network storage.