My VPS bill is one number. Here is what each app on it actually costs
My server bill arrives as one line. Six dollars a month, four apps, and a Postgres database I forgot about. When I wondered which app was costing me the most, I had no answer, because the bill does not know about apps.
Levelrail has two features that attack this from different sides. One splits your real server bill across the apps by measured usage. The other estimates what an app would cost on typical metered cloud pricing. I ran both on a throwaway local instance. This post covers exactly what each computes, what I saw, and where the numbers stop being trustworthy.
TL;DR
| Question | Feature | Input | Output |
|---|---|---|---|
| What does each app cost me on my server? | Usage and cost | Your monthly bill, vCPUs, memory, measured usage | Per app, project, environment and team cost, plus idle capacity |
| What would this app cost elsewhere? | Cost estimate | The app's CPU and memory | Three reference prices per month |
| Am I on track this month? | Cost explorer | The same daily snapshots | Trend, forecast and budgets |
Both are showback, not billing. Levelrail pulls nothing from a cloud provider and sends no invoice.
Splitting the bill by usage
You enter what each server costs per month, with its vCPUs and memory. Levelrail divides the bill by the days in the month, and each app gets a share of the day by how much of the server it used:
share = cpu_weight * (cores used / server vCPUs) + (1 - cpu_weight) * (memory used / server memory) app cost for the day = daily bill * share cpu_weight defaults to 50 percent. Whatever no app used is idle capacity. By default it shows as Unallocated, so you can see how much of the bill nobody used. If the apps together use more than the server, Levelrail scales their shares down so the total never exceeds the bill.
Here is the setup I ran, with a made up bill that matches a small VPS:
levelrail-cli usage settings set --mode node --node local=6:2:4 --reprice-month The --node value is NAME=MONTHLY[:VCPUS[:GIB]], so this says a node called local costs 6 a month and has 2 vCPUs and 4 GiB. The --reprice-month flag applies the new prices to the month so far. Without it, past days keep the prices they had when recorded, so a price change never quietly rewrites last month.
Then I asked for the report:
levelrail-cli usage report --period mtd --by resource Period: 2026-11-01 to 2026-11-11 (node) Total: 2.04 USD (- vs previous period) Unallocated (idle capacity): 2.04 USD Forecast month end: 6.00 USD RESOURCE COST PREVIOUS CHANGE database:w2f-main-db 0.00 0.00 - app:w2f-web 0.00 0.00 - I should be plain about what that shows. My instance had two idle containers, a whoami web app and an empty Postgres, and they had run for five minutes. Their measured usage rounds to zero cents, so the whole 2.04 is idle capacity. The forecast of 6.00 is the full monthly bill, which is the right answer for a server doing nothing. The report fits a quiet server. It tells you nothing about load. I did not generate load.
A worked example you can check
Since my test box was idle, here is the arithmetic from the docs formula, not a measured result. Take the same 6 per month server with 2 vCPUs and 4 GiB. November has 30 days, so the daily bill is 6 / 30 = 0.20. Say an app averages 0.5 cores and 1 GiB for the day:
share = 0.5 * (0.5 / 2) + 0.5 * (1 / 4) = 0.125 + 0.125 = 0.25 app cost for the day = 0.20 * 0.25 = 0.05 That app costs 0.05 a day, about 1.50 a month at that usage. The other 75 percent of the day's bill is Unallocated. That is why the method helps on a small server: it shows the idle share, which a per app invoice would hide.
Two settings change the picture. The idle spread, a percent from 0 to 100, distributes idle capacity over apps by usage, so the report adds up to the whole bill. I set it to 100 and repriced:
levelrail-cli usage settings set --idle-spread-pct 100 --reprice-month levelrail-cli usage report --period mtd --csv The CSV then showed small idle shares on the database and the app (0.0313 and 0.0063 for the current day), while the earlier days stayed Unallocated, because nothing had logged usage on them. The docs say history starts when recording starts.
What counts as usage
The collector already keeps the metrics behind this, sampled every 15 seconds. For each day Levelrail records the average and the median (p50) CPU and memory, plus how many seconds the container was reporting. A stopped container accrues nothing, so downtime costs nothing. You choose in the prices dialog whether allocation uses the average or the p50.
You can also price storage per GiB per month and egress per GiB. Storage counts the app's local named volumes at the moment the day was recorded, and only volumes on the control plane's own Docker daemon count.
If you have no single bill to split, the other mode takes a price per vCPU hour and per GiB of memory hour, and an app costs cores * hours * cpu price + GiB * hours * memory price.
The other number: what would this cost elsewhere
The cost estimate is a different tool, and people over-read it, so the docs say plainly: an estimate, not a real bill. It never calls a live pricing API. For one app:
levelrail-cli apps cost w2f-web I had declared resources: { memory: 128Mi, cpu: 0.25 } for the app, and this is what came back:
size: 0.25 vCPU (declared), 0.12 GiB memory (declared) Generic cloud VM $4.00/mo (provider minimum) cpu $1.00 + memory $0.50 Generic managed VM $10.00/mo (provider minimum) cpu $2.00 + memory $0.75 Generic metered container PaaS $7.50/mo cpu $6.25 + memory $1.25 The size comes from the declared limit first. With no limit, it uses the p95 of CPU and memory over the last 7 days. With neither, you get a response priced at each profile's floor, and the basis fields say there is nothing real behind the number.
The formula per profile is max(cpu_cost + memory_cost, floor), using three built in rate profiles:
| Profile | Represents | $/vCPU/month | $/GiB/month | Floor/month |
|---|---|---|---|---|
vm | A bare unmanaged cloud VM | 4 | 4 | 4 |
managed_vm | A managed VM with backups and support bundled | 8 | 6 | 10 |
paas | Metered container PaaS pricing | 25 | 10 | 7 |
The docs say these are round numbers and not any provider's rate card, and I checked the arithmetic against my output: 0.25 * 25 = 6.25 and 0.125 * 10 = 1.25, which gives 7.50. The 0.25 vCPU and 0.125 GiB on the vm profile come to 1.50, under the 4 floor, so the floor applied. The formula has no discounts, sustained use pricing, egress or storage costs. Correct the rates with APP_COST_ESTIMATE_<KEY>_CPU_USD, ..._MEMORY_USD and ..._MINIMUM_USD, then restart the control plane.
I use this number to compare rough sizes, nothing finer. It says nothing about what your actual provider charges.
Budgets and forecasts
The Cost explorer builds on the same snapshots. The docs list grouping by app, project, tag, node or kind, a month end forecast from the mean of the last full days (up to seven), and budgets that alert at thresholds or throttle or pause apps at 100 percent. The CLI forms are levelrail-cli costs explore --by tag --period 30d and levelrail-cli costs budgets create .... I ran costs explore once and it printed 0.00 for my idle app, so I cannot tell you more than the docs do about how the forecast behaves with real traffic.
FAQ
Is this an invoice or a billing integration?
No. The docs call it showback, a report of who used what. Levelrail bills no one and queries no cloud provider.
How accurate is it?
Only as accurate as the bill you enter and the usage it measured. If a server bill is wrong, every cost on it is wrong by the same factor. Days are UTC, and storage is a single reading per day.
Does the platform itself show up as a cost?
Not as its own line. A server has one bill, and the platform and the operating system fall into the idle share.
Can I get the data out?
Yes. levelrail-cli usage report --period 30d --csv > usage.csv writes the resource breakdown, and every command takes --json.
The bottom line
On a small VPS the useful number is rarely the cost of one app. The idle share matters more, the part of the six dollars nobody used. My idle test box says 100 percent of the bill is idle, which is honest and dull. I plan to run this on a server with real traffic for a month and see how close the forecast gets.
If this saved you time, a star on the repo helps others find it.
glincker / levelrail
Self-hosted alternative to Vercel and Coolify. Git push, HTTPS, logs, rollback. No Kubernetes.
Levelrail
Your own Vercel, on your own servers.
Push to deploy with HTTPS, logs, metrics and one-step rollback. One Go binary, no Kubernetes. Apache 2.0.
Website · Docs · Install · Compare · FAQ · Roadmap · Discussions
English · 简体中文 · Español · Português (BR) · Deutsch · Français · 日本語
Turn any Linux server into a private cloud: Git deploys, HTTPS, databases with verified backups, metrics, logs and alerts, without Kubernetes and without a monitoring stack to bolt on. Pre-1.0: single-server is production ready, multi-node is beta (Feature status).
200 MBidle RAM, 1.5% of one CPU | 1 binaryGo control plane, Docker runtime | 360one-click service templates | 240+MCP tools for coding agents |
Install
curl -fsSL https://levelrail.com/install.sh | sudo sh Needs Linux (amd64 or arm64) with systemd and root; the installer adds Docker. Open ports…
Sources checked
Levelrail docs and code, read at the v1.0.1 release branch: docs/usage-and-cost.md, docs/cost-estimate.md, docs/cost-explorer.md, and the reference rates in internal/costestimate. Command output is from a local instance I started with levelrail-cli dev, built from that branch. The worked example is arithmetic from the documented formula, not a measurement.


