It was about quarter to ten in the morning, I was three merge requests into the day’s reviews, and GitLab told me to slow down. Again! I’d spent the previous afternoon convinced the twenty-odd agent sessions working my projects were burning through my rate limit, and I’d started rebuilding how they authenticate to put a stop to it. Except they’d been sat idle all night… nobody left to blame, and the limit still tripping.
So I went looking for whatever was still switched on. It was me.
Twenty appliances on one circuit
GitLab announced new rate limits for GitLab.com in September, and the Free plan went first, on 19 October. The docs list two kinds of limit: a sustained one, counted over an hour, and a burst one, counted per minute. It’s the burst limit that bites, and the easiest way I’ve found to think about it is the trip switch in your fuse box. It doesn’t care how much electricity you used today. It cares how much you tried to pull at once, and if the answer is too much, everything goes dark for a bit. And when it trips, you go and glare at the biggest appliances in the house first. In GitLab’s case “dark” is an HTTP 429 (Too Many Requests) and a header telling you how long to wait.
My circuit has a lot plugged into it. A few weeks ago I wrote about what running fourteen sessions at once costs the person reading them, and fourteen was meant to be the ceiling. I’m past twenty now. They open merge requests, push branches, read pipelines and comment on issues, and until last week they all did it as me, on my personal token.
So when the 429s started turning up, the culprit was obvious. I’d already had to fix the automation side once (the scheduled jobs got their own tokens), and now the limits were hitting the development side too. That afternoon I was grumbling to Claude that a paid subscription was starting to feel unavoidable, and I started giving each agent role a service account of its own. My own usage, I reckoned, was minimal by comparison to the swarm.
I’ve since stopped reckoning things about my own usage.
Something was still switched on
Next morning the sessions had been quiet all night, and the warning arrived within minutes of me sitting down. So I asked the session that runs the estate (it co-ordinates all the others) to find out what was still polling GitLab.
It went about it properly. GitLab’s API responses carry a few RateLimit-* headers, including one called RateLimit-Observed, which is how many requests the server has counted against you in the current window. So it sampled that at ten-second intervals, watched the dev server’s connections, and asked the session on my Mac to log, once a second for five minutes, which processes there were talking to gitlab.com.
What came back cleared most of the suspects:
- The dev server, where all twenty sessions live, made no GitLab calls in the whole run apart from the sampler’s own. (Which, at one request every ten seconds, was quietly eating about six percent of my budget on its own. It stopped.)
- The CI runners poll constantly, but they use runner tokens, and my counter stayed flat the whole time they were at it.
- The Mac had three processes that ever talked to gitlab.com: two runners and Chrome. And Chrome, left alone with a few tabs open, made no requests at all.
Then, at quarter to ten, the counter went from 9 to 78 in ten seconds. Eight minutes later it went from 94 to 130 and the 429 landed. Neither burst came from the dev server, the runners or anything idle.
The kettle was mine
At quarter to ten I was clicking links.
I hadn’t given that a moment’s thought. The sessions fire merge requests at me all day, and most of them are small on purpose, compact and easy to review, which I’m finding I get more comfortable with the more of it I do. So when three or four land together I do the obvious thing and open them all, one after the other, in quick succession. If you review on GitLab.com you’ve probably done the same a hundred times without a second thought. Try opening three within a few seconds and… BOOM! Trip switch.
A GitLab merge request page isn’t one request. The page loads, and then it makes a whole run of follow-up calls to fill itself in: the diff, the discussion, the pipeline, approvals, the bits down the side. Most of those are GraphQL queries (GraphQL being a query API where the page asks for exactly the data it needs, which tends to mean many small asks rather than one big one), and they all count. My first reaction, typed straight back at the time, was that a single page was triggering 70 calls. That turned out to be me getting carried away. The trace couldn’t tell one page from several, and I’d had four open, so the estate session’s better guess was twenty-five to thirty-five a page. Still, that’s most of a minute’s allowance gone in four clicks!
GitLab’s announcement says that browsing the UI will carry on “exactly as they do today”. The docs read a little differently. Their definition of web traffic is “what your browser sends while you use GitLab, such as page loads and the GraphQL queries the pages make”, and it all counts towards the per-user limit. At the time of writing that docs page still describes the Free limits as “proposed”, with a separate allowance of 200 a minute for web traffic, and yet the 429 I got named a burst limit of 100. Perhaps the docs are behind, perhaps I’m reading the headers wrong… either way, it tripped.
And the service accounts? Still the right call, I’m inclined to say, for reasons that had little to do with that morning: twenty sessions sharing one identity was a mess for attribution, whatever it did to the rate limit. But they were never going to protect me from myself. The per-user limit counts whoever is sat at the browser, and that’s me, whatever the agents authenticate as. On Free, all the accounts in a top-level group also share a ceiling of 400 a minute, so spreading the agents across accounts spreads the load without raising the total.
I built a whole identity scheme to keep the swarm’s hands off my budget, and the swarm was innocent. The big appliances were all switched off, and the trip kept going because I kept putting the kettle on.
The price of a bigger supply
Here’s where I’d normally have a go at GitLab, and I can’t, really. I put a lot of load on that platform myself. Nothing is free, it costs them real money to keep the lights on, and the announcement says as much: demand is growing fast and the limits are there “so no single workload can slow the platform for everyone else”. It even names “the automation and agent workloads teams are building on the platform”, and a one-man estate running twenty agents is very much one of those, whatever the morning’s trace said. My own throughput has gone up phenomenally in a matter of weeks. I don’t think they can be blamed for putting the brakes on the Free tier.
I love GitLab as a product, and I’ve promoted it personally and professionally for a long time. That’s despite a rough couple of months on the security front: in September a path traversal in the commits API let anyone read files off a self-managed server without logging in, and ended up on CISA’s list of vulnerabilities being exploited in the wild, then in October came unauthenticated code execution through the regex compiler, reaching all the way back to version 7.0. Both scored a full 10 on CVSS (the industry’s zero-to-ten severity scale). GitLab.com was patched before either was announced, to be fair, so it was the self-managed crowd who had the worse of it.
My problem is the size of the step up. Premium is $29 a user a month, billed annually, so $348 in one go, and the pricing page doesn’t offer a monthly option. For comparison I already pay GitHub about $48 a year for Pro and barely use it, because I prefer GitLab. The new dev server has just had a good rummage through the piggy bank, and a single annual charge that size is a bigger jump than I’m comfortable making for what is, at the end of the day… a hobby. One I’m very passionate about, granted, but a hobby. Monthly payments might soften the blow. A tier somewhere between Free and Premium would do a lot more for one-man bands like me.
I’ve thought about another way out. I could run my own GitLab on the homelab, point the agents at that, tune it for the throughput (and probably tighten security while I’m there), and mirror it all out to the public GitLab. The trouble is that it means doing the actual work behind a curtain and publishing a copy, and I’ve spent most of my career arguing for developing in the open. It kinda defeats the point of all that.
One cup at a time
So for now I’m holding out on Free… just about. The estate session works as a merge desk these days: it co-ordinates the merge requests, paces how they reach me, and handles some of the more primitive ones itself, under very strict instructions about what it can and can’t approve on my behalf. That has thinned out how many land on me at once, which helps.
The longer-term answer is probably “HUD”, the heads-up display I’m building for keeping an eye on the agent sessions. It reads a merge request through the API in a handful of calls rather than a page’s worth of GraphQL, so reviewing from there instead of a browser tab ought to leave plenty of headroom. It isn’t running yet. If GitLab ever puts merge request pages on a diet, or slots a monthly tier in under Premium, I’ll be first in the queue. Until then I’m boiling the kettle one cup at a time.





