Obviously the title is click-bait. I’m not at web-scale; I’m still developing my product. And I don’t use WebObjects. Except that I do. Read on.
I’m using GNUstepWeb, the WebObjects 4.5-compatible web application framework from GNUstep. That lets me use Objective-C as the implementation language; I like me some Java, as used in WebObjects 5.x and the community’s Project WOnder, but I love me some Objective-C.
The traditional way to deploy GSW apps is with apache2, using the mod_gsw module that GSW provides. You give it a configuration file that tells it where to find the app executable and how many instances to launch, and it takes care of running them and routing requests to them in a roughly load-balanced way (typically round-robin).
That approach works well—I had a website up for years that ran a single GSW instance behind apache2 and only ever restarted it for kernel updates and code changes—but has its limitations. There’s no GSW version of WOMonitor—the WebObjects deployment, monitoring and dashboard software—and any attempt at a continuous deployment would need to be built from scratch. I’ve done that for the SE100 reading tracker, but that’s a Go application that ships as a single binary so deployment means copying one file to the server and restarting a systemd service.
Instead, for the GSW app, I have a Containerfile that uses a two-stage process to build and test the app, then to copy it into a container with the runtime dependencies, launch it, and expose the port. In development, I use podman to make sure the build and tests pass in the container. Then I push to codeberg, where Woodpecker-CI builds the image, confirms that the tests pass, and pushes the image to quay.io.
Deployment is in kubernetes, in production that’s on a Linode kubernetes engine cluster. ArgoCD in the cluster watches the repo, applies any configuration changes, pulls the image, and updates the deployment. Instead of apache2 and mod_gsw, I use a standard nginx ingress controller, configured to use sticky sessions so that requests from a client all get forwarded to the same instance. That’s because the default behavior for a WebObjects app is to use in-memory storage for session data; it’s extensible though, so I have a plan to switch to redis for session storage so that routing between different instances is transparent. I have the cluster set up with two app replicas, just because 2 is much closer to infinity than 1 is for testing purposes.
The app uses Firebase for a couple of capabilities: authentication, and cloud storage. WebObjects is designed to work with the Enterprise Objects Framework (EOF), which GNUstep implements as the GNUstep Database Library (GDL2). That works with SQL databases, so the cluster adds a sidecar to the app pods that provides proxy access to the Google Cloud SQL service that provides a postgresql-compatible interface.
That architecture mandates some careful error handling and retry logic in the app itself. The usual deployment scenario for WebObjects assumes the DBMS is a long-running service that’s already available when the app launches, but if the proxy sidecar comes up after the app, the app needs to deal with not having access at launch, and potentially needing to wait to apply pending schema migrations.
To test all of this locally, I use skaffold to configure the cluster running on minikube, with some kustomization to replace the database sidecars with a connection to a real postgresql server on the host, remove ArgoCD, and replace the real firebase connection with an in-cluster emulator service. Along the way, I’ve found a couple of bugs in GSW and GDL2 that I’ve sent fixes for upstream; hopefully, if you try all of this yourself, you have a smoother experience than I did. I’m currently using a patched fork of GDL2 while I wait for one of the fixes to make its way upstream; the Containerfile builds from the fork branch instead of a GDL2 release.
As you know, kubernetes comes from an ancient Greek word that means “more containers than customers”, and that’s the situation for my app. The app’s still under development—slowly, as it’s one of a number of projects I have on the go behind Chiron Codex and my book on AI-augmented software development patterns. You can check out the source on codeberg, and from there you could try out the app yourself—it’s a nature journal—I just don’t think you’ll get much out of it yet.

