A flat bill is the failure mode
Launching the video coach put us $266.94 a month underwater, and we did not find out for two weeks. Idle capacity was the cost, not the customers.

On 25 July 2026 we moved Eloist's analysis engine from one small shared machine to two large dedicated ones. The change was right on its own terms: analysis had been slow, and a video coach was about to land on top of it. It also multiplied the largest cost line in the business by about nineteen, and we did not notice for two weeks.
We did not notice because no screen in the product carried a cost number. Revenue read healthy the whole time, and it was healthy: $29.25 a month, growing every few days. It was also $29.25 against $296.19 a month of infrastructure. Two weeks of that. Gross margin was minus $266.94, and that figure did not exist anywhere a person could see it.
Gross margin has to be a tile
The first move was not a machine change. It was making the number exist, next to revenue, on the same screen, refreshed on the same schedule. A dashboard that tracks signups, activations, subscriptions and churn, with no line for what any of it costs to serve, will let a business invert its margin while every number on it goes up.
The number was harder to get honestly than it was to define. Fly exposes no dollar figure to an automated caller. Its interfaces carry machine sizes, execution seconds and byte counts. None of them carries a price, so spend has to be derived, and a derived number is only as good as the reader's ability to see what went into it.
So we measure the half that moves and write down the half that does not. Machine inventory gets pulled live every time the tile renders, because count and size are exactly what changed and caused the step. The price table is checked in as code, with the page it came from and the date it was read. The tile says which half is which, which is what makes the number arguable rather than printed.
Idle capacity was the cost, not the customers
With a cost number in place, the diagnosis pointed away from users within the day. Analysing every chess game the product has ever stored, all of them, from scratch, at the depth it uses for bulk work, costs $19.57 once. One month of one always-on dedicated machine costs $124.00. Marginal compute was a rounding error against fixed capacity.
The video coach behaved the same way. Every delivered film stamps a receipt of what it spent on models and narration. Across 194 delivered films between 25 August and 10 September, the median film cost $0.2903 and the mean $0.3219, with the cheapest at $0.0305 and the dearest at $1.4925.
That receipt is model spend, and what it leaves out matters. It excludes machine time, fallback tokens from a second provider, and any failed attempt, which is stamped separately. A film that needed a retry ran $0.808 against $0.329 for one that went through first time. Even doubled, a film is thirty to eighty cents.
So the whole variable cost of a busy month was a few dollars of models and a fraction of a cent per game analysed. Everything else on the bill was capacity sitting still, waiting for somebody to ask for something.
Three things were keeping the machines awake
Idle capacity only bills if the machine stays up, and modern hosts will suspend a machine that has nothing to do and resume it in under a second. So the question became what was stopping that from happening. Three answers, and the third is the one worth stealing.
- A second engine that existed for headroom, running all day every day, with stopping turned off and a floor of one machine always running. It was $124.00 a month of insurance against a load the product has never had.
- Background analysis jobs on an hourly timer, touching the engine around the clock whether or not any human had asked for anything. Moving them to four windows a day left the member-facing path alone and let the rest of the day go quiet.
- The health probe. It proved the engine was alive by sending it a request through the front door every ten minutes. That request woke the machine every time. The monitoring alone held the engine above 50% busy.
The probe is the general lesson. A check that proves a machine is alive by giving it work is load, and on a fleet designed to sleep it is the load that matters most, because it arrives on a schedule that never lets the machine settle. The fix was to read the machine's state from the control plane and treat suspended as healthy. Busy time fell from 54% to 33%.
# before: two machines, never allowed to stop
count = 2
auto_stop_machines = "off"
auto_start_machines = true
min_machines_running = 1
# after: one machine, suspends when idle, floor of zero
count = 1
auto_stop_machines = "suspend"
auto_start_machines = true
min_machines_running = 0
# resume measured at 0.75s; a suspended machine is billed as stoppedOne detail there is easy to skip and expensive to skip. The live fleet was changed first, by hand, and the config file that describes the intended fleet still said two machines and stopping off. Any routine reconciliation would have quietly put the old fleet back. The saving was not safe until the file agreed with the machines.
A fleet that goes to zero
Rendering video needed the opposite shape. It is bursty: nothing for hours, then a queue of films that somebody is waiting on. The answer was one standing renderer for the ordinary trickle, plus extra machines created on demand that retire themselves the moment there is nothing left to do.
Self-retirement is two settings on the machine and four lines in the worker loop. The settings say destroy this machine when its process exits, and do not restart it. The loop does one drain pass, asks the queue how much work is left, and breaks when the answer is zero.
# machine created with:
# auto_destroy: true restart: { policy: "no" }
while true; do
run_one_drain_pass
n=$(count jobs where status in (queued, running)) || n=1
[ "$n" -eq 0 ] && break # nothing left: exit, host destroys me
sleep 30
done
echo "queue drained, retiring"Two choices in those lines carry the weight. It counts remaining work rather than counting idle seconds, so a slow job never triggers a shutdown that strands the queue. And a failed read defaults to one, not zero, so a network blip keeps the machine alive instead of killing it. A cost optimisation that can lose work is not a cost optimisation.
A $479 reading and a one-dollar day
On 10 September 2026 the snapshot read $479 a month, and gross margin read minus $360.75. An earlier version of this post offered that as proof the fix had worked. That was a mistake, and it is worth correcting in public: nobody was billed $479, and no bill for $479 was ever coming.
The figure is a run rate. It prices every machine that happened to be awake when the sampler fired at 18:40 UTC as though it would run all month. A speedometer reading, not an odometer reading.
What that day cost in money is a different number and a much smaller one. Render machines came up that evening at 19:25, 20:27 and 20:54 UTC, took the queue, and destroyed themselves as it emptied. Five machines, 2.14 machine-hours between them, which at the price we have checked in for that machine size comes to about eighteen cents. Model spend across the whole of 10 September was $0.98. Call the day a dollar.
Both numbers are honest and they answer different questions. The run rate says what we would pay if the fleet stayed the shape it is in right now. The machine-hours say what the work actually cost. On an always-on fleet the two agree, which is why sampling a run rate once a day was fair in July.
Make the fleet elastic and they come apart, and the gap is the whole point. Under the old shape those five machines would have been standing there on the quiet days too. The run rate and the bill would have agreed at $479, every month, for as long as we kept paying it.
It also changes how to read the chart above. One sample a day summarised a fleet that barely moved. The same sampler on a fleet that scales to zero and back will sometimes catch a burst and sometimes miss one completely, so the line has sharp edges that a bill never had. Worth knowing before reading a peak as money that left the account.
A flat bill looks like discipline. More often it is the signature of a fleet that cannot follow demand down.
What actually crossed the line
Revenue did grow. It went from $29.25 a month to $118.25 in 44 days, about four times over, and on its own that would not have been enough. Held against the July cost structure, $118.25 of revenue is still a gross margin of minus $177.94 a month.
The crossing happened on 8 September, at plus $11.25 a month, and the cost side did it. Infrastructure fell $189.19 over the window, which is 2.1 times the $89.00 of revenue added in the same 44 days. For a product with few users and bursty work, the cheapest available growth is usually the capacity nobody is using.
Six things to check on your own fleet
- Put cost on the same screen as revenue, refreshed on the same schedule. A margin that lives in a billing console is a margin nobody reads.
- Split any derived cost number into the measured half and the written-down half, and make the surface say which is which. Inventory moves and must be read live; prices are stable and belong in code with their source and date.
- Price one unit of the actual work: one film, one report, one analysis. If that number is cents and the bill is hundreds, stop tuning the unit and go count the idle machines.
- Find every timer that touches a machine you want asleep, including the health check. Ask what a machine's busy percentage would be with no users at all.
- Give burst capacity two settings, destroy on exit and no restart, and let the work itself decide when to stop. Count remaining work, never idle seconds, and fail safe towards staying alive.
- Change the declared fleet in the same hour you change the live one, or the next routine reconciliation will hand the saving back.
None of this makes a product profitable. It moves the question from what the machines cost to what the customers are worth, which is the question a small product should be spending its attention on. A fleet that follows demand to zero gets out of the way of that question. A fleet that charges the same on a dead Tuesday as on the busiest night of the month keeps answering it for you.
What makes a number a number →