Put a person in the date, write the changelog first, cut until it fits. Then ship it to five people, not three hundred.
Rae Doughty
@two_vcpu_club
I deploy a Django app for a nonprofit on the cheapest server that will hold it. Most of my expertise comes from making builds fit in 1GB of RAM without crying.
63 credit Contributor
- From answers
- 0
- From questions
- 63
Do not cut over in one go. Keep both for a week and run them on the same tickets, otherwise every rough edge in the new setup reads as "the new model is worse" when it is actually you not knowing the keyboard shortcuts yet. A week of overlap costs you one month of one subscription and saves you the migration you have to undo.
And fire it once on purpose, in a throwaway project, before you rely on it. A kill switch you have never tested is a hope with a deployment attached. Ours had a permissions problem that we would only ever have discovered on the worst possible day.
Also worth stating plainly: middleware should not be your authorisation. Verifying a signature there is fine as a cheap redirect for logged-out users, but the actual check belongs where the data is read, in the route handler or the server component. If somebody ever hits your API directly, middleware is not what saves you. Treat it as UX, not as a gate, and the pressure to cram a database call in there disappears.
Worth checking the project level default too, since a lot of people are running with an old value they set once. In the current docs the platform default duration is five minutes on every plan with fluid compute, with a higher ceiling on paid plans, and there is a per function extended duration option in beta that goes considerably further. But your project settings can override that downward, and dashboard settings under Functions is where a forgotten fifteen second default hides. Check what your project says before assuming the platform is the constraint.
Answering (3): your own development traffic counts. Same project, same daily quota. Every hot reload that re-runs a query, every time you open the console and browse a collection, every integration test run.
Use the local emulator suite for day to day work, and a separate project for staging so it has its own quota. For a lot of small apps this alone is the difference between comfortably inside the limit and running out at 3pm, and it costs nothing to change.
I measured this properly because I was about to buy a laptop over it. Baseline on our monorepo, opened at the root, peaked around 6GB resident and completions averaged just under two seconds after the first hour. Opening a single service directory instead brought it to well under 2GB with sub-300ms completions on the same machine.
The interesting part is that turning off analyses on top of that made almost no additional difference once the workspace was scoped correctly. Scope first, then tune. Tuning a badly scoped workspace is rearranging deck chairs.
The barrel file thing is real - one import of @/lib was dragging in the email client and a PDF renderer into an endpoint that returns JSON. Server chunk went from 4.1MB to 900KB.
Whichever route you take, the important thing is that sending is guarded by a per-user-per-day uniqueness constraint rather than by the enqueue being correct. I have watched a well intentioned retry on a dispatcher re-enqueue a whole page, and the only reason it did not send eight thousand duplicate digests is that there was a unique index on user and date that made the second attempt a no-op. Make the database refuse the duplicate. Do not rely on the scheduler being right.
You need a sent-today marker with an index that the sweep can use, otherwise the sweep itself becomes the expensive query. That is the one thing to get right.
Tests are the one place I let it write freely, because a wrong test fails loudly and you learn from it in ten seconds. Wrong implementation code just quietly works until it does not.
Far more common than the copy answers in this thread. Check the actual load waterfall on a throttled connection before rewriting a single word.
You already found the problem and then asked a different question. The skill you are missing is not typing the function, it is holding the shape of a program in your head long enough to know the generated code is wrong. Keep the assistant, but make it explain instead of produce for the next month and see if the gap closes.
Also worth separating leak from heap growth, because they look identical from the outside. A garbage collected runtime will happily let the heap grow toward whatever ceiling it thinks it has before it works hard at collecting, and on a small box the defaults are not always what you want. Try pinning the old space limit to something well under your container limit and watch whether the curve flattens and stabilises rather than climbing. If it flattens, you never had a leak, you had a runtime doing exactly what it was told. If it still climbs and then the process dies, it is a real leak.
Do not feel stupid, feel relieved. That is a one afternoon fix instead of a one quarter rewrite.
Different angle: the regulatory risk to someone sending 30 a day is not the thing that will actually cost you. The thing that costs you is one irritated recipient who decides to make a point, and then you spend a fortnight writing careful letters instead of building. That risk is managed by not being annoying - one email, one short follow-up, obvious opt-out, never a fourth message - far more than by picking the right legal basis in a document nobody reads.
Check whether your platform has a minimum instances setting before building anything. It is the supported version of what you are trying to fake, it keeps N instances alive rather than one, and it is usually priced as "you are paying for an always-on instance" - which is the same money as the container with none of the migration. If it exists, use it. If it does not, take the container.
Agreed on order. Runtime, then build tooling, then framework, then your own code. Doing it in any other order is where the whole weekend goes.
budgets are a monthly reporting tool and the underlying cost data they read is only refreshed a few times a day, so no amount of tuning gets you an hourly alarm out of them. two things actually page us. anomaly detection catches the shape changes but it's usually next-day, so it's a safety net rather than a first responder. the thing that actually wakes someone up is a scheduled query against the hourly cost and usage report in athena, running every hour, comparing the last hour's spend per service against the trailing median. it's about fifteen lines of sql and a lambda.
Did exactly what the comment above says three months ago and it worked, so here are the mechanics. Found the partner page buried in the docs footer, filled a form asking for company name (put my own name, sole trader, nobody cared), a one paragraph description, and expected monthly call volume. Estimated high on purpose. Got a reply in eleven days from an actual person saying the use case was fine and pointing me at a rate limit tier I didn't know existed.
It isn't a contract and they could change their mind, but it is in an email and I sleep better. They also asked me not to use their brand name in my domain, which I would absolutely have done otherwise.
Charge from day one, launch to ten people, keep the other thirty. Thin is survivable, silence is not.
Mostly right, but the once-a-year version is worth doing precisely because it catches the forgotten subscriptions, which are pure waste rather than a tradeoff. Once a year, an hour, then leave it alone.
The pooler is not the only pool in the picture. Your ORM opens its own client-side pool inside every function instance, and the default size is usually derived from CPU count - commonly something like 5 to 9 connections per instance. 120 instances times 5 is 600 clients arriving at a pooler that will happily accept a bounded number and then refuse the rest.
Append the limit to the pooled URL:
postgres://...:6543/db?pgbouncer=true&connection_limit=1
One connection per instance is correct for serverless. The instance handles one request at a time, so a pool inside it buys you nothing and costs you 4 to 8 sockets.
While you are there, confirm the host really is the pooled one. On most providers the pooled endpoint is a different hostname, not just a different port, and it is very easy to change 5432 to 6543 on the direct host and get a connection refused or a silent fallback depending on the provider.
It reads like a safety limit and it is actually a per-instance allocation. The mental model that fixes it: in serverless you are not sizing a pool for your app, you are sizing it for one request.
So the channel only makes sense if losing an item is genuinely acceptable.