Self-hosting Limitr
There is no Limitr server to deploy. The engine is the @formata/limitr package, and it runs inside your app, in
Node, Bun, Deno, or the browser, wherever your app already runs. No Docker image, no Kubernetes chart, no binary, no
port to open. Self-hosting Limitr is:
npm i @formata/limitr
import { Limitr } from '@formata/limitr';
const limitr = await Limitr.new(policy); // the policy: a document in your repo
That's the whole deployment. Every allow, check, reserve, and settle runs in your process, with no network
call, and the engine never phones home. The same engine runs in Limitr Cloud: the only
difference is who runs the parts around it.
What you own
Self-hosted, the engine is the whole product in your process, and you run the parts around it:
| Part | Self-hosted | With Limitr Cloud |
|---|---|---|
| The policy | A document in your repo, reviewed and deployed like code | Edited and published from Cloud, live everywhere with no deploy |
| Customer state | Your database: save it from events, load it on start | Stored and synced across your servers |
| Billing periods | Your subscriptions (setCustomerPeriod), or the calendar | From Cloud's subscriptions and contracts |
| Billing | From the events, with your Stripe or whatever you bill with | Ledgers and invoices through your own Stripe |
| Contracts and deals | Rules in the policy, assigned in your code | Set up by sales or finance in Cloud |
| Analytics | Your own, from the events | Usage, cost, and margin by customer, feature, model, or task |
Nothing here is a lesser engine. The policy and the calls are the same, so you can start self-hosted and move to Cloud, or the other way, without changing your integration.
System requirements
- A JavaScript runtime: Node, Bun, Deno, or a browser, from the same package. The engine is WebAssembly, so there's nothing to compile and nothing platform-specific.
- Memory: one engine holds its policy and its customers in memory, and grows with customers, not with calls (runtime). A long-running engine stays flat.
- No other services. The engine makes no network calls unless the policy's own capabilities do, and those are off until you allow them.
Running it in production
- One engine per process. Create it once at startup and share it, since it holds customer state in memory.
- Save customer state. Save a customer's record when its events say it changed, and load records on start or on first use. Save customer state shows the loop, including running more than one server.
- Bill from the events.
meter-overage,cap-limit, and the threshold events carry the price and cost of each call. Bill from events covers what to bill from and who pays. - Keep the policy in version control. It's a plain document. Review changes like code, and
test it in CI with
stof testor from TypeScript. - Keep the sandbox closed.
Limitr.newpolicies have no file system or network access. If a capability needs the network, allow only the hosts it calls:limitr.doc.allowHttp(['api.example.com'])(runtime). - Scale by process. Every process runs its own engine against the shared customer state in your database. There's no engine cluster to size: capacity is your app's capacity.
Running it in your app, in the tutorial, puts all of this together in one runnable example.