Autoscaling has a reputation it does not deserve. To a lot of people it sounds like magic you switch on and then hope about: the platform decides how many copies of your app to run, and you find out how that went when the bill arrives. That version is worth being nervous about. The version worth wanting is smaller and much calmer. You give your app a floor and a ceiling, and between those two numbers it adds copies when load rises and removes them when load falls. You are never caught at zero when traffic shows up, and you can never be scaled into a number you did not agree to. That is the whole idea, and it shipped in Burrow in v0.8.
What autoscaling actually gives you
Start with the problem it solves, because if you do not have the problem you do not need the feature.
Your app runs as some number of copies, and each copy handles a share of the incoming requests. If you run one copy and traffic doubles, that copy has to work twice as hard, and past a point it cannot keep up: requests queue, latency climbs, some of them time out. The fix is more copies to share the load. If you run three copies through a quiet night, you are paying for two of them to do nothing. The fix is fewer copies when it is quiet.
Doing that by hand is fine when you see it coming. You notice traffic is up, you tell your agent to scale the app to three, and three copies now share the work. For a one day bump that is the right move. But you cannot sit and watch the number all day, and the moments that most need more copies (a post that took off, a morning rush, a customer running a big import) are exactly the moments you are least likely to be watching.
Autoscaling is the standing instruction that does the watching for you. You tell Burrow: keep this app between a floor and a ceiling, add copies when they are working hard, remove them when they are idle. From then on the copy count rises and falls with demand on its own, and it stays inside the two numbers you chose. You are not handing the decision to a black box. You are handing it a range and letting it move within it.
The two numbers are the whole point, so it is worth being clear about what each one is for.
The floor is the minimum number of copies. Its job is to make sure you are never at zero, or never below what a normal amount of traffic needs, when requests arrive. Some platforms will happily scale you down to nothing when it is quiet and then cold start when the next request comes in, which is cheap on paper and feels awful in practice: the first visitor after a lull waits for the app to wake up. A floor of one, or two, says never go below this, there is always something warm and ready.
The ceiling is the maximum number of copies, and it is the number that lets you relax. This is the cap that a spike, a stampede, or a runaway loop somewhere upstream can never push past. Without a ceiling, "scale with demand" is an open ended promise, and open ended promises about how many copies to run are the same thing as open ended promises about the bill. With a ceiling, the worst case is bounded: if demand goes through the roof, your app scales up to the ceiling and stops, and beyond that point it degrades (requests get slower, some queue) rather than quietly spending money you did not plan to spend. A bounded slowdown is recoverable. A surprise invoice for ten times your normal spend is a bad morning.
Put the two together and autoscaling stops being a leap of faith. It is a promise with edges: at least this many, at most this many, and it moves between them so you do not have to.
Setting it up in Burrow
You add autoscaling to an app by giving it that floor and ceiling. On your own terminal:
burrow app autoscale web --min 2 --max 6
That says: keep the web app between two copies and six copies, and adjust within that range as load changes. Burrow adds autoscaling to your app and starts adjusting the copy count for you, scaling on how hard the copies are working (CPU) by sensible defaults, so you do not have to invent a rule to get started. Two copies is your floor, six is your ceiling, and you never have to think about the number in between again unless you want to.
You can do the same thing by talking to your agent, which is how most day to day operating happens. You say something like "set up autoscaling on web, floor of two, ceiling of six," and the agent proposes exactly that. It does not just apply it silently, which brings us to the part that makes this safe to hand off.
When you want a fixed number of copies again, you turn autoscaling off and it goes back to holding a steady count:
burrow app autoscale web off
And when you just want to set an exact number by hand, with no automatic adjustment at all, that is a different command and always available:
burrow app scale web 3
Scaling by hand and autoscaling are two tools for two situations. A manual scale is right for a bump you can see (a launch today, a sale this weekend) where you know the number you want and you want it to stay put. Autoscaling is right for a pattern you cannot babysit (daily peaks and troughs, unpredictable traffic) where you want the range set once and left alone. You will use both, and switching between them is one line either way.
The ceiling is also a cost guardrail
Here is the thing that makes the ceiling do double duty. It is not only a performance setting. It is the closest thing to a spending limit that a copy count can give you.
Think about what actually runs up a bill when an app scales. Each copy uses some slice of your cluster: some memory, some CPU. More copies, more of your cluster in use. On a platform where someone else runs your app, that translates directly into more money, and "scale to meet demand with no upper bound" is a sentence that can end in a number you would not have signed off on. This is the honest place to concede a point: a single small app is often cheaper and simpler on a PaaS today, and their autoscaling will handle a spike for you. The catch is that they are the ones deciding how far up it goes.
Burrow runs your app on the cluster you own, so the ceiling is your bound, set by you, on your own hardware. When you say --max 6, you are saying this app will never take more than six copies' worth of my cluster, no matter what the internet throws at it. If a scraper hammers you, if a loop in some client goes haywire, if a link goes viral at 3am, the answer is the same: scale up to six, then hold. The failure mode is a slower app for a while, which you can see and fix, not a spending event you discover after the fact.
That is why bounds you set beat magic scaling every time. The interesting question about autoscaling is never "how high can it go," it is "how high have I decided to let it go," and Burrow makes that your decision, on your infrastructure.
How it fits with the guardrail you already set
Burrow has a guardrail model underneath all of this: for operations that change your running system, a human sets the policy and the agent proposes within it. You set policy with a command like:
burrow guard set --env prod app.autoscale confirm
That means when the agent wants to set up or change autoscaling on prod, it holds and waits for your sign off rather than just doing it. You could set it to allow if you are happy for the agent to manage autoscaling freely, or deny if you want autoscaling changes to be yours alone. The point is that the policy is yours, and the agent proposes against it.
Autoscaling has its own safety belt on top of that. There is a separate guardrail, app.replica_ceiling, that caps the maximum number of copies your app is ever allowed to have, by any route. The autoscale guardrail is bounded by it: the agent cannot set an autoscaling ceiling higher than the replica ceiling you set. So even if you let the agent manage autoscaling on its own, it cannot quietly raise the roof. You define the absolute maximum once, as policy, and every autoscaling decision, whether you make it or the agent proposes it, lives underneath that cap.
This is the same shape as everything else in Burrow. The agent can do the work of tuning the range as your traffic changes, but the outer bound is a line you drew, and it cannot cross that line. The floor and ceiling you set on the autoscaler are the day to day range; the replica ceiling guardrail is the wall behind them.
When autoscaling helps, and when it does not
Autoscaling is a specific tool for a specific problem, and part of using it well is knowing what it does not fix.
Autoscaling helps when your problem is load. More people are using the app than a fixed number of copies can comfortably serve, and the copies are healthy, they are just outnumbered. That is the case it was built for. Spread the work across more copies, and each one breathes again. When traffic falls, hand the extra copies back. If your latency climbs with your traffic and falls with it, autoscaling is exactly the lever.
Autoscaling does not help when your problem is inside a single copy. The clearest example is a memory leak. If each copy slowly eats memory until it falls over, adding more copies does not fix anything; it just gives you more copies that will each slowly eat memory and fall over. You have not solved the leak, you have multiplied it. A memory leak is a code problem, not a scaling problem, and the fix is in the app, not in the copy count. The same goes for a slow database query that every copy has to wait on, or a downstream service that is the real bottleneck: more copies of your app all queue behind the same slow thing. Autoscaling moves work around; it does not make a broken piece healthy.
The tell is simple. If one copy under light load is fine and the trouble only shows up as traffic grows, that is load, and autoscaling is the tool. If one copy under light load already misbehaves (leaks, stalls, errors on a cold path), that is a bug, and no number of copies will paper over it. Diagnose before you scale. The worst outcome is scaling up in response to a bug and paying more for a problem you have now hidden instead of fixed.
There is also a resource shaped question that autoscaling does not answer, and it is worth being straight about because it is a real gap today. Autoscaling controls how many copies you run. It does not control how much memory or CPU any single copy is allowed to take. A cap per service (a hard limit on the memory or CPU each service can use, so one hungry service cannot starve the rest of your cluster) is a different guardrail, and it is coming soon rather than shipped. So today, autoscaling is the honest answer for "handle more traffic," and it is fully shipped and real. Per service resource caps are on the way for "keep any one service from taking too much," and I am not going to describe them as working before they are.
Reading whether it is working
A standing instruction is only as good as your ability to check it, so operating autoscaling day to day is mostly about seeing what it is doing. You do that the same way you do everything else in Burrow: you ask.
The first thing to look at is the app's status, which tells you how many copies are running right now and whether they are healthy. You ask your agent how web is doing, and it reads it back: how many copies, ready and available, and whether autoscaling is on with what floor and ceiling. If you set a floor of two and a ceiling of six and you see two copies on a quiet afternoon, that is autoscaling working correctly: it is sitting at the floor because there is nothing to scale up for. If you see six copies during a rush, that is also it working: it climbed to the ceiling under load. Six copies pinned at the ceiling with latency still climbing is the signal that your ceiling might be too low for your real peak, or that the bottleneck is somewhere a copy count cannot help.
The way you read whether autoscaling is helping is by watching the two things it is supposed to move: the copy count and your latency. If traffic rose, the copy count rose to meet it, and latency stayed flat, the system did its job, you never noticed, and that quiet is the success. If traffic rose, the copy count rose, and latency climbed anyway, the copies are not where the problem is. If the copy count is stuck at the floor while you are clearly under load, that is worth a look too. In every case the move is the same: ask the agent what the running app is actually doing, read the copy count and the health back, and decide from what is true rather than what you assume.
The good news is that most days there is nothing to read, because most days it just works. You set the floor and the ceiling once, and the app breathes in and out between them while you get on with building. You check in when something feels off, and the check in is a sentence, not a session.
Why bounded beats magic
Pull it together and the argument is simple. Autoscaling is genuinely useful: it keeps your app responsive under load without you babysitting a number, and it hands resources back when the rush is over so you are not paying for idle copies. But the reason it is safe to hand to a standing instruction, and safe to let an agent help manage, is entirely down to the bounds. A floor means you are never caught cold. A ceiling means a spike is a slowdown you can see, not a bill you cannot. A replica ceiling guardrail behind that means even an agent tuning the range cannot raise the roof. And running on the cluster you own means the maximum is a number you set on your own hardware, not one negotiated for you on someone else's platform.
That is the difference between autoscaling you can trust and autoscaling you have to hope about. Not that the system is clever enough to always get it right, but that you drew the edges, so even a wrong call lands somewhere you already decided you could live with. Set the floor, set the ceiling, then let it run and spend your attention on the app instead of the copy count.