Why App Engine?
The main reason this app runs on App Engine is that I like it and am comfortable with it, let’s be real here. But there are some actually good reasons other than maintainer preference that App Engine is a good choice for this particular app, so this doc is going to go into a few of the reasons why. One thing to do if you are really interested is just ask whatever AI you like to look at the code and see if it can reproduce what it does at the same price and with the same resources as your architecture of choice. No need to trust me, trust the robot! (My own robot tells me that I should ask you to compare “behavior, failure semantics, idle cost, and operational guarantees,” so maybe do that if you want to get fancy)
Reliability
I have another website, Shfl, that I built 7 years ago or so. It isn’t super complicated, but it does a lot behind the scenes — there’s a crawler, a data-management piece, and the regular app, which gets somewhere around 85k visits/8k unique users a month, so not super-popular but people use it. Some people log in and use it every day, like me. I wrote that thing entirely in Vim three or four times before I got it right, stuck it on App Engine, and haven’t touched it in a year and a half. It just sits there and runs, cheerfully adding more servers when the traffic needs them and shutting them down when it doesn’t. App Engine handles instance replacement, TLS termination, request routing, supported-runtime patching, and the mechanics of scaling up and down; I have to configure or worry about none of that. It’s about as close to zero maintenance as you can get, and that’s what I want for Lagniappe as well, to feel more like software you install on your computer rather than a bunch of web infrastructure that requires management.
Price
I have done quite a bit of work in order to make this whole app fit onto one App Engine instance that can sleep and generate minimal charges. Data storage costs money, but both Datastore and Cloud Storage are pretty reasonable, and at the scale of an individual or a small business that storage generally costs significantly less than a running webserver does. And while you could certainly replicate all of Lagniappe’s functionality on one container that you could install and run cheaply somewhere, there are a lot of costs associated with that approach that aren’t denominated in money — flexibility, reliability, latency, etc. — and if that container also contains the database, scheduler, queue, object store, and workers, it has reproduced the features by collapsing them into one machine, which comes with its own set of risks.
Scaling
This app is specifically designed to scale to zero. If you’re General Motors you aren’t going to use it. But it does scale, and in the ways that I personally think are important — for instance, there’s no separate database server or local-disk ceiling to worry about. Cloud Storage can easily scale to terabytes, and Datastore can grow without creating new database servers or sharding. If you want to serve more traffic with no cold starts? That’s configurable. The bill would grow, but the application itself would not need to suddenly change its architecture in order to accommodate increased usage.
The default installation uses App Engine’s standard environment with basic scaling. This is a tradeoff — a cold start is noticeable — but a configurable one. While Lagniappe does quite a bit of work to make startup fast and wake the server when the browser becomes visible, and the UI makes it clear when the server is offline, if you don’t want to wait Lagniappe can be configured that way — one always warm server (the configuration of this demo instance) costs around $30/month as of this writing. An individual installation using basic scaling is entirely usable, it has all the same capabilities as one that is always running, it just costs a lot less.
Simplicity
App Engine integrates with some Google Cloud services in ways that elsewhere would require cobbling together a number of separately-configured services. A good example is Cloud Tasks — with App Engine those tasks just wake the server up with a single authenticated HTTP request. No task-specific machines required, just a couple of extra routes. This has some downsides, jobs that might take some time are using the same server and the same workers as your web traffic, but the operational simplicity of having the same server service both users and background tasks removes quite a bit of wiring.
Another thing I like is app.yaml itself. It is a small routing layer in front of the application, not merely a description of the container. Lagniappe declares its CSS, JavaScript, fonts, images, and other static assets there, so App Engine serves them directly without waking a sleeping application instance. It also sends only an explicit list of dynamic URL families into Python. The endless internet background noise looking for /admin.php, old WordPress plugins, and whatever vulnerability was announced that morning falls through to a static not-found page without starting the application. Those requests may still consume a negligible amount of static delivery or bandwidth, but they do not turn into application compute. Cloud Run can be placed behind infrastructure that accomplishes the same thing, but it does not provide this little routing layer as part of the service.
Testing
Before Lagniappe gets a release that can be downloaded and installed, it has to pass an extensive test suite. This uses App Engine too — the release candidate is an actual App Engine deployment that uses the same service types and provider APIs as it would in production, with isolated test identities and test-prefixed data. A separate Cloud Run job runs the suite from outside the application, using Playwright to drive a real browser against that exact zero-traffic version.
IAM/ADC
Another thing I particularly appreciate is GCloud’s IAM (Identity and Access Management) and ADC (Application Default Credentials). For instance, the human installer of the app must have the broad project-level permissions needed to create resources and grant roles, but the deployed application never receives those permissions. It runs as a dedicated service account with only the permissions required at runtime. Locally, ADC begins with my user identity, and Lagniappe exchanges that for a short-lived credential impersonating the runtime service account. That lets the same client libraries exercise nearly the same project and permission boundary locally without downloading a service-account key. Cloud Tasks signs its internal requests as that runtime identity, while hosted testing repeats the same pattern with dedicated test accounts.
All of this can be installed in about fifteen minutes with a script. I’m not claiming that App Engine is the cheapest thing out there, or that the app couldn’t be recreated in AWS or as one portable container. I do believe that the tradeoff for accepting the considerable GCP coupling is worth it — the app costs almost nothing when idle, requires almost no ongoing maintenance, and still has a straightforward path to grow from one user to hundreds.
For more about why Lagniappe was built, refer to Why Lagniappe?, and an installation guide can be found in the manual.