<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Finops on lo0 — Tech Blog</title><link>https://blog.lo0.es/en/tags/finops/</link><description>Recent content in Finops on lo0 — Tech Blog</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 07 Sep 2026 18:00:00 +0200</lastBuildDate><atom:link href="https://blog.lo0.es/en/tags/finops/index.xml" rel="self" type="application/rss+xml"/><item><title>LiteLLM and Langfuse: the operational pair, and the four places where the trace is lost between the gateway and the dashboard</title><link>https://blog.lo0.es/en/posts/litellm-langfuse-operational-pair/</link><pubDate>Mon, 07 Sep 2026 18:00:00 +0200</pubDate><guid>https://blog.lo0.es/en/posts/litellm-langfuse-operational-pair/</guid><description>&lt;blockquote>
&lt;p>This post opens the operational track of the control layer. The previous articles left the pieces chosen and assembled: &lt;a href="https://blog.lo0.es/en/posts/choosing-oss-gateway-llm-inference/">choosing the OSS gateway&lt;/a> and &lt;a href="https://blog.lo0.es/en/posts/llm-inference-router-l7-switchboard/">the L7 inference router&lt;/a> explain why LiteLLM ends up in front of the fleet, and &lt;a href="https://blog.lo0.es/en/posts/langfuse-inside-sorting-centre-bottleneck/">Langfuse from the inside&lt;/a> takes apart the backend that receives the traces. Here we deal with the seam between the two, which is where the work of the next three years lives.&lt;/p>
&lt;/blockquote>
&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>The LiteLLM plus Langfuse pair is configured with a list of callbacks and two API keys, and from there four matters decide whether the dashboard is good for operating or only for showing in a demo.&lt;/p>
&lt;p>&lt;strong>There are three integration routes and they are not equivalent.&lt;/strong> The native &lt;code>langfuse&lt;/code> callback is tied to the Python SDK v2 (&lt;code>langfuse&amp;gt;=2.59.7,&amp;lt;3.0&lt;/code> in LiteLLM&amp;rsquo;s &lt;code>pyproject.toml&lt;/code>), which writes against the legacy ingestion that Langfuse Cloud retires on &lt;strong>16 November 2026&lt;/strong>. The &lt;code>langfuse_otel&lt;/code> preset exports over OTLP and is the path recommended in the current documentation of both projects. The OTel v2 route, behind the &lt;code>LITELLM_OTEL_V2=true&lt;/code> flag, changes span names, leaves the success status at &lt;code>UNSET&lt;/code> and, above all, its Langfuse mapper does not read the request metadata: &lt;code>session_id&lt;/code>, &lt;code>trace_user_id&lt;/code>, &lt;code>tags&lt;/code> and &lt;code>trace_id&lt;/code> stop arriving.&lt;/p>
&lt;p>&lt;strong>The cost of a self-hosted model comes out as zero unless a price is registered.&lt;/strong> &lt;code>get_model_info()&lt;/code> raises an exception for a model that is not in the pricing map, &lt;code>completion_cost()&lt;/code> propagates it, the logger catches it, stores it in a debugging field and returns &lt;code>None&lt;/code>, and the payload turns it into &lt;code>0.0&lt;/code>. There is no warning at the default log level. A team&amp;rsquo;s budget is never exhausted because its spend is always zero.&lt;/p>
&lt;p>&lt;strong>Client-gateway-engine correlation does not come out of the box.&lt;/strong> LiteLLM does continue the client&amp;rsquo;s trace if a &lt;code>traceparent&lt;/code> arrives, and it also extracts from it the 32-hex trace-id to use as &lt;code>litellm_trace_id&lt;/code>, which makes the Langfuse identifier equal to the W3C one. Upstream the story changes: &lt;code>forward_traceparent_to_llm_provider&lt;/code> is &lt;code>False&lt;/code> by default, the function that applies it returns early if no &lt;code>otel&lt;/code> logger is loaded, and when it does act it forwards the client&amp;rsquo;s original header, so the vLLM span ends up a sibling of LiteLLM&amp;rsquo;s rather than a child. Add to that the fact that vLLM exports OTLP over gRPC by default and Langfuse does not accept gRPC.&lt;/p>
&lt;p>&lt;strong>Between the request and the dashboard there are four bounded queues that drop events.&lt;/strong> LiteLLM&amp;rsquo;s logging thread pool (100 threads, 10,000 pending tasks, dropping once full), the asynchronous &lt;code>LoggingWorker&lt;/code> (50,000, and once full it aggressively flushes half), OTel&amp;rsquo;s &lt;code>BatchSpanProcessor&lt;/code> (2,048 spans by default) and Langfuse&amp;rsquo;s &lt;code>ClickhouseWriter&lt;/code>, which drops rows after exhausting the retries with no dead-letter queue. None of the four applies back pressure on the request, which is exactly what you want, and none of them guarantees delivery, which is exactly what an auditor asks about.&lt;/p>
&lt;p>The rest of the article develops the four points with configuration applicable to a generic 4×H100 cluster with vLLM, the overhead figures the project itself publishes, and the honest limit of what this data sustains before ENS or ISO/IEC 42001.&lt;/p>
&lt;h2 id="you-are-here-the-seam-between-deploy-and-observe">You are here: the seam between DEPLOY and OBSERVE&lt;/h2>
&lt;p>In the &lt;a href="https://blog.lo0.es/en/posts/anatomy-on-premise-llm-inference-stack-seven-layers/">seven-layer stack&lt;/a>, LiteLLM lives in the gateway layer and Langfuse in the observability one. The separation is clean on the diagram and false in operation: the gateway is the only point in the system that sees the complete request before chopping it up and the complete response after reassembling it, so it is also the only reasonable place from which to emit the trace. The inference engine sees tokens and latencies; it does not see who is asking or how much that team has spent this month.&lt;/p>
&lt;p>That turns LiteLLM into a telemetry producer as well as a traffic distributor, and moves onto the gateway a responsibility that was not in the original design: if the callback fails, the request is served all the same and nobody notices that the dashboard has been incomplete for two days.&lt;/p>
&lt;h2 id="the-analogy-the-toll-booth-that-issues-the-ticket">The analogy: the toll booth that issues the ticket&lt;/h2>
&lt;p>A motorway toll booth does two independent jobs. It raises the barrier, which is its visible function, and it issues a ticket with number plate, time, section and amount, which is the function the concession lives off. The two share a booth and do not share a destination: the barrier responds in a hundred milliseconds because there is a car waiting, and the ticket travels to a processing centre over a slower channel that batches thousands of records before sending them.&lt;/p>
&lt;p>The design is well thought out as long as its consequence is understood. If the processing centre goes down, cars keep going through. If the ticket queue fills up, tickets are thrown away, traffic is not stopped. The concession accepts losing records rather than causing a jam, and charges on a statistical sample it knows to be imperfect.&lt;/p>
&lt;p>LiteLLM and Langfuse work the same way. The four queues in the corresponding section are the slow channel, and all of them prefer to drop rather than block. Whoever builds the pair inheriting the complete analogy gets the operation right. Whoever thinks the ticket is an accounting entry ends up explaining to an auditor why traces from a Tuesday afternoon are missing.&lt;/p>
&lt;h2 id="three-routes-and-one-with-an-expiry-date">Three routes, and one with an expiry date&lt;/h2>
&lt;p>As of September 2026 three paths coexist for getting a request from LiteLLM to Langfuse. LiteLLM&amp;rsquo;s documentation already labels the first as legacy.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Route&lt;/th>
&lt;th>How it is enabled&lt;/th>
&lt;th>Transport&lt;/th>
&lt;th>Status&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Native &lt;code>langfuse&lt;/code> callback&lt;/td>
&lt;td>&lt;code>success_callback: [&amp;quot;langfuse&amp;quot;]&lt;/code>&lt;/td>
&lt;td>Langfuse Python SDK v2, legacy ingestion&lt;/td>
&lt;td>Maintained for compatibility. Tied to &lt;code>langfuse&amp;lt;3.0&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>langfuse_otel&lt;/code> preset (v1)&lt;/td>
&lt;td>&lt;code>callbacks: [&amp;quot;langfuse_otel&amp;quot;]&lt;/code>&lt;/td>
&lt;td>OTLP/HTTP against &lt;code>/api/public/otel&lt;/code>&lt;/td>
&lt;td>Recommended by both projects&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>OTel v2 route&lt;/td>
&lt;td>&lt;code>callbacks: [&amp;quot;langfuse_otel&amp;quot;]&lt;/code> + &lt;code>LITELLM_OTEL_V2=true&lt;/code>&lt;/td>
&lt;td>OTLP/HTTP, new mapper&lt;/td>
&lt;td>Opt-in. Changes semantics and loses metadata&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The date that orders the decision is &lt;strong>16 November 2026&lt;/strong>. Langfuse&amp;rsquo;s compatibility matrix marks the legacy batch ingestion of the Python SDK v2 as retired on that date for Cloud, at which point trace ingestion through that route stops working. On self-hosted the endpoint survives, but a v4 server in &lt;code>events_only&lt;/code> mode returns 400 for every event type except &lt;code>score-create&lt;/code> and &lt;code>sdk-log&lt;/code>.&lt;/p>
&lt;p>Since LiteLLM&amp;rsquo;s native callback is pinned to &lt;code>langfuse&amp;gt;=2.59.7,&amp;lt;3.0&lt;/code> in its own &lt;code>pyproject.toml&lt;/code>, and its code calls APIs that only exist in SDK v2 (&lt;code>self.Langfuse.trace(...)&lt;/code>, &lt;code>trace.generation(...)&lt;/code>), there is no way to move up an SDK without changing route. Anyone who today has &lt;code>success_callback: [&amp;quot;langfuse&amp;quot;]&lt;/code> against Langfuse Cloud has a migration with a date on it. Anyone who has it against a self-hosted Langfuse has one with some slack, because v3 receives security patches until the end of January 2027, but they have one all the same.&lt;/p>
&lt;p>One detail of the OTLP route worth checking in older deployments: the &lt;code>x-langfuse-ingestion-version: 4&lt;/code> header is the one that enables real-time ingestion. Without it the traces arrive, but with a delay that Langfuse&amp;rsquo;s documentation puts at around ten minutes (the compatibility page says fifteen, and that discrepancy remains unresolved in their own documents). LiteLLM started sending it in version 1.95.0, of August 2026. A proxy older than that version against a Langfuse v4 produces the classic symptom: the dashboard works, but it is always behind and nobody knows why.&lt;/p>
&lt;h2 id="the-configuration-that-works-today">The configuration that works today&lt;/h2>
&lt;p>For a generic 4×H100 cluster with vLLM behind it and Langfuse self-hosted in the same Kubernetes, the OTLP route in its v1 variant is the one that gives the most functionality with the fewest surprises.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">model_list&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">model_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">llama-70b&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">litellm_params&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">model&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">hosted_vllm/meta-llama/Llama-3.3-70B-Instruct&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">api_base&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">http://vllm-llama70b.inference.svc.cluster.local:8000/v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">api_key&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">os.environ/VLLM_API_KEY&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># Price derived from the cluster&amp;#39;s €/GPU-hour. Without this, spend = 0.0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">input_cost_per_token&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0.00000031&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">output_cost_per_token&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0.00000089&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">litellm_settings&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">callbacks&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;langfuse_otel&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">turn_off_message_logging&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># a conscious decision, see the PII section&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">langfuse_default_tags&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;model_group&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;user_api_key_team_alias&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;cache_hit&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">general_settings&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">disable_error_logs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># provider errors inflate SpendLogs&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">proxy_batch_write_at&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">60&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The credentials travel through the environment. &lt;code>LANGFUSE_PUBLIC_KEY&lt;/code>, &lt;code>LANGFUSE_SECRET_KEY&lt;/code> and &lt;code>LANGFUSE_HOST&lt;/code> are enough: the preset builds the &lt;code>Authorization: Basic base64(public:secret)&lt;/code> header and adds the ingestion version one, so there is no need to touch &lt;code>OTEL_EXPORTER_OTLP_HEADERS&lt;/code> by hand. The preset&amp;rsquo;s default host points to the US region of Langfuse Cloud, a relevant detail for anyone building a platform with data sovereignty requirements: if the variable is not defined, the telemetry leaves Spain without anyone having decided it.&lt;/p>
&lt;p>For real multi-tenancy, LiteLLM supports per-team Langfuse credentials (&lt;code>POST /team/&amp;lt;id&amp;gt;/callback&lt;/code> with &lt;code>callback_vars&lt;/code>) and per-key ones (&lt;code>metadata.logging[]&lt;/code> when generating the key), and the key&amp;rsquo;s wins over the team&amp;rsquo;s without mixing. There is a limitation that breaks the architecture of anyone who planned to separate tenants across different Langfuse instances: under OTel v2, a per-key or per-team &lt;code>langfuse_host&lt;/code> does not move that tenant to another host, because the exporter fixes the destination at startup and the only thing that varies per tenant are the headers.&lt;/p>
&lt;p>And one operational warning with years of history behind it: &lt;code>MAX_LANGFUSE_INITIALIZED_CLIENTS&lt;/code> is 50 by default because each Langfuse client starts a thread. The comment in LiteLLM&amp;rsquo;s code is explicit about the incident that motivated it, with the CPU at 100% from repeated initialisations. A deployment with per-team credentials and many teams hits that ceiling.&lt;/p>
&lt;h2 id="what-is-lost-when-moving-to-otel-v2">What is lost when moving to OTel v2&lt;/h2>
&lt;p>The &lt;code>LITELLM_OTEL_V2=true&lt;/code> flag brings a step up in quality in the proxy&amp;rsquo;s general instrumentation, and at the same time a concrete regression on the path to Langfuse that the documentation acknowledges without underlining it.&lt;/p>
&lt;p>The Langfuse mapper of the v2 route emits &lt;code>langfuse.observation.type&lt;/code>, &lt;code>langfuse.observation.model.name&lt;/code>, the observation identifier, the model parameters, input and output, &lt;code>usage_details&lt;/code> and &lt;code>cost_details&lt;/code>, plus &lt;code>langfuse.trace.metadata.team_id&lt;/code> and &lt;code>team_alias&lt;/code>. It does not emit &lt;code>session.id&lt;/code>, nor &lt;code>user.id&lt;/code>, nor &lt;code>langfuse.trace.tags&lt;/code>, nor &lt;code>langfuse.trace.id&lt;/code>, nor the generation name, nor &lt;code>parent_observation_id&lt;/code>. LiteLLM&amp;rsquo;s documents put it this way: those attributes are set by the preset from the request and the response, not from metadata supplied by the client.&lt;/p>
&lt;p>The practical consequence is that an application that today groups conversations by sending &lt;code>metadata: {&amp;quot;session_id&amp;quot;: &amp;quot;...&amp;quot;, &amp;quot;trace_user_id&amp;quot;: &amp;quot;...&amp;quot;}&lt;/code> in the request body stops grouping them when the flag is enabled, with no error and no warning. The traces keep arriving, loose.&lt;/p>
&lt;p>The other v2 changes are semantic and affect alerts already written. The root span goes from being called &lt;code>Received Proxy Server Request&lt;/code> to being named after the route (&lt;code>POST /v1/chat/completions&lt;/code>), the inference span goes from &lt;code>litellm_request&lt;/code> to &lt;code>{operation} {model}&lt;/code>, the guardrail spans stop hanging off the inference span and hang off the root instead, and the success status stays at &lt;code>UNSET&lt;/code> instead of &lt;code>OK&lt;/code>. Any rule that fires on &lt;code>status == OK&lt;/code> stops firing. The way back is to remove the flag and restart.&lt;/p>
&lt;h2 id="cost-why-the-trace-says-zero">Cost: why the trace says zero&lt;/h2>
&lt;p>This is the part that takes the most time to diagnose and the least to fix.&lt;/p>
&lt;p>LiteLLM computes the cost of a request from a per-model pricing map. For commercial provider models the map comes filled in. For &lt;code>hosted_vllm/whatever&lt;/code>, which is all there is in an in-house inference factory, the map is empty, and the failure path is as follows:&lt;/p>
&lt;ol>
&lt;li>&lt;code>litellm.get_model_info()&lt;/code> raises an exception with the message &lt;code>This model isn't mapped yet&lt;/code>.&lt;/li>
&lt;li>&lt;code>completion_cost()&lt;/code> and &lt;code>response_cost_calculator()&lt;/code> propagate the exception.&lt;/li>
&lt;li>&lt;code>Logging._response_cost_calculator()&lt;/code> catches it, writes a &lt;code>StandardLoggingModelCostFailureDebugInformation&lt;/code> into &lt;code>response_cost_failure_debug_information&lt;/code>, logs it at &lt;strong>debug&lt;/strong> level and returns &lt;code>None&lt;/code>.&lt;/li>
&lt;li>The standard logging payload does &lt;code>llm_response_cost = raw_response_cost or 0.0&lt;/code>.&lt;/li>
&lt;/ol>
&lt;p>The system delivers a spend of zero, with no visible error at the default log level, and writes it into the &lt;code>spend&lt;/code> column of &lt;code>LiteLLM_SpendLogs&lt;/code>. The per-key, per-team and per-organisation budgets are evaluated against that zero and are never exhausted. The Langfuse dashboard shows a total cost of zero for everything self-hosted, which is precisely the part you wanted to measure.&lt;/p>
&lt;h3 id="registering-the-price">Registering the price&lt;/h3>
&lt;p>The pricing keys are accepted both in &lt;code>litellm_params&lt;/code> and in &lt;code>model_info&lt;/code>, and those in &lt;code>litellm_params&lt;/code> win when they are in both places. The complete set has 91 fields, but for a deployment with vLLM two are enough, and there are three things to know about them.&lt;/p>
&lt;p>The first is that &lt;strong>the price is per individual token, in USD&lt;/strong>. There are no per-thousand variants: searching for &lt;code>per_1k&lt;/code> or &lt;code>per_1000&lt;/code> in the pricing model returns nothing. A three-orders-of-magnitude error here is easy to make and hard to see, because it produces plausible figures.&lt;/p>
&lt;p>The second is that &lt;code>input_cost_per_request&lt;/code> exists in the schema and &lt;strong>is not applied&lt;/strong> in chat calls: a model registered with that field returns a cost of 0.0. To charge per request it has to be converted into a cost per token.&lt;/p>
&lt;p>The third is that &lt;code>input_cost_per_second&lt;/code> needs the response time and is computed as price times &lt;code>response_time_ms / 1000&lt;/code>, so outside the proxy&amp;rsquo;s request path it silently returns zero.&lt;/p>
&lt;p>The number that goes into those two keys comes from the article on &lt;a href="https://blog.lo0.es/en/posts/finops-multi-tenancy-gpu-cluster-who-pays/">FinOps and multi-tenancy with LiteLLM&lt;/a> and the one on &lt;a href="https://blog.lo0.es/en/posts/gpu-hour-to-cost-per-token-on-prem-vs-cloud/">cost per token and per request&lt;/a>: amortisation plus energy divided by tokens served. The example arithmetic, to set the order of magnitude: a cluster whose fully loaded cost is 9.60 USD per hour sustaining 3,000 output tokens per second produces 10.8 million tokens an hour, that is 0.00000089 USD per output token. That is the value that goes into &lt;code>output_cost_per_token&lt;/code>. If the cluster&amp;rsquo;s average utilisation is 30 % and not 100 %, the divisor changes and the price triples, which is the same lever that article develops.&lt;/p>
&lt;p>Before sending traffic, the &lt;code>/spend/calculate&lt;/code> endpoint accepts the same parameters as &lt;code>completion_cost&lt;/code> and returns the computed cost. It is the cheap way to check that the price resolves.&lt;/p>
&lt;h3 id="how-that-number-reaches-langfuse-and-who-wins">How that number reaches Langfuse, and who wins&lt;/h3>
&lt;p>The two integration routes write the cost into different fields. The native callback sends it as &lt;code>usage.total_cost&lt;/code> of the v2 schema, and duplicates it in the trace metadata as &lt;code>litellm_response_cost&lt;/code>. The OTLP preset emits &lt;code>langfuse.observation.cost_details&lt;/code> as a JSON string of the form &lt;code>{&amp;quot;total&amp;quot;: cost}&lt;/code>, and &lt;code>langfuse.observation.usage_details&lt;/code> with input, output and total.&lt;/p>
&lt;p>The rule on the Langfuse side is documented and is the right one for this case: when there is ingested cost and inferred cost, &lt;strong>the ingested one takes priority&lt;/strong>. Langfuse only computes from its own pricing table when the emitter sends no cost, and changes to a model definition apply to new generations, with no retroactive recalculation. A self-hosted model therefore needs no pricing definition in Langfuse: it is enough for LiteLLM to send &lt;code>cost_details&lt;/code>.&lt;/p>
&lt;p>One detail remains that explains a puzzling symptom. Langfuse stores the ingested cost and the computed one in separate columns, &lt;code>totalCost&lt;/code> and &lt;code>calculatedTotalCost&lt;/code>, and the session header totals add up only the first. A mixed deployment where part of the traffic brings cost and part lets Langfuse compute it shows sessions at zero while the individual generations show cost. There is an open issue about it.&lt;/p>
&lt;h3 id="the-tokens-of-a-streaming-response">The tokens of a streaming response&lt;/h3>
&lt;p>Almost all of an assistant&amp;rsquo;s traffic is streamed, and there the OpenAI &lt;code>usage&lt;/code> object only appears if the client asks for &lt;code>stream_options.include_usage&lt;/code>. LiteLLM solves this by injecting the option on its own: on the asynchronous completion routes with &lt;code>stream: true&lt;/code>, if the caller did not set it and all the candidate deployments support it, it adds it and marks &lt;code>_litellm_strip_stream_usage&lt;/code> so the usage block is used in the cost calculation and not returned to the client. The &lt;code>general_settings.always_include_stream_usage&lt;/code> flag forces or disables that behaviour, and is worth setting to &lt;code>True&lt;/code> when capability detection does not recognise the vLLM deployment.&lt;/p>
&lt;p>When the usage is missing anyway, LiteLLM reassembles the response with &lt;code>stream_chunk_builder()&lt;/code> and counts tokens with the tokeniser. It is an approximation, and it produces the second path towards a cost of zero. The two paths to zero are told apart by looking at &lt;code>total_tokens&lt;/code> in &lt;code>LiteLLM_SpendLogs&lt;/code>: zero tokens with a correct price is a usage problem, correct tokens with zero cost is a price problem.&lt;/p>
&lt;h2 id="correlation-from-the-client-to-the-engine">Correlation: from the client to the engine&lt;/h2>
&lt;p>The operational question that justifies the whole setup is being able to take a user complaint, find their request and see where the time went. That requires the trace identifier to survive three hops.&lt;/p>
&lt;div class="diagram" style="max-width:860px;margin:1.5rem auto;">
&lt;svg viewBox="0 0 860 330" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Propagation of the client's traceparent to the LiteLLM gateway and the vLLM engine, with the points where correlation breaks">
&lt;style>
.bx{fill:none;stroke:currentColor;stroke-width:1.3}
.bxd{fill:none;stroke:currentColor;stroke-width:1;stroke-dasharray:4 3}
.t{font:600 13px sans-serif;fill:currentColor}
.h{font:600 12px sans-serif;fill:currentColor}
.s{font:11px sans-serif;fill:currentColor}
.ar{stroke:currentColor;stroke-width:1.2;fill:none;marker-end:url(#ah)}
.ard{stroke:currentColor;stroke-width:1.1;fill:none;stroke-dasharray:5 3;marker-end:url(#ah)}
&lt;/style>
&lt;defs>&lt;marker id="ah" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="currentColor"/>&lt;/marker>&lt;/defs>
&lt;text x="430" y="18" text-anchor="middle" class="t">One identifier, three hops and two places where it is lost&lt;/text>
&lt;rect x="20" y="40" width="150" height="60" class="bx" rx="6"/>
&lt;text x="36" y="62" class="h">Client&lt;/text>
&lt;text x="36" y="78" class="s">traceparent: 00-abc…-01&lt;/text>
&lt;text x="36" y="92" class="s">baggage: session.id=…&lt;/text>
&lt;rect x="240" y="40" width="180" height="60" class="bx" rx="6"/>
&lt;text x="256" y="62" class="h">LiteLLM Proxy&lt;/text>
&lt;text x="256" y="78" class="s">litellm_trace_id = abc…&lt;/text>
&lt;text x="256" y="92" class="s">gateway parent span&lt;/text>
&lt;rect x="500" y="40" width="160" height="60" class="bx" rx="6"/>
&lt;text x="516" y="62" class="h">vLLM&lt;/text>
&lt;text x="516" y="78" class="s">gen_ai.latency.*&lt;/text>
&lt;text x="516" y="92" class="s">OTLP gRPC by default&lt;/text>
&lt;rect x="700" y="40" width="140" height="60" class="bxd" rx="6"/>
&lt;text x="716" y="62" class="h">Collector&lt;/text>
&lt;text x="716" y="78" class="s">gRPC to HTTP&lt;/text>
&lt;text x="716" y="92" class="s">renames attributes&lt;/text>
&lt;path d="M170 70 L236 70" class="ar"/>
&lt;text x="203" y="62" text-anchor="middle" class="s">1&lt;/text>
&lt;path d="M420 70 L496 70" class="ard"/>
&lt;text x="458" y="62" text-anchor="middle" class="s">2&lt;/text>
&lt;path d="M660 70 L696 70" class="ar"/>
&lt;rect x="20" y="140" width="400" height="52" class="bxd" rx="6"/>
&lt;text x="36" y="160" class="h">1 · Inbound: works&lt;/text>
&lt;text x="36" y="177" class="s">LiteLLM extracts the 32-hex trace-id from the traceparent and uses it&lt;/text>
&lt;rect x="440" y="140" width="400" height="52" class="bxd" rx="6"/>
&lt;text x="456" y="160" class="h">2 · Outbound: off by default&lt;/text>
&lt;text x="456" y="177" class="s">forward_traceparent_to_llm_provider = False&lt;/text>
&lt;rect x="20" y="212" width="820" height="96" class="bx" rx="6"/>
&lt;text x="36" y="232" class="h">The three ways hop 2 fails to happen&lt;/text>
&lt;text x="36" y="252" class="s">a · The function returns early if no otel callback is loaded, even with the flag at True&lt;/text>
&lt;text x="36" y="270" class="s">b · It forwards the client header, not its own span context: vLLM ends up a sibling, not a child&lt;/text>
&lt;text x="36" y="288" class="s">c · If the client sends no traceparent, none is created&lt;/text>
&lt;path d="M220 100 L220 136" class="ar"/>
&lt;path d="M640 100 L640 136" class="ar"/>
&lt;/svg>
&lt;/div>
&lt;h3 id="what-does-work">What does work&lt;/h3>
&lt;p>If the client sends &lt;code>traceparent&lt;/code>, LiteLLM&amp;rsquo;s spans hang off the existing trace. In addition, and independently of the OTel subsystem, &lt;code>litellm_pre_call_utils.py&lt;/code> extracts by regular expression the 32-character hexadecimal trace-id and assigns it to &lt;code>data[&amp;quot;litellm_trace_id&amp;quot;]&lt;/code> as a last resort, and takes the &lt;code>session_id&lt;/code> from the W3C &lt;code>baggage&lt;/code> header. That makes the Langfuse trace identifier equal to the W3C trace identifier, which is the most robust correlation key available: with it, an identifier copied from the application log opens the trace in Langfuse with no translation.&lt;/p>
&lt;p>For the routes that are not OTel v2, moreover, headers prefixed with &lt;code>langfuse_&lt;/code> inject per-request metadata. &lt;code>langfuse_trace_id&lt;/code>, &lt;code>langfuse_trace_user_id&lt;/code> and &lt;code>langfuse_trace_metadata&lt;/code> are documented, but the code strips the prefix and writes the rest into the metadata, so any body key works as a header. Two warnings about this: header values arrive as strings and are not interpreted as JSON, and Nginx drops headers with underscores by default (&lt;code>underscores_in_headers off&lt;/code>), which produces the symptom that the metadata works locally and disappears behind the ingress.&lt;/p>
&lt;h3 id="what-does-not-work-without-effort">What does not work without effort&lt;/h3>
&lt;p>Towards the engine, propagation is off. The flag is &lt;code>litellm.forward_traceparent_to_llm_provider&lt;/code>, it is &lt;code>False&lt;/code>, and its implementation has three behaviours to know about before enabling it:&lt;/p>
&lt;ul>
&lt;li>The function returns immediately if &lt;code>open_telemetry_logger is None&lt;/code>. A deployment with &lt;code>callbacks: [&amp;quot;langfuse_otel&amp;quot;]&lt;/code> and without the generic &lt;code>otel&lt;/code> callback propagates nothing, whatever the flag is set to.&lt;/li>
&lt;li>When it does propagate, it copies the client&amp;rsquo;s &lt;code>traceparent&lt;/code> header as is, instead of injecting the context of LiteLLM&amp;rsquo;s active span. The vLLM span ends up hanging off the client&amp;rsquo;s span-id, a sibling of the gateway call&amp;rsquo;s span. The trace is one, the hierarchy is wrong.&lt;/li>
&lt;li>If the client sends no &lt;code>traceparent&lt;/code>, none is created.&lt;/li>
&lt;/ul>
&lt;p>Add to that the fact that LiteLLM&amp;rsquo;s general policy is not to forward client headers to the provider, and that the &lt;code>forward_client_headers_to_llm_api&lt;/code> allowlist covers headers prefixed with &lt;code>x-&lt;/code>, among which &lt;code>traceparent&lt;/code> is not. This is the only route.&lt;/p>
&lt;h3 id="the-vllm-side-and-why-the-collector-stops-being-optional">The vLLM side, and why the Collector stops being optional&lt;/h3>
&lt;p>vLLM enables traces with &lt;code>--otlp-traces-endpoint&lt;/code>, and &lt;code>--collect-detailed-traces&lt;/code> with &lt;code>model&lt;/code>, &lt;code>worker&lt;/code> or &lt;code>all&lt;/code> adds forward and execute times, with a performance impact its own documentation warns about. The attributes it emits are in &lt;code>vllm/tracing/utils.py&lt;/code> and are the most useful part of the setup: &lt;code>gen_ai.latency.time_in_queue&lt;/code>, &lt;code>time_to_first_token&lt;/code>, &lt;code>time_in_scheduler&lt;/code>, &lt;code>time_in_model_prefill&lt;/code>, &lt;code>time_in_model_decode&lt;/code>, &lt;code>time_in_model_forward&lt;/code>, &lt;code>time_in_model_execute&lt;/code>, &lt;code>time_in_model_inference&lt;/code> and &lt;code>e2e&lt;/code>, plus the token counters. That breakdown is what turns a &amp;ldquo;it&amp;rsquo;s slow&amp;rdquo; into &amp;ldquo;the time goes in the queue, not in decode&amp;rdquo;, which is the conversation you want to have during an incident. It fits with what was already covered in &lt;a href="https://blog.lo0.es/en/posts/instrumenting-vllm-otel-measuring-optimisations/">instrumenting vLLM with OTel&lt;/a> and with the metrics in &lt;a href="https://blog.lo0.es/en/posts/anatomy-dcgm-vllm-metrics-analogies-documented-anomalies/">DCGM and vLLM&lt;/a>.&lt;/p>
&lt;p>There are three constraints that decide the architecture:&lt;/p>
&lt;p>vLLM exports over gRPC unless &lt;code>OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf&lt;/code> is set, and its gRPC exporter is built with &lt;code>insecure=True&lt;/code> hard-coded. Langfuse, for its part, does not support gRPC. And vLLM emits the old convention names (&lt;code>gen_ai.usage.prompt_tokens&lt;/code>, &lt;code>gen_ai.usage.completion_tokens&lt;/code>), renamed in the specification to &lt;code>input_tokens&lt;/code> and &lt;code>output_tokens&lt;/code>, plus a &lt;code>gen_ai.latency.*&lt;/code> family that was never standard and that the file itself documents as its own attributes until they are normalised.&lt;/p>
&lt;p>An OpenTelemetry Collector in between solves all three at once: it receives gRPC, exports HTTP with the Basic header towards &lt;code>/api/public/otel/v1/traces&lt;/code>, and renames attributes with a &lt;code>transform&lt;/code>. It is also the only place where tail sampling can be done, which is the next point. And add the warning from the llm-d community, which holds for any deployment with Envoy or sidecars in between: &lt;code>OTEL_SERVICE_NAME&lt;/code> has to be set per engine, because vLLM sends no service name and without it every span arrives as &lt;code>unknown_service&lt;/code>.&lt;/p>
&lt;h2 id="the-four-serial-queues">The four serial queues&lt;/h2>
&lt;p>A request&amp;rsquo;s telemetry crosses four bounded buffers before it is queryable. All four drop once full and none blocks the request.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Queue&lt;/th>
&lt;th>Capacity&lt;/th>
&lt;th>What it does once full&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>LiteLLM logging thread pool&lt;/td>
&lt;td>100 threads, 10,000 pending tasks&lt;/td>
&lt;td>Drops the new ones with a rate-limited warning&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Asynchronous &lt;code>LoggingWorker&lt;/code>&lt;/td>
&lt;td>50,000 events, concurrency 100&lt;/td>
&lt;td>Aggressively flushes 50 % of the queue; &lt;code>enqueue()&lt;/code> documents that it never blocks&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>OTel&amp;rsquo;s &lt;code>BatchSpanProcessor&lt;/code>&lt;/td>
&lt;td>2,048 spans (&lt;code>OTEL_BSP_MAX_QUEUE_SIZE&lt;/code>), batch 512, sent every 5 s&lt;/td>
&lt;td>Drops spans silently&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Langfuse&amp;rsquo;s &lt;code>ClickhouseWriter&lt;/code>&lt;/td>
&lt;td>Bounded retries&lt;/td>
&lt;td>Drops rows after exhausting retries, with no dead-letter queue&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The first is documented in LiteLLM&amp;rsquo;s own code with a sentence that saves half an hour of discussion: logging is best-effort and, once the cap is reached, new entries are dropped with a rate-limited warning instead of being queued forever. The third is a barely visible consequence: LiteLLM builds the &lt;code>BatchSpanProcessor&lt;/code> without adjusting &lt;code>max_queue_size&lt;/code>, so the OTel SDK defaults govern, and no LiteLLM documentation mentions the &lt;code>OTEL_BSP_*&lt;/code> variables that change them. The Langfuse SDK does the same on its own account: it sets batch size and interval, and leaves the queue size at the default.&lt;/p>
&lt;p>On top of this comes a fifth loss point that is not a queue: LiteLLM does not call &lt;code>flush()&lt;/code> per request on the native route, so a &lt;code>SIGKILL&lt;/code> on the pod takes the last batch with it. On Kubernetes, this is an argument for a generous &lt;code>terminationGracePeriodSeconds&lt;/code> and a &lt;code>preStop&lt;/code> that gives some slack.&lt;/p>
&lt;p>None of this is a defect. It is the right design decision for a piece that sits on the critical path of inference. What changes is what can be asserted with the resulting data.&lt;/p>
&lt;h2 id="what-cannot-be-promised-to-the-auditor">What cannot be promised to the auditor&lt;/h2>
&lt;p>A dashboard built on this pipeline answers statistical questions well: what is the p95 of time to first token for the model group, which team consumes the most, what proportion of requests fails, how cost per request evolves. That is what it is designed for, and that is what it is good for.&lt;/p>
&lt;p>It does not answer completeness questions well, which are the ones an ENS auditor or an ISO/IEC 42001 management system auditor asks. Faced with &amp;ldquo;show me that I have a record of every interaction with the AI system during March&amp;rdquo;, the honest answer is that the traces are a best-effort record with four documented points of silent dropping, with no dead-letter queue on the last hop and no durability guarantee on any of them.&lt;/p>
&lt;p>The clean separation is the one already proposed in &lt;a href="https://blog.lo0.es/en/posts/technical-controls-ens-iso-42001-eu-ai-act-cross-mapping/">technical controls ENS, 42001 and the AI Act&lt;/a>: the regulatory audit record is a different artefact, with a different write path, durability guarantees and retention of its own. In the pair this article deals with, the &lt;code>LiteLLM_SpendLogs&lt;/code> table in Postgres is a much better candidate than Langfuse for that function, because it is a transactional write against a relational database, and because it already contains &lt;code>request_id&lt;/code>, &lt;code>api_key&lt;/code> in hashed form, &lt;code>team_id&lt;/code>, &lt;code>organization_id&lt;/code>, &lt;code>end_user&lt;/code>, &lt;code>model&lt;/code>, tokens, &lt;code>startTime&lt;/code>, &lt;code>endTime&lt;/code>, &lt;code>status&lt;/code> and &lt;code>requester_ip_address&lt;/code>. Writing the audit event from the same place and in the same transaction as the spend is the sensible route; delegating it to the tracing pipeline is not.&lt;/p>
&lt;p>Here an operational tension appears that has to be resolved deliberately. LiteLLM&amp;rsquo;s performance recommendation for production includes &lt;code>disable_error_logs: true&lt;/code> and, in very loaded deployments, &lt;code>disable_spend_logs: true&lt;/code>, because provider errors inflate the table and concurrent writes against the same rows produce locks and exhaust the Postgres connections. Disabling &lt;code>spend_logs&lt;/code> to gain performance and at the same time leaning on that table for the audit record are incompatible decisions. Above a thousand requests per second or more than ten instances, the way out is &lt;code>use_redis_transaction_buffer: true&lt;/code>, which batches the spend updates in Redis before pushing them down to Postgres, watching the &lt;code>litellm_in_memory_spend_update_queue_size&lt;/code> and &lt;code>litellm_redis_spend_update_queue_size&lt;/code> metrics.&lt;/p>
&lt;h2 id="pii-switching-off-the-content-before-it-leaves">PII: switching off the content before it leaves&lt;/h2>
&lt;p>An LLM trace contains, by definition, the user&amp;rsquo;s prompt. In an on-premise deployment for a regulated customer, that content may be exactly what cannot leave the application&amp;rsquo;s domain, not even towards the observability backend in the same cluster.&lt;/p>
&lt;p>The controls on the proxy side, from most global to most fine-grained:&lt;/p>
&lt;ul>
&lt;li>&lt;code>litellm_settings.turn_off_message_logging: true&lt;/code> cuts messages and responses in every callback, and keeps the metadata and the spend. The redaction replaces the content with the &lt;code>REDACTED_BY_LITELLM&lt;/code> sentinel in messages, &lt;code>choices&lt;/code>, &lt;code>reasoning_content&lt;/code>, streaming deltas, tool call arguments and in the standard logging object.&lt;/li>
&lt;li>The &lt;code>x-litellm-enable-message-redaction: true&lt;/code> header enables redaction per request, and &lt;code>LiteLLM-Disable-Message-Redaction: true&lt;/code> disables it. The second is checked first and wins over everything else, including the global configuration, which makes it a control to block at the ingress if the policy is that content is never logged.&lt;/li>
&lt;li>&lt;code>redact_user_api_key_info: true&lt;/code> removes the hashed token, the &lt;code>user_id&lt;/code> and the &lt;code>team_id&lt;/code> from the log.&lt;/li>
&lt;li>Per request and per callback, &lt;code>metadata: {&amp;quot;mask_input&amp;quot;: true, &amp;quot;mask_output&amp;quot;: true}&lt;/code>.&lt;/li>
&lt;li>The body parameter &lt;code>&amp;quot;no-log&amp;quot;: true&lt;/code> disables logging of that call. It is a body parameter, not a header, and it can be disabled globally with &lt;code>global_disable_no_log_param: true&lt;/code> so a client cannot make itself invisible.&lt;/li>
&lt;/ul>
&lt;p>On the OTel v2 route the policy is inverted towards the safe side: content is off by default and is opened with &lt;code>OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT&lt;/code>, with values &lt;code>no_content&lt;/code>, &lt;code>span_only&lt;/code>, &lt;code>event_only&lt;/code> and &lt;code>span_and_event&lt;/code>. The documentation underlines that the gate is applied centrally and that a user&amp;rsquo;s request cannot force their prompt towards the backend while capture is disabled. It is also the stance the OpenTelemetry specification recommends, whose preferred pattern for production is to store the content in external storage and leave only the reference in the span.&lt;/p>
&lt;p>One specific version to check in the inventory: below LiteLLM 1.98.0, with per-team Langfuse credentials, the team&amp;rsquo;s own credentials ended up inside its traces. That version changed the origin of the metadata emitted to the &lt;code>StandardLoggingPayload&lt;/code> to cut it off. The same change removed a couple of dozen metadata fields (&lt;code>model_group&lt;/code>, &lt;code>deployment&lt;/code>, &lt;code>queue_time_seconds&lt;/code>, &lt;code>attempted_retries&lt;/code> and others), so it breaks saved dashboards and alerts that filter on them. It is an upgrade with work attached, and it has to be done anyway. The same applies to the general hardening of the stack, covered in &lt;a href="https://blog.lo0.es/en/posts/hardening-secrets-sovereign-llm-stack-defence-in-depth/">hardening and secrets of the sovereign stack&lt;/a>.&lt;/p>
&lt;h2 id="the-price-in-latency">The price in latency&lt;/h2>
&lt;p>LiteLLM publishes two overhead figures that differ by a factor of thirty, and both are theirs.&lt;/p>
&lt;p>The benchmarks page, with Locust, a thousand users with think time and a fake OpenAI endpoint, gives for four instances an &lt;code>x-litellm-overhead-duration-ms&lt;/code> of median 2 ms, p95 8 ms and p99 13 ms, at 1,170 requests per second. For two instances, median 12 ms, p95 29 ms and p99 43 ms. The page itself warns that those figures hold with around 130 in-flight requests, and that a closed-loop client with no think time keeps a thousand in flight and sees on the order of eight times that latency at the same rate, by Little&amp;rsquo;s law.&lt;/p>
&lt;p>The AIGatewayBench benchmark, of July 2026, measures the Python proxy at &lt;strong>257.7 ms of added p99&lt;/strong> with 329.5 MB of peak memory, against 0.7 ms for the Rust variant in beta. That measurement has no callbacks, no spend tracking and no persistence.&lt;/p>
&lt;p>The operational reading is that the first figure is the best case and the second the order of magnitude to plan for agentic load, which is the one that arrives in a closed loop against fast responses. To watch it there are two always-on headers: &lt;code>x-litellm-overhead-duration-ms&lt;/code> and &lt;code>x-litellm-callback-duration-ms&lt;/code>, and the documentation sets the useful threshold of the second at 100 ms, above which the diagnosis is that the payloads are too large.&lt;/p>
&lt;p>Three production settings that appear in LiteLLM&amp;rsquo;s documentation and that are wrong by default:&lt;/p>
&lt;p>&lt;strong>&lt;code>request_timeout&lt;/code> is 6,000 seconds.&lt;/strong> A hundred minutes holding a connection against a hung upstream. The recommendation is to lower it to 600.&lt;/p>
&lt;p>&lt;strong>&lt;code>LITELLM_LOG=DEBUG&lt;/code> serialises the payload with &lt;code>json.dumps(indent=4)&lt;/code> synchronously&lt;/strong>, and the documentation puts the cost of that serialisation at 2 to 5 seconds with payloads over 2 MB. It is the first cause of latency their troubleshooting guide mentions.&lt;/p>
&lt;p>&lt;strong>Background jobs register per uvicorn worker, not per pod.&lt;/strong> With &lt;code>--num_workers 4&lt;/code> and ten replicas that is forty copies of every periodic job. The &lt;code>LITELLM_JOB_ROLE&lt;/code> variable separates the pods that serve traffic from the replica that runs jobs.&lt;/p>
&lt;p>And one failure mode with an open issue that deserves to be in the runbook: under sustained 429s from the upstream, pods have been reported unable to answer readiness probes during startup, with the ensuing restart loop. It fits with what was covered in &lt;a href="https://blog.lo0.es/en/posts/incident-response-runbooks-llm-inference-kafka-keep/">incident response runbooks&lt;/a>.&lt;/p>
&lt;h2 id="start-up-checklist">Start-up checklist&lt;/h2>
&lt;p>Ten checks before signing off the installation of the pair:&lt;/p>
&lt;ol>
&lt;li>&lt;code>callbacks: [&amp;quot;langfuse_otel&amp;quot;]&lt;/code> instead of &lt;code>success_callback: [&amp;quot;langfuse&amp;quot;]&lt;/code>, unless you depend on prompt management, which the OTLP preset does not support.&lt;/li>
&lt;li>&lt;code>LANGFUSE_HOST&lt;/code> defined explicitly. The preset&amp;rsquo;s default value points to the US region.&lt;/li>
&lt;li>LiteLLM at 1.98.0 or higher, because of the team credential leak into the traces, and at 1.95.0 or higher because of the v4 ingestion header.&lt;/li>
&lt;li>A price registered for every self-hosted model, per token and in USD, verified with &lt;code>/spend/calculate&lt;/code> before opening traffic.&lt;/li>
&lt;li>A test request with a known &lt;code>traceparent&lt;/code>, checking that the same identifier appears in Langfuse.&lt;/li>
&lt;li>An OpenTelemetry Collector between vLLM and Langfuse, with &lt;code>OTEL_SERVICE_NAME&lt;/code> per engine and renaming of the old token attributes.&lt;/li>
&lt;li>&lt;code>request_timeout: 600&lt;/code> and &lt;code>LITELLM_LOG&lt;/code> out of &lt;code>DEBUG&lt;/code>.&lt;/li>
&lt;li>Tail sampling in the Collector, with a latency and status code policy, to keep 100 % of what is slow and what failed.&lt;/li>
&lt;li>A written decision about prompt content, and blocking of the &lt;code>LiteLLM-Disable-Message-Redaction&lt;/code> header at the ingress if the policy is not to log.&lt;/li>
&lt;li>A dashboard with &lt;code>x-litellm-overhead-duration-ms&lt;/code>, &lt;code>x-litellm-callback-duration-ms&lt;/code> and Langfuse&amp;rsquo;s ingestion queue depth (&lt;code>langfuse.queue.ingestion.depth&lt;/code>), which is the autoscaling signal for its workers.&lt;/li>
&lt;/ol>
&lt;h2 id="traps-and-things-that-are-not-what-they-look-like">Traps and things that are not what they look like&lt;/h2>
&lt;p>&lt;strong>The trace arrives, but ten minutes late.&lt;/strong> The &lt;code>x-langfuse-ingestion-version: 4&lt;/code> header is missing, which LiteLLM sends from 1.95.0. It is not a ClickHouse capacity problem.&lt;/p>
&lt;p>&lt;strong>The metadata works in development and disappears in production.&lt;/strong> Nginx drops headers with underscores by default, and all of this integration&amp;rsquo;s have one.&lt;/p>
&lt;p>&lt;strong>Session cost comes out as zero and generation cost does not.&lt;/strong> Langfuse adds up only the ingested cost in the session totals, not the one it computes itself.&lt;/p>
&lt;p>&lt;strong>Enabling OTel v2 breaks nothing visible and ungroups the conversations.&lt;/strong> The v2 mapper does not read &lt;code>session_id&lt;/code> or &lt;code>trace_user_id&lt;/code> from the request.&lt;/p>
&lt;p>&lt;strong>A team&amp;rsquo;s budget is never exhausted.&lt;/strong> Its spend is zero because the model has no price, and the calculation failure is only logged at debug level.&lt;/p>
&lt;p>&lt;strong>The alerts stop firing after the migration to v2.&lt;/strong> The success status becomes &lt;code>UNSET&lt;/code>; no rule on &lt;code>OK&lt;/code> is satisfied.&lt;/p>
&lt;p>&lt;strong>Forwarding the &lt;code>traceparent&lt;/code> is enabled and it does not reach the engine.&lt;/strong> The generic &lt;code>otel&lt;/code> callback is missing, without which the function that applies it returns before looking at the flag.&lt;/p>
&lt;p>&lt;strong>All the engines&amp;rsquo; spans appear under &lt;code>unknown_service&lt;/code>.&lt;/strong> vLLM emits no service name; &lt;code>OTEL_SERVICE_NAME&lt;/code> has to be set per deployment.&lt;/p>
&lt;h2 id="closing">Closing&lt;/h2>
&lt;p>The gateway and the tracing backend are the two pieces of the inference stack that take the least time to install and the longest to operate well. The installation fits in a ConfigMap. The operation consists of four decisions no getting-started guide raises: which integration route to take knowing that one expires in November, where the price per token of a model nobody invoices comes from, what to do with the &lt;code>traceparent&lt;/code> at each hop, and what to tell an auditor about data the system drops by design when things get tight.&lt;/p>
&lt;p>The answer to the fourth is the one that orders the other three. This pipeline is operational instrumentation, with statistical precision and best-effort guarantees, and separating it from day one from the regulatory audit record avoids the awkward conversation two years from now. With that separation made, the LiteLLM and Langfuse pair is the pair of pieces that turns a fleet of GPUs into a platform with an owner, a known cost and explainable latency.&lt;/p>
&lt;h2 id="see-also">See also&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/langfuse-inside-sorting-centre-bottleneck/">Langfuse from the inside: the sorting centre&lt;/a> — the six-service architecture and the ten backend knobs behind the receiving end of this integration. That post opens the box; this one connects the cable.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/llm-inference-router-l7-switchboard/">The LLM inference router: the L7 switchboard&lt;/a> — the router&amp;rsquo;s four functions and the minimal LiteLLM Proxy manifest on the generic cluster.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/choosing-oss-gateway-llm-inference/">Choosing the OSS gateway for LLM inference&lt;/a> — the decision prior to this article, with the small print on licences and what is gated in LiteLLM.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/finops-multi-tenancy-gpu-cluster-who-pays/">FinOps and GPU multi-tenancy with LiteLLM&lt;/a> — where the numbers that go into &lt;code>input_cost_per_token&lt;/code> and &lt;code>output_cost_per_token&lt;/code> come from, and why utilisation is the lever.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/gpu-hour-to-cost-per-token-on-prem-vs-cloud/">From GPU-hour to cost per token and per request&lt;/a> — the full arithmetic of the conversion summarised here in one line.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/instrumenting-vllm-otel-measuring-optimisations/">Instrumenting vLLM with OpenTelemetry&lt;/a> — the other end of the correlation, with the detail of what each &lt;code>gen_ai.latency.*&lt;/code> measures.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/llm-tracing-opentelemetry-genai/">LLM tracing with OpenTelemetry GenAI&lt;/a> — the &lt;code>SDK → Collector → backend&lt;/code> pipeline and the two-layer sampling this post takes as given.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/technical-controls-ens-iso-42001-eu-ai-act-cross-mapping/">Technical controls: ENS, ISO 42001 and the EU AI Act&lt;/a> — why the regulatory audit record cannot lean on a best-effort pipeline.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/incident-response-runbooks-llm-inference-kafka-keep/">Incident response runbooks&lt;/a> — where the diagnostic headers and the readiness probe failure mode under sustained 429s fit.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/anatomy-llm-request-production-tour-six-stages/">Anatomy of an LLM request in production&lt;/a> — the complete journey of a request through every layer, of which this post details two.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/litellm-day-2-high-availability/">LiteLLM on day 2: high availability&lt;/a> — the process model, the probes, the retries and the 6,000-second timeout of the proxy that emits these traces.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/litellm-virtual-keys-budgets-limits/">Virtual keys, budgets and limits&lt;/a> — who can call and with what cap, and the audit table that does work as evidence when the traces do not.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/gateway-jwt-audience-trace-identity-seams/">The gateway does not live alone&lt;/a> — the gateway&amp;rsquo;s other four seams, including the identity that never reaches the trace.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/langfuse-v4-what-goes-into-a-trace/">Langfuse v4: what goes into a trace&lt;/a> — the new data model at the receiving end, and the 16 November date with its small print.&lt;/li>
&lt;/ul>
&lt;h2 id="sources">Sources&lt;/h2>
&lt;ul>
&lt;li>LiteLLM, &lt;em>Langfuse integration&lt;/em> (rutas de integración, metadata aceptada, cabeceras &lt;code>langfuse_*&lt;/code>): &lt;a href="https://docs.litellm.ai/docs/observability/langfuse_integration">https://docs.litellm.ai/docs/observability/langfuse_integration&lt;/a>.&lt;/li>
&lt;li>LiteLLM, &lt;em>OpenTelemetry v2&lt;/em> y &lt;em>Migración a OTel v2&lt;/em> (preset, captura de contenido, cambios de semántica): &lt;a href="https://docs.litellm.ai/docs/observability/opentelemetry_v2">https://docs.litellm.ai/docs/observability/opentelemetry_v2&lt;/a> · &lt;a href="https://docs.litellm.ai/docs/observability/opentelemetry_v2_migration">https://docs.litellm.ai/docs/observability/opentelemetry_v2_migration&lt;/a>.&lt;/li>
&lt;li>LiteLLM, &lt;em>Logging&lt;/em> y &lt;em>Config settings&lt;/em> (redacción, &lt;code>turn_off_message_logging&lt;/code>, variables de entorno): &lt;a href="https://docs.litellm.ai/docs/proxy/logging">https://docs.litellm.ai/docs/proxy/logging&lt;/a> · &lt;a href="https://docs.litellm.ai/docs/proxy/config_settings">https://docs.litellm.ai/docs/proxy/config_settings&lt;/a>.&lt;/li>
&lt;li>LiteLLM, &lt;em>Custom pricing&lt;/em> y &lt;em>Spend tracking&lt;/em> (claves de precio, SpendLogs, endpoints de gasto): &lt;a href="https://docs.litellm.ai/docs/proxy/custom_pricing">https://docs.litellm.ai/docs/proxy/custom_pricing&lt;/a> · &lt;a href="https://docs.litellm.ai/docs/proxy/cost_tracking">https://docs.litellm.ai/docs/proxy/cost_tracking&lt;/a>.&lt;/li>
&lt;li>LiteLLM, &lt;em>Production best practices&lt;/em> (topología, &lt;code>request_timeout&lt;/code>, buffer de transacciones en Redis, &lt;code>LITELLM_JOB_ROLE&lt;/code>): &lt;a href="https://docs.litellm.ai/docs/proxy/prod">https://docs.litellm.ai/docs/proxy/prod&lt;/a>.&lt;/li>
&lt;li>LiteLLM, &lt;em>Benchmarks&lt;/em> y &lt;em>Latency overhead&lt;/em> (cifras de sobrecarga y cabeceras de diagnóstico): &lt;a href="https://docs.litellm.ai/docs/benchmarks">https://docs.litellm.ai/docs/benchmarks&lt;/a> · &lt;a href="https://docs.litellm.ai/docs/troubleshoot/latency_overhead">https://docs.litellm.ai/docs/troubleshoot/latency_overhead&lt;/a>.&lt;/li>
&lt;li>LiteLLM, &lt;em>Rust AI Gateway benchmarks&lt;/em> (la cifra de 257,7 ms de p99 del proxy Python): &lt;a href="https://docs.litellm.ai/blog/rust-ai-gateway-benchmarks">https://docs.litellm.ai/blog/rust-ai-gateway-benchmarks&lt;/a>.&lt;/li>
&lt;li>LiteLLM, notas de versión v1.95.0, v1.98.0 y v1.100.0: &lt;a href="https://docs.litellm.ai/release_notes/v1.95.0/v1-95-0">https://docs.litellm.ai/release_notes/v1.95.0/v1-95-0&lt;/a> · &lt;a href="https://docs.litellm.ai/release_notes/v1.98.0/v1-98-0">https://docs.litellm.ai/release_notes/v1.98.0/v1-98-0&lt;/a> · &lt;a href="https://docs.litellm.ai/release_notes/v1.100.0/v1-100-0">https://docs.litellm.ai/release_notes/v1.100.0/v1-100-0&lt;/a>.&lt;/li>
&lt;li>Langfuse, &lt;em>OpenTelemetry&lt;/em> (endpoint, autenticación, tabla de mapeo de atributos, ausencia de gRPC): &lt;a href="https://langfuse.com/integrations/native/opentelemetry">https://langfuse.com/integrations/native/opentelemetry&lt;/a>.&lt;/li>
&lt;li>Langfuse, &lt;em>Compatibility&lt;/em> y &lt;em>Migrate v3 to v4&lt;/em> (la fecha del 16 de noviembre de 2026 y los modos de escritura): &lt;a href="https://langfuse.com/docs/compatibility">https://langfuse.com/docs/compatibility&lt;/a> · &lt;a href="https://langfuse.com/self-hosting/upgrade/upgrade-guides/upgrade-v3-to-v4">https://langfuse.com/self-hosting/upgrade/upgrade-guides/upgrade-v3-to-v4&lt;/a>.&lt;/li>
&lt;li>Langfuse, &lt;em>Token and cost tracking&lt;/em> (prioridad del coste ingerido sobre el inferido): &lt;a href="https://langfuse.com/docs/observability/features/token-and-cost-tracking">https://langfuse.com/docs/observability/features/token-and-cost-tracking&lt;/a>.&lt;/li>
&lt;li>Langfuse, &lt;em>Scaling&lt;/em> y &lt;em>Sampling&lt;/em> (dimensionado, señal de autoescalado, muestreo de cabecera): &lt;a href="https://langfuse.com/self-hosting/configuration/scaling">https://langfuse.com/self-hosting/configuration/scaling&lt;/a> · &lt;a href="https://langfuse.com/docs/observability/features/sampling">https://langfuse.com/docs/observability/features/sampling&lt;/a>.&lt;/li>
&lt;li>Langfuse, issue #13468 — descarte de filas en &lt;code>ClickhouseWriter&lt;/code> sin cola de mensajes muertos: &lt;a href="https://github.com/langfuse/langfuse/issues/13468">https://github.com/langfuse/langfuse/issues/13468&lt;/a>. Issue #15109 — coste de sesión a cero: &lt;a href="https://github.com/langfuse/langfuse/issues/15109">https://github.com/langfuse/langfuse/issues/15109&lt;/a>. Issue #5173 — Nginx y cabeceras con guión bajo: &lt;a href="https://github.com/langfuse/langfuse/issues/5173">https://github.com/langfuse/langfuse/issues/5173&lt;/a>.&lt;/li>
&lt;li>vLLM, &lt;code>vllm/tracing/utils.py&lt;/code> y &lt;code>vllm/tracing/otel.py&lt;/code> en la etiqueta v0.28.0 (atributos emitidos, protocolo por defecto, &lt;code>insecure=True&lt;/code>): &lt;a href="https://github.com/vllm-project/vllm/blob/v0.28.0/vllm/tracing/utils.py">https://github.com/vllm-project/vllm/blob/v0.28.0/vllm/tracing/utils.py&lt;/a>.&lt;/li>
&lt;li>Langfuse, &lt;em>vLLM integration&lt;/em> (la advertencia de que vLLM solo exporta contadores y latencias): &lt;a href="https://langfuse.com/integrations/model-providers/vllm">https://langfuse.com/integrations/model-providers/vllm&lt;/a>.&lt;/li>
&lt;li>LiteLLM, &lt;code>litellm/constants.py&lt;/code> y &lt;code>litellm/litellm_core_utils/logging_worker.py&lt;/code> (capacidades de las colas y política de descarte): &lt;a href="https://github.com/BerriAI/litellm/blob/main/litellm/constants.py">https://github.com/BerriAI/litellm/blob/main/litellm/constants.py&lt;/a>.&lt;/li>
&lt;li>OpenTelemetry, &lt;em>Semantic Conventions for Generative AI&lt;/em> y el repositorio &lt;code>semantic-conventions-genai&lt;/code> (estado de desarrollo, renombrados, captura de contenido): &lt;a href="https://github.com/open-telemetry/semantic-conventions-genai">https://github.com/open-telemetry/semantic-conventions-genai&lt;/a>.&lt;/li>
&lt;li>OpenTelemetry Collector, &lt;code>tailsamplingprocessor&lt;/code> (políticas de muestreo por cola y la restricción de instancia única por traza): &lt;a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/tailsamplingprocessor/README.md">https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/tailsamplingprocessor/README.md&lt;/a>.&lt;/li>
&lt;li>llm-d, &lt;em>End-to-end and fine-grained tracing in llm-d&lt;/em> (&lt;code>OTEL_SERVICE_NAME&lt;/code> y los spans huérfanos tras Envoy): &lt;a href="https://llm-d.ai/blog/end-to-end-and-fine-grained-tracing-in-llm-d">https://llm-d.ai/blog/end-to-end-and-fine-grained-tracing-in-llm-d&lt;/a>.&lt;/li>
&lt;/ul></description></item><item><title>From public cloud to private: what you really lose and which projects fill the gap</title><link>https://blog.lo0.es/en/posts/public-cloud-to-private-what-you-lose-which-projects-fill-gap/</link><pubDate>Mon, 31 Aug 2026 09:15:00 +0200</pubDate><guid>https://blog.lo0.es/en/posts/public-cloud-to-private-what-you-lose-which-projects-fill-gap/</guid><description>&lt;blockquote>
&lt;p>With this one I open a run about the biggest gap a sovereign platform leaves you: how to give your teams the self-service experience of a public cloud, without the public cloud. Here you have the map of the problem; the next two go down into the detail of the tools, &lt;a href="https://blog.lo0.es/en/posts/crossplane-control-plane-private-cloud-api/">Crossplane&lt;/a> as the infrastructure control plane and &lt;a href="https://blog.lo0.es/en/posts/kubevela-score-platform-cli-without-teaching-kubernetes/">KubeVela with Score&lt;/a> as application-centric self-service.&lt;/p>
&lt;/blockquote>
&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>Leaving the public cloud has a solid business case, and I am not making it up. 37signals declared savings of close to two million dollars in 2024 after repatriating, the classic a16z analysis puts cloud spend at around half of the cost of revenue of a software company, and the Barclays CIO survey of 2024 recorded the highest repatriation intent in its history. If your workload is a predictable, sustained base (which is exactly the continuous LLM inference this blog is about) the numbers work out, and in Europe the sovereignty reasons pile on top: the Data Act, the tension with the US CLOUD Act, and that dependence on three hyperscalers that control around 70 % of the European cloud market.&lt;/p>
&lt;p>But the problem is not the compute. Your on-premise GPU rack serves tokens just as well as a rented one. What the cloud was selling you and you now have to rebuild is something else: the elasticity of asking for a hundred machines at ten and handing them back at eleven, the catalogue of hundreds of managed services with an on-call team behind them, the unified provisioning API with fine-grained permissions, the multi-region network, the bill that arrives itemised, and that model where operating all of it was somebody else&amp;rsquo;s problem. All of that you rebuild piece by piece with open source projects, and the rebuild has a cost that the savings figures do not usually tell you about. What follows is the map of what you lose, which project fills each gap, and where a gap remains that nothing will close completely.&lt;/p>
&lt;h2 id="the-analogy-leaving-the-hotel-to-set-up-your-own-house">The analogy: leaving the hotel to set up your own house&lt;/h2>
&lt;p>Think of it like this: living in the public cloud is living in a hotel. You pay a lot per night, but you do not change light bulbs, reception is open at three in the morning, if you need another room you ask for it and it appears, and when you leave you take nothing with you to maintain. Setting up your own house works out much cheaper per square metre if you are going to stay for years, and on top of that you decide who comes in and where your things are kept. The problem arrives the day a pipe bursts: then you do not call reception, because reception is you.&lt;/p>
&lt;p>Repatriation is that move. The savings are real and so is data ownership, but what in the hotel came included in the price of the night is now your to-do list: the plumbing is your network, the boiler is your storage, the concierge who got you anything is the catalogue of managed services you no longer have, and the itemised bill they slid under your door is a cost model you have to build, because electricity and depreciation do not arrive broken down per tenant. The house pays off, but only if you count what it costs to furnish it and fix the pipes, not just the price per square metre against the hotel.&lt;/p>
&lt;p>The rest of the article walks through that to-do list and tells you, for each item, which open source tool does the job the hotel used to do for you.&lt;/p>
&lt;h2 id="why-this-conversation-belongs-to-2026-not-to-always">Why this conversation belongs to 2026, not to always&lt;/h2>
&lt;p>Repatriating is nothing new, but in 2026 three things come together that make it urgent if you run a European AI platform.&lt;/p>
&lt;p>The first is economic, and Andreessen Horowitz put it on the table in 2021 with &lt;em>The Cost of Cloud, a Trillion Dollar Paradox&lt;/em>. Their thesis, which you should read knowing that a16z has interests in the ecosystem it analyses, is that cloud spend averages around 50 % of the cost of revenue in a software company, and that recovering half of that spend would free up margins the market penalises. The cleanest case, and one you can verify, is 37signals: DHH published that their cloud bill dropped from 3.2 to 1.3 million dollars a year after repatriating, with an initial investment of about 700,000 dollars in Dell servers. Now the honest caveat, which the specialist press itself underlines: those savings figures did not include the extra operations staff, nor the energy, nor the cooling. I come back to it in the hidden costs section.&lt;/p>
&lt;p>The second is declared intent. The Barclays CIO survey from the first half of 2024 recorded that 83 % planned to repatriate some workload to private cloud or on-premise in the following twelve months, the highest reading in the series. But read that 83 % precisely: it is the proportion of managers who plan to move &lt;em>some&lt;/em> workload, not the percentage of workloads or of spend that moves. Flexera, in its 2025 report, gives you the more grounded figure: large companies have repatriated around 21 % of the workloads they had in the public cloud, and cost saving is priority number one for almost 60 %.&lt;/p>
&lt;p>The third is regulatory and European, and it is the one that turns repatriation into something more than a FinOps decision. The European Union Data Act came into force in January 2024 and has applied since September 2025; it mandates portability between providers and, from January 2027, bans the exit fees that today chain you to your provider. And underneath it beats a deeper legal tension: the US CLOUD Act allows United States authorities to demand data from an American provider even if it is stored in Frankfurt, which is exactly the argument for why a European region of a hyperscaler does not give you sovereignty. With AWS, Microsoft and Google controlling around 70 % of the European cloud market, the conversation about sovereign cloud stopped being theoretical. I developed this already in &lt;a href="https://blog.lo0.es/en/posts/sovereign-on-premise-vs-hyperscalers-data/">sovereign on-premise versus hyperscalers&lt;/a>.&lt;/p>
&lt;h2 id="what-you-lose-when-you-leave-the-cloud-specifically">What you lose when you leave the cloud, specifically&lt;/h2>
&lt;p>Let us get to the core, ordered by what is going to hurt you most. And one thing before starting: none of these gaps is about compute. They are all about what the cloud put around the compute.&lt;/p>
&lt;p>&lt;strong>Hardware elasticity disappears.&lt;/strong> On-premise, the GPU you did not buy does not exist. The cloud bills you by the hour precisely because it takes on the risk of idle capacity: it lets you ask for a peak and hand it back. With your own iron, you buy the peak up front and pay for it whether it is in use or not, so your profitability depends on high sustained utilisation. Below a certain usage threshold, the cloud beats you again. This is the irreducible gap, the one no tool fills, and if you repatriate a workload with extreme peaks you are making the wrong decision.&lt;/p>
&lt;p>&lt;strong>Managed services become your operations.&lt;/strong> Every box you took for granted (the database, the queue, the object storage, the load balancer, DNS, the secrets manager) stops being a checkbox you tick and becomes a system you install, update and watch yourself. The 37signals case teaches it well: getting out of S3 object storage was the last and hardest part, with petabytes of data and a contract they dragged along for years. Your managed Postgres turns into operating CloudNativePG; your S3 turns into operating MinIO; and so on with every service.&lt;/p>
&lt;p>&lt;strong>You have to build the provisioning control plane yourself.&lt;/strong> AWS gives you a unified API where you ask for a resource with fine-grained permissions and it appears. On-premise that does not come for free: you have to build the control plane that translates &amp;ldquo;I want a database&amp;rdquo; into real resources. It is exactly the gap that &lt;a href="https://blog.lo0.es/en/posts/crossplane-control-plane-private-cloud-api/">Crossplane&lt;/a> fills, and that is why it has its own article.&lt;/p>
&lt;p>&lt;strong>You replace the managed network piece by piece.&lt;/strong> The VPC, the security groups, NAT, the load balancer: every network abstraction in the cloud has its on-premise equivalent (Cilium for network policies on eBPF, MetalLB for the load balancer without ELB, Gateway API for L7 ingress), but they are pieces you assemble, not a service you switch on. A good part of this we already saw in the networking vertical with &lt;a href="https://blog.lo0.es/en/posts/network-numa-cilium-ebpf-dranet-fourth-leg-pinning/">Cilium and eBPF&lt;/a>.&lt;/p>
&lt;p>&lt;strong>Billing stops existing as such.&lt;/strong> In the cloud the cost reaches you broken down by service and by tag; you have a Cost Explorer that answers &amp;ldquo;how much does this team cost me&amp;rdquo;. On-premise there is no natural bill: there is hardware depreciation, an electricity bill and some payroll, and splitting that per workload is a model you have to build. OpenCost helps you with the split per workload inside the cluster, but the total physical cost (depreciation plus energy plus staff) you model separately, as we saw in &lt;a href="https://blog.lo0.es/en/posts/opencost-gpu-cost-allocation-kubernetes/">OpenCost and cost allocation&lt;/a>.&lt;/p>
&lt;p>&lt;strong>You design multi-zone availability yourself.&lt;/strong> The three availability zones the cloud gives you for free turn into several sites, several racks and several utility feeds that you have to plan. 37signals solved it with two datacenters and replication between them. It is neither free nor automatic.&lt;/p>
&lt;p>&lt;strong>And above all, the on-call is yours.&lt;/strong> The cloud was not selling you only compute: it was selling you operations included. When something falls over at three in the morning, in the cloud there is an on-call team at the provider; with open source, that team is you. This is the cost that savings spreadsheets almost never include, and it is the one that decides whether your repatriation was a good idea or a hidden bill.&lt;/p>
&lt;h2 id="the-map-of-projects-that-fill-each-gap">The map of projects that fill each gap&lt;/h2>
&lt;p>With the gaps ordered, the catalogue of substitutes reads itself. I group it by layer, from the lowest to the closest to the developer.&lt;/p>
&lt;p>In the &lt;strong>infrastructure substrate&lt;/strong>, what replicates EC2 virtual machines, virtual networking and disks, you have OpenStack as the classic standard for a complete private cloud, SUSE&amp;rsquo;s Harvester as bare-metal hyperconvergence on KubeVirt managed from Kubernetes, and emerging projects such as Spinifex that rebuild an AWS-compatible API on your own iron. It is the heaviest layer and the one I touch least in this blog, because I assume your cluster already exists.&lt;/p>
&lt;p>In the &lt;strong>provisioning control plane&lt;/strong>, what replicates the AWS IAM and resource API, there is Crossplane, graduated in the CNCF in November 2025, which brings the Kubernetes declarative API model to infrastructure. Alongside it, Cluster API for declarative cluster provisioning. And so you can see how far this reaches in our own field, the Crossplane team itself published Modelplane in mid-2026, an inference control plane built on compositions that serves models with vLLM. This layer is developed by the &lt;a href="https://blog.lo0.es/en/posts/crossplane-control-plane-private-cloud-api/">Crossplane article&lt;/a>.&lt;/p>
&lt;p>In the &lt;strong>developer self-service experience&lt;/strong>, what replicates the AWS console and CLI, you have three different approaches you should not confuse. Backstage is the web portal, the catalogue and the templates, and it already has &lt;a href="https://blog.lo0.es/en/posts/backstage-self-service-portal-llm-platform/">its own article&lt;/a>. KubeVela brings self-service to the application model with a friendly CLI. Score defines a workload specification that is portable between environments. KubeVela and Score share the &lt;a href="https://blog.lo0.es/en/posts/kubevela-score-platform-cli-without-teaching-kubernetes/">third article&lt;/a> because they cover the same layer from angles that complement each other.&lt;/p>
&lt;p>In &lt;strong>on-premise managed services&lt;/strong>, CloudNativePG operates Postgres for you with high availability and backups to object storage, MinIO gives you S3-compatible storage, and OpenCost covers that cost allocation you lost with Cost Explorer.&lt;/p>
&lt;p>The way to read this map is that none of these pieces is surplus if you want the cloud experience: they are the boxes the cloud ticked for you and that you now tick one by one.&lt;/p>
&lt;h2 id="the-gap-you-will-not-close">The gap you will not close&lt;/h2>
&lt;p>I will tell you plainly, because it is the part the &amp;ldquo;leave the cloud&amp;rdquo; brochures skip. No combination of open source projects reproduces 100 % of the public cloud for you, and not for lack of maturity, but by design.&lt;/p>
&lt;p>What you will not replicate is, first, instant hardware elasticity: you still buy the peaks up front. Second, the depth and breadth of the managed services catalogue, hundreds of services with a service level agreement and an on-call team behind them, which no internal team matches. Third, the global multi-region network the cloud gives you almost for free. And fourth, that model of transferred operational responsibility: with open source, operations come back home.&lt;/p>
&lt;p>The correct reading is not that repatriating is a bad idea, but that you swap a predictable operating expense and a delegated responsibility for a cheaper capital investment and a responsibility of your own. For sustained base load it works out. For everything else, think about it.&lt;/p>
&lt;h2 id="when-not-to-repatriate">When NOT to repatriate&lt;/h2>
&lt;p>This section has to be here, because repatriation badly applied is expensive. There are three clear signs that the cloud is still your right answer.&lt;/p>
&lt;p>The first is a workload with extreme peaks or very variable traffic. Most inference deployments operate between 40 and 65 % GPU utilisation, and below roughly 70 % sustained usage the cloud beats you on total cost. If your workload has long troughs, you are buying iron so it can sleep.&lt;/p>
&lt;p>The second is uncertainty or the start of a project. The break-even point in favour of buying only applies to you when utilisation is consistently high and, above all, measured and not projected. Buying GPUs against an optimistic forecast is the fastest way to end up with a warehouse of idle silicon. Sizing it properly is worth a whole article: &lt;a href="https://blog.lo0.es/en/posts/slo-to-number-of-gpus-sizing-justifying-inference-hardware/">sizing and justifying the GPU investment&lt;/a>.&lt;/p>
&lt;p>The third is the lack of an operations team. The total cost of ownership on-premise includes between half and one full-time engineer per GPU server. Without that team, your saving is illusory, because the on-call the cloud included is now covered by nobody. a16z itself frames its thesis as a hybrid approach, not as &amp;ldquo;leave the cloud&amp;rdquo; at all.&lt;/p>
&lt;h2 id="the-numbers-to-ground-it">The numbers, to ground it&lt;/h2>
&lt;p>So as not to leave you with the discussion in the abstract, here are the orders of magnitude, triangulating two sources with opposite biases, a cloud provider and a neutral FinOps tool. An H100 in the cloud runs you between 2.90 and almost 7 dollars per GPU-hour depending on provider; bought, an H100 card is around 31,000 dollars and an eight-way system between 250,000 and 320,000. At full utilisation, a rented H100 passes its purchase price in less than a year, and an eight-way system with second-hand hardware can pay for itself in about seven months. The three-year total cost of an eight-H100 system, counting staff, colocation, energy and cooling, is estimated at between 700,000 and 950,000 dollars, and the threshold where on-premise beats the hyperscalers is around 80 % sustained utilisation.&lt;/p>
&lt;p>The two sources disagree on the exact number, as you would expect, but they agree on the deciding factor: sustained utilisation, measured and not projected. The full working of this calculation is in &lt;a href="https://blog.lo0.es/en/posts/full-tco-on-premise-gpu-cluster-capex-gpu-hour-break-even/">the TCO of an on-premise GPU cluster&lt;/a>. Here the conclusion is enough for you: for continuous base load the numbers work out, and that is exactly the profile of an inference factory.&lt;/p>
&lt;h2 id="for-your-sovereign-inference-factory">For your sovereign inference factory&lt;/h2>
&lt;p>Everything above arranges itself into a practical decision. If you operate an LLM inference platform on your own iron, you are not deciding whether to repatriate (you already took that decision when you built the cluster), but how to rebuild the cloud experience your teams miss.&lt;/p>
&lt;p>The sensible sequence starts with the substrate, which you probably already have in the form of RKE2 and storage. It goes on to the provisioning control plane, where Crossplane turns &amp;ldquo;open a ticket and wait&amp;rdquo; into a declarative API. It continues with the developer experience, where you choose between the Backstage portal, the KubeVela application model or the portable Score specification, depending on whether your people prefer a web or a CLI. And do not forget the on-premise managed services or the cost allocation, because without a bill your platform loses the discipline the cloud imposed by default.&lt;/p>
&lt;p>The next two articles go down into the two layers least covered in this blog: &lt;a href="https://blog.lo0.es/en/posts/crossplane-control-plane-private-cloud-api/">Crossplane&lt;/a> for the infrastructure control plane, and &lt;a href="https://blog.lo0.es/en/posts/kubevela-score-platform-cli-without-teaching-kubernetes/">KubeVela with Score&lt;/a> for CLI self-service centred on the application. With them, the map of this layer is covered, and your own house starts to look like the hotel you left, with the difference that the data stays inside.&lt;/p>
&lt;h2 id="see-also">See also&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/sovereign-on-premise-vs-hyperscalers-data/">Sovereign on-premise versus hyperscalers, with data&lt;/a> — the business and sovereignty case, in detail.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/full-tco-on-premise-gpu-cluster-capex-gpu-hour-break-even/">The TCO of an on-premise GPU cluster&lt;/a> — the full break-even calculation.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/backstage-self-service-portal-llm-platform/">Backstage as a self-service portal&lt;/a> — the portal layer, the third route to self-service.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/five-maturity-levels-platform-under-llm/">The five maturity levels of the platform&lt;/a> — at what point each of these pieces starts to pay off for you.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/blog-map-from-utility-feed-to-token-served/">The map of the blog by layers&lt;/a> — where all of this fits in the complete stack.&lt;/li>
&lt;/ul>
&lt;h2 id="sources">Sources&lt;/h2>
&lt;ul>
&lt;li>Andreessen Horowitz (S. Wang, M. Casado), &lt;em>The Cost of Cloud, a Trillion Dollar Paradox&lt;/em> — &lt;a href="https://a16z.com/the-cost-of-cloud-a-trillion-dollar-paradox/">https://a16z.com/the-cost-of-cloud-a-trillion-dollar-paradox/&lt;/a>&lt;/li>
&lt;li>Data Center Dynamics, &lt;em>37signals claims it saved almost 2M USD last year from cloud repatriation&lt;/em> — &lt;a href="https://www.datacenterdynamics.com/en/news/37signals-claims-it-saved-almost-2m-last-year-from-cloud-repatriation/">https://www.datacenterdynamics.com/en/news/37signals-claims-it-saved-almost-2m-last-year-from-cloud-repatriation/&lt;/a>&lt;/li>
&lt;li>The Register, &lt;em>Developer pockets 2M USD in savings from going cloud-free&lt;/em> — &lt;a href="https://www.theregister.com/2024/10/21/37signals_aws_savings/">https://www.theregister.com/2024/10/21/37signals_aws_savings/&lt;/a>&lt;/li>
&lt;li>Barclays, &lt;em>Technology: 1H24 CIO Survey&lt;/em> (PDF) — &lt;a href="https://8198920.fs1.hubspotusercontent-na1.net/hubfs/8198920/Barclays_Cio_Survey_2024-1.pdf">https://8198920.fs1.hubspotusercontent-na1.net/hubfs/8198920/Barclays_Cio_Survey_2024-1.pdf&lt;/a>&lt;/li>
&lt;li>Channelnomics, &lt;em>Breaking Down the 83% Public Cloud Repatriation Number&lt;/em> — &lt;a href="https://channelnomics.com/breaking-down-the-83-public-cloud-repatriation-number/">https://channelnomics.com/breaking-down-the-83-public-cloud-repatriation-number/&lt;/a>&lt;/li>
&lt;li>The New Stack, &lt;em>Updated Stats on Cloud Sustainability, Repatriation and Cost Optimization&lt;/em> (Flexera 2025) — &lt;a href="https://thenewstack.io/updated-stats-on-cloud-sustainability-repatriation-and-cost-optimization/">https://thenewstack.io/updated-stats-on-cloud-sustainability-repatriation-and-cost-optimization/&lt;/a>&lt;/li>
&lt;li>Comisión Europea, &lt;em>Data Act | Shaping Europe&amp;rsquo;s digital future&lt;/em> — &lt;a href="https://digital-strategy.ec.europa.eu/en/policies/data-act">https://digital-strategy.ec.europa.eu/en/policies/data-act&lt;/a>&lt;/li>
&lt;li>EU Data Act, &lt;em>Article 29 — Gradual withdrawal of switching charges&lt;/em> — &lt;a href="https://www.eu-data-act.com/Data_Act_Article_29.html">https://www.eu-data-act.com/Data_Act_Article_29.html&lt;/a>&lt;/li>
&lt;li>Kiteworks, &lt;em>How the EU Data Act and GDPR Conflict with U.S. CLOUD Act&lt;/em> — &lt;a href="https://www.kiteworks.com/gdpr-compliance/eu-data-act-gdpr-cloud-conflict/">https://www.kiteworks.com/gdpr-compliance/eu-data-act-gdpr-cloud-conflict/&lt;/a>&lt;/li>
&lt;li>Computerworld, &lt;em>EU takes first steps to reduce reliance on US hyperscalers&lt;/em> — &lt;a href="https://www.computerworld.com/article/4181816/eu-takes-first-steps-to-reduce-reliance-on-us-hyperscalers.html">https://www.computerworld.com/article/4181816/eu-takes-first-steps-to-reduce-reliance-on-us-hyperscalers.html&lt;/a>&lt;/li>
&lt;li>CNCF, &lt;em>Announces Graduation of Crossplane&lt;/em> (6-nov-2025) — &lt;a href="https://www.cncf.io/announcements/2025/11/06/cloud-native-computing-foundation-announces-graduation-of-crossplane/">https://www.cncf.io/announcements/2025/11/06/cloud-native-computing-foundation-announces-graduation-of-crossplane/&lt;/a>&lt;/li>
&lt;li>CNCF, &lt;em>Score accepted as a CNCF Sandbox Project&lt;/em> — &lt;a href="https://www.cncf.io/blog/2024/08/08/score-accepted-as-a-cncf-sandbox-project/">https://www.cncf.io/blog/2024/08/08/score-accepted-as-a-cncf-sandbox-project/&lt;/a>&lt;/li>
&lt;li>Harvester HCI (SUSE) — &lt;a href="https://harvesterhci.io/">https://harvesterhci.io/&lt;/a>&lt;/li>
&lt;li>CloudNativePG — &lt;a href="https://cloudnative-pg.io/">https://cloudnative-pg.io/&lt;/a>&lt;/li>
&lt;li>Crossplane Blog, &lt;em>Building Modelplane on Crossplane&lt;/em> — &lt;a href="https://blog.crossplane.io/building-modelplane/">https://blog.crossplane.io/building-modelplane/&lt;/a> · &lt;a href="https://modelplane.ai">https://modelplane.ai&lt;/a>&lt;/li>
&lt;li>Spheron, &lt;em>LLM Inference On-Premise vs GPU Cloud: 2026 Cost and Break-Even Analysis&lt;/em> — &lt;a href="https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/">https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/&lt;/a>&lt;/li>
&lt;li>CloudZero, &lt;em>H100 GPU Cost in 2026: Buy, Rent, and Cloud Pricing Compared&lt;/em> — &lt;a href="https://www.cloudzero.com/blog/h100-gpu-cost/">https://www.cloudzero.com/blog/h100-gpu-cost/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>From the SLO to the number of GPUs: how to size and justify the investment in inference hardware</title><link>https://blog.lo0.es/en/posts/slo-to-number-of-gpus-sizing-justifying-inference-hardware/</link><pubDate>Tue, 16 Jun 2026 11:00:00 +0200</pubDate><guid>https://blog.lo0.es/en/posts/slo-to-number-of-gpus-sizing-justifying-inference-hardware/</guid><description>&lt;blockquote>
&lt;p>Notation: amounts in &lt;strong>N €&lt;/strong> or &lt;strong>N USD&lt;/strong> (source denominated in dollars); decimal point; comma as thousands separator (&lt;code>1{,}234&lt;/code>). Generic example hardware: 4×H100 SXM5 80 GB nodes. The dollar sign is not used (it is the formula delimiter).&lt;/p>
&lt;/blockquote>
&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;ul>
&lt;li>With an SLO of &lt;strong>TTFT P99 ≤ 300 ms&lt;/strong> and &lt;strong>ITL P99 ≤ 50 ms&lt;/strong> for a production chat, peak GPU utilisation must stay at &lt;strong>≤ 63 %&lt;/strong>.&lt;/li>
&lt;li>A service receiving &lt;strong>5M requests/day&lt;/strong> with 512 input tokens and 256 output tokens on average generates an hourly peak of &lt;strong>~11,600 tok/s&lt;/strong> (with a peak factor of 1.8×). With vLLM on H100 SXM5 (Llama-3.3 70B FP8, ~1,850 tok/s at 50 concurrent requests), &lt;strong>≥ 10 GPUs&lt;/strong> are needed at peak to meet the SLO, which translates into &lt;strong>3 4×H100 nodes&lt;/strong> (with headroom).&lt;/li>
&lt;li>The all-in cost of those 3 nodes is ~&lt;strong>83,300 € per year per node&lt;/strong> (3-year capex + base opex); the &lt;strong>€/1M tokens&lt;/strong> in the base scenario at 63 % utilisation is ~&lt;strong>0.37 €&lt;/strong>.&lt;/li>
&lt;li>Against AWS p5 on-demand (~3.90 USD/GPU-hour after the 44 % cut of Jun. 2025), the utilisation &lt;strong>break-even&lt;/strong> sits at &lt;strong>~55 %&lt;/strong>; against neocloud on-demand (~2.01 USD/GPU-hour), there is no feasible break-even at medium utilisation.&lt;/li>
&lt;li>Capex payback happens between &lt;strong>13 and 24 months&lt;/strong> if sustained utilisation exceeds 70 %.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="1-the-sizing-chain-from-demand-to-the-number-of-gpus">1. The sizing chain: from demand to the number of GPUs&lt;/h2>
&lt;p>Sizing an inference platform follows a causal chain of five steps. Each step has a formula; none can be skipped.&lt;/p>
&lt;h3 id="11-step-1--characterise-the-demand">1.1 Step 1 — Characterise the demand&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Parameter&lt;/th>
&lt;th>Symbol&lt;/th>
&lt;th>Example&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Requests per day (average)&lt;/td>
&lt;td>\(D\)&lt;/td>
&lt;td>5,000,000&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Input tokens per request (average)&lt;/td>
&lt;td>\(L_{\text{in}}\)&lt;/td>
&lt;td>512&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Output tokens per request (average)&lt;/td>
&lt;td>\(L_{\text{out}}\)&lt;/td>
&lt;td>256&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Peak factor (peak hour vs average ratio)&lt;/td>
&lt;td>\(k_{\text{peak}}\)&lt;/td>
&lt;td>1.8&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Hours per day with significant traffic&lt;/td>
&lt;td>\(H\)&lt;/td>
&lt;td>16&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The &lt;strong>average output throughput&lt;/strong>:&lt;/p>
$$\dot{T}_{\text{avg}} = \frac{D \times L_{\text{out}}}{86{,}400\,\text{s}} = \frac{5{,}000{,}000 \times 256}{86{,}400} \approx 14{,}815\;\text{tok/s}$$
&lt;p>The &lt;strong>peak throughput&lt;/strong> (busy hour):&lt;/p>
$$\dot{T}_{\text{peak}} = k_{\text{peak}} \times \frac{D \times L_{\text{out}}}{H \times 3{,}600} = 1.8 \times \frac{5{,}000{,}000 \times 256}{16 \times 3{,}600} \approx 40{,}000\;\text{tok/s}$$
&lt;blockquote>
&lt;p>Note: if the traffic profile has very pronounced peaks (peak/average ratio &amp;gt; 3), sizing is done for the peak and average utilisation falls; the sensitivity analysis in §5 quantifies the effect.&lt;/p>
&lt;/blockquote>
&lt;h3 id="12-step-2--set-the-slo-and-derive-the-maximum-utilisation">1.2 Step 2 — Set the SLO and derive the maximum utilisation&lt;/h3>
&lt;p>The latency SLO imposes a ceiling on GPU utilisation. Using queueing theory (M/G/1 model):&lt;/p>
$$\rho_{\max} \approx 1 - \frac{1}{\sqrt{1 + C_{s}^{2}}} \cdot \frac{W_{\text{queue}}^{*}}{\bar{s}}$$
&lt;p>where \(\rho\) is the utilisation, \(W_{\text{queue}}^{*}\) the maximum admissible queueing time and \(\bar{s}\) the average service time per request. For the simplified M/M/1 case with prefill time dominant:&lt;/p>
$$\rho_{\max} = 1 - \frac{W_{\text{queue}}^{*}}{\bar{s} \cdot (1 + W_{\text{queue}}^{*}/\bar{s})}$$
&lt;p>The practical reference table (derived from the Spheron/Little&amp;rsquo;s Law formula, 2026):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>SLO TTFT P99&lt;/th>
&lt;th>Maximum utilisation \(\rho_{\max}\)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>200 ms&lt;/td>
&lt;td>55 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>300 ms&lt;/td>
&lt;td>63 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>400 ms&lt;/td>
&lt;td>70 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>500 ms&lt;/td>
&lt;td>75 %&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>For our example (SLO 300 ms P99): \(\rho_{\max} = 0.63\).&lt;/p>
&lt;p>The ITL SLO imposes an additional constraint: the inference engine must be able to generate the next token in ≤ 50 ms. On H100 SXM5 with vLLM and Llama-3.3 70B FP8, the ITL P50 at 50 concurrent requests is ~20 ms, with P99 ~45 ms. ITL is the dominant constraint only when the batch size is very high (&amp;gt;64 sequences) or VRAM is nearly full.&lt;/p>
&lt;h3 id="13-step-3--required-throughput-and-throughput-per-gpu">1.3 Step 3 — Required throughput and throughput per GPU&lt;/h3>
&lt;p>The throughput the cluster must serve at peak, while respecting \(\rho_{\max}\):&lt;/p>
$$\dot{T}_{\text{required}} = \frac{\dot{T}_{\text{peak}}}{\rho_{\max}} = \frac{40{,}000}{0.63} \approx 63{,}500\;\text{tok/s (installed capacity)}$$
&lt;p>The &lt;strong>throughput per GPU&lt;/strong> (reference benchmark, vLLM v0.18.0, Llama-3.3 70B FP8, H100 SXM5 80 GB, 50 concurrent requests):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Engine&lt;/th>
&lt;th>Throughput (tok/s per GPU)&lt;/th>
&lt;th>TTFT P50 / P95 (50 req)&lt;/th>
&lt;th>Source&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>vLLM 0.18.0&lt;/td>
&lt;td>1,850&lt;/td>
&lt;td>380 ms / 720 ms&lt;/td>
&lt;td>Spheron benchmarks, Mar. 2026&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SGLang 0.5.9&lt;/td>
&lt;td>1,920&lt;/td>
&lt;td>360 ms / 680 ms&lt;/td>
&lt;td>Spheron benchmarks, Mar. 2026&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>TensorRT-LLM 1.2.0&lt;/td>
&lt;td>2,100&lt;/td>
&lt;td>340 ms / 620 ms&lt;/td>
&lt;td>Spheron benchmarks, Mar. 2026&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>The throughput data correspond to the test with Llama 3.3 70B Instruct FP8, 512 input / 256 output tokens, 50 concurrent requests, on bare-metal H100 SXM5. See the engine analysis in &lt;a href="https://blog.lo0.es/en/posts/llm-serving-engines-pareto-frontier-comparison/">serving engines Pareto comparison&lt;/a>.&lt;/p>
&lt;/blockquote>
&lt;p>We use vLLM as the general-purpose production reference: \(\dot{T}_{\text{GPU}} = 1{,}850\) tok/s.&lt;/p>
&lt;h3 id="14-step-4--number-of-gpus-and-number-of-nodes">1.4 Step 4 — Number of GPUs and number of nodes&lt;/h3>
$$N_{\text{GPU}} = \left\lceil \frac{\dot{T}_{\text{required}}}{\dot{T}_{\text{GPU}}} \right\rceil = \left\lceil \frac{63{,}500}{1{,}850} \right\rceil = \lceil 34.3 \rceil = 35\;\text{GPUs}$$
&lt;p>With 4×H100 SXM5 nodes:&lt;/p>
$$N_{\text{nodes}} = \left\lceil \frac{N_{\text{GPU}}}{4} \right\rceil = \left\lceil \frac{35}{4} \right\rceil = 9\;\text{nodes}$$
&lt;p>We add &lt;strong>15 % headroom&lt;/strong> for hardware failures (~5 % annual rate in small clusters), upgrades and unforeseen peaks:&lt;/p>
$$N_{\text{nodes, final}} = \lceil 9 \times 1.15 \rceil = 11\;\text{nodes} \approx 44\;\text{GPUs}$$
&lt;blockquote>
&lt;p>For the TL;DR example (5M requests/day with a 16 h active profile, k 1.8 and an SLO of 300 ms) the number of nodes is 11. The simplified TL;DR case with k=1 and H=24 gives 3 nodes; the difference illustrates the impact of the hourly profile.&lt;/p>
&lt;/blockquote>
&lt;h3 id="15-summary-of-the-chain">1.5 Summary of the chain&lt;/h3>
&lt;div class="diagram" style="max-width:700px;margin:1rem auto;">
&lt;svg viewBox="0 0 700 110" role="img" aria-label="Sizing chain: demand and SLO derive the peak throughput, which divided by the throughput per GPU gives the number of GPUs and of nodes" xmlns="http://www.w3.org/2000/svg">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.2}.ts{font:11px sans-serif;fill:currentColor}.arr{fill:none;stroke:currentColor;stroke-width:1.2;marker-end:url(#a)}&lt;/style>
&lt;defs>
&lt;marker id="a" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto">
&lt;path d="M0,0 L6,3 L0,6 Z" fill="currentColor"/>
&lt;/marker>
&lt;/defs>
&lt;rect class="bx" x="10" y="30" width="110" height="50" rx="4"/>
&lt;text x="65" y="52" class="ts" text-anchor="middle">Demand&lt;/text>
&lt;text x="65" y="66" class="ts" text-anchor="middle">D, L, k, H&lt;/text>
&lt;line class="arr" x1="120" y1="55" x2="155" y2="55"/>
&lt;rect class="bx" x="155" y="30" width="110" height="50" rx="4"/>
&lt;text x="210" y="52" class="ts" text-anchor="middle">SLO&lt;/text>
&lt;text x="210" y="66" class="ts" text-anchor="middle">TTFT/ITL P99 → ρ&lt;/text>
&lt;line class="arr" x1="265" y1="55" x2="300" y2="55"/>
&lt;rect class="bx" x="300" y="30" width="110" height="50" rx="4"/>
&lt;text x="355" y="52" class="ts" text-anchor="middle">Required&lt;/text>
&lt;text x="355" y="66" class="ts" text-anchor="middle">throughput&lt;/text>
&lt;line class="arr" x1="410" y1="55" x2="445" y2="55"/>
&lt;rect class="bx" x="445" y="30" width="110" height="50" rx="4"/>
&lt;text x="500" y="52" class="ts" text-anchor="middle">N GPUs /&lt;/text>
&lt;text x="500" y="66" class="ts" text-anchor="middle">N nodes&lt;/text>
&lt;line class="arr" x1="555" y1="55" x2="590" y2="55"/>
&lt;rect class="bx" x="590" y="30" width="100" height="50" rx="4"/>
&lt;text x="640" y="52" class="ts" text-anchor="middle">TCO →&lt;/text>
&lt;text x="640" y="66" class="ts" text-anchor="middle">€/1M tok&lt;/text>
&lt;/svg>
&lt;/div>
&lt;hr>
&lt;h2 id="2-from-sizing-to-cost-the-tco-model">2. From sizing to cost: the TCO model&lt;/h2>
&lt;p>With \(N_{\text{nodes}} = 11\) 4×H100 SXM5 nodes, the TCO follows the model detailed in &lt;a href="https://blog.lo0.es/en/posts/full-tco-on-premise-gpu-cluster-capex-gpu-hour-break-even/">TCO of an on-premise GPU cluster&lt;/a>. Here the compact formula is reproduced and applied to the sized cluster.&lt;/p>
&lt;h3 id="21-annual-cost-per-node-base-scenario">2.1 Annual cost per node (base scenario)&lt;/h3>
$$C_{\text{node/year}} = \frac{\text{node capex}}{\text{years}} + \text{node opex/year}$$
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Line item&lt;/th>
&lt;th>Value (USD / €)&lt;/th>
&lt;th>Note&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>4×H100 node capex (midpoint)&lt;/td>
&lt;td>178,500 USD&lt;/td>
&lt;td>GPUs + server + network + storage + rack&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3-year amortisation&lt;/td>
&lt;td>59,500 USD/year ≈ 55,300 €/year&lt;/td>
&lt;td>Straight line&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Opex/year (base scenario, 8–16 node cluster)&lt;/td>
&lt;td>~28,000 €/year&lt;/td>
&lt;td>Energy + staff + maintenance + colocation&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Total per node per year&lt;/strong>&lt;/td>
&lt;td>&lt;strong>~83,300 €/year&lt;/strong>&lt;/td>
&lt;td>—&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>For 11 nodes: &lt;strong>915,300 €/year&lt;/strong> of total fixed cost.&lt;/p>
&lt;h3 id="22-from-node-year-to-gpu-hour">2.2 From €/node-year to €/GPU-hour&lt;/h3>
$$\text{EUR/GPU-hour} = \frac{C_{\text{node/year}}}{4\;\text{GPUs} \times 8{,}760\;\text{h} \times \rho}$$
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Utilisation \(\rho\)&lt;/th>
&lt;th>EUR/GPU-hour&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>40 %&lt;/td>
&lt;td>5.93&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>55 %&lt;/td>
&lt;td>4.31&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>63 % (SLO 300 ms)&lt;/td>
&lt;td>3.76&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>70 %&lt;/td>
&lt;td>3.39&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>80 %&lt;/td>
&lt;td>2.97&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>100 %&lt;/td>
&lt;td>2.38&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="23-from-gpu-hour-to-1m-tokens">2.3 From €/GPU-hour to €/1M tokens&lt;/h3>
$$\text{EUR/1M tokens} = \frac{\text{EUR/GPU-hour} \times 10^{6}}{\dot{T}_{\text{GPU}} \times 3{,}600}$$
&lt;p>With \(\dot{T}_{\text{GPU}} = 1{,}850\) tok/s (vLLM, Llama-3.3 70B FP8):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Utilisation&lt;/th>
&lt;th>EUR/GPU-hour&lt;/th>
&lt;th>EUR/1M tokens&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>40 %&lt;/td>
&lt;td>5.93&lt;/td>
&lt;td>&lt;strong>0.891&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>55 %&lt;/td>
&lt;td>4.31&lt;/td>
&lt;td>&lt;strong>0.647&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>63 % (SLO 300 ms)&lt;/td>
&lt;td>3.76&lt;/td>
&lt;td>&lt;strong>0.564&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>70 %&lt;/td>
&lt;td>3.39&lt;/td>
&lt;td>&lt;strong>0.509&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>80 %&lt;/td>
&lt;td>2.97&lt;/td>
&lt;td>&lt;strong>0.446&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>100 %&lt;/td>
&lt;td>2.38&lt;/td>
&lt;td>&lt;strong>0.357&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>The complete cost/token identity as a function of throughput and utilisation is developed in &lt;a href="https://blog.lo0.es/en/posts/gpu-hour-to-cost-per-token-on-prem-vs-cloud/">cost per token and per request&lt;/a>.&lt;/p>
&lt;/blockquote>
&lt;h3 id="24-comparison-with-cloud-equivalent-1m-tokens">2.4 Comparison with cloud (equivalent €/1M tokens)&lt;/h3>
&lt;p>To compare, the cloud price is converted to the €/1M tokens equivalent using the same reference throughput (\(\dot{T}_{\text{GPU}} = 1{,}850\) tok/s):&lt;/p>
$$\text{EUR/1M tokens (cloud)} = \frac{P_{\text{cloud}} \times 10^{6}}{1{,}850 \times 3{,}600}$$
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Provider / Mode&lt;/th>
&lt;th>GPU-hour price (USD)&lt;/th>
&lt;th>EUR/GPU-hour (\(1\,\text{USD} \approx 0.93\,\text{EUR}\))&lt;/th>
&lt;th>EUR/1M tokens equiv.&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>AWS p5 on-demand (post Jun. 2025)&lt;/td>
&lt;td>3.90&lt;/td>
&lt;td>3.63&lt;/td>
&lt;td>&lt;strong>0.545&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AWS p5 1-year reserved&lt;/td>
&lt;td>~2.50&lt;/td>
&lt;td>2.33&lt;/td>
&lt;td>&lt;strong>0.350&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CoreWeave on-demand&lt;/td>
&lt;td>~2.01&lt;/td>
&lt;td>1.87&lt;/td>
&lt;td>&lt;strong>0.281&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CoreWeave 3-year reserved&lt;/td>
&lt;td>~1.49&lt;/td>
&lt;td>1.39&lt;/td>
&lt;td>&lt;strong>0.209&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GCP A3 on-demand&lt;/td>
&lt;td>~3.67&lt;/td>
&lt;td>3.41&lt;/td>
&lt;td>&lt;strong>0.512&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Azure ND H100 v5 on-demand&lt;/td>
&lt;td>~6.98&lt;/td>
&lt;td>6.49&lt;/td>
&lt;td>&lt;strong>0.975&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Sources: IntuitionLabs (Jun. 2026), CloudZero (Jun. 2026), Spheron GPU pricing (May 2026).&lt;/p>
&lt;hr>
&lt;h2 id="3-break-even-and-payback-of-your-own-investment">3. Break-even and payback of your own investment&lt;/h2>
&lt;h3 id="31-the-utilisation-break-even-formula">3.1 The utilisation break-even formula&lt;/h3>
&lt;p>The utilisation break-even \(u^{*}\) is the utilisation at which the annual on-prem cost per GPU-hour equals the cloud price:&lt;/p>
$$u^{*} = \frac{\text{capex/year} + \text{opex/year}}{4 \times 8{,}760 \times P_{\text{cloud}}}$$
&lt;p>where \(P_{\text{cloud}}\) is the cloud price in the same currency as the on-prem costs.&lt;/p>
&lt;p>With the base scenario (capex/year 55,300 €, opex/year 28,000 €, total 83,300 €/node/year):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Cloud reference&lt;/th>
&lt;th>Cloud price (EUR/GPU-hour)&lt;/th>
&lt;th>\(u^{*}\) break-even&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Azure on-demand (~6.49 EUR)&lt;/td>
&lt;td>6.49&lt;/td>
&lt;td>&lt;strong>23 %&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AWS on-demand post-cut (~3.63 EUR)&lt;/td>
&lt;td>3.63&lt;/td>
&lt;td>&lt;strong>41 %&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GCP on-demand (~3.41 EUR)&lt;/td>
&lt;td>3.41&lt;/td>
&lt;td>&lt;strong>44 %&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CoreWeave on-demand (~1.87 EUR)&lt;/td>
&lt;td>1.87&lt;/td>
&lt;td>&lt;strong>80 %&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CoreWeave 3-yr reserved (~1.39 EUR)&lt;/td>
&lt;td>1.39&lt;/td>
&lt;td>&lt;strong>&amp;gt;100 %&lt;/strong> (impossible)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AWS 1-yr reserved (~2.33 EUR)&lt;/td>
&lt;td>2.33&lt;/td>
&lt;td>&lt;strong>64 %&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;div class="diagram" style="max-width:720px;margin:1rem auto;">
&lt;svg viewBox="0 0 720 240" role="img" aria-label="Break-even on-prem vs cloud: the on-prem curve falls with utilisation and crosses AWS on-demand at 41 per cent, GCP at 44 per cent and CoreWeave on-demand at 80 per cent" xmlns="http://www.w3.org/2000/svg">
&lt;style>.ax{fill:none;stroke:currentColor;stroke-width:1}.cv{fill:none;stroke:currentColor;stroke-width:1.8}.dsh{fill:none;stroke:currentColor;stroke-width:1.2;stroke-dasharray:5 3}.ts{font:11px sans-serif;fill:currentColor}.tl{font:600 11px sans-serif;fill:currentColor}&lt;/style>
&lt;line class="ax" x1="60" y1="20" x2="60" y2="200"/>
&lt;line class="ax" x1="60" y1="200" x2="700" y2="200"/>
&lt;text x="20" y="120" class="ts" transform="rotate(-90 20 120)">EUR/GPU-h&lt;/text>
&lt;text x="340" y="222" class="ts">utilisation →&lt;/text>
&lt;text x="62" y="216" class="ts">0&lt;/text>
&lt;text x="190" y="216" class="ts">30&lt;/text>
&lt;text x="330" y="216" class="ts">60&lt;/text>
&lt;text x="460" y="216" class="ts">80&lt;/text>
&lt;text x="600" y="216" class="ts">100 %&lt;/text>
&lt;line class="ax" x1="200" y1="197" x2="200" y2="203"/>
&lt;line class="ax" x1="340" y1="197" x2="340" y2="203"/>
&lt;line class="ax" x1="460" y1="197" x2="460" y2="203"/>
&lt;line class="ax" x1="600" y1="197" x2="600" y2="203"/>
&lt;path class="cv" d="M75,30 C120,55 200,100 300,140 C400,165 520,180 670,190"/>
&lt;text x="76" y="26" class="tl">on-prem (fixed capex)&lt;/text>
&lt;line class="cv" x1="60" y1="130" x2="700" y2="130"/>
&lt;text x="590" y="125" class="ts">Azure OD (6.49 €)&lt;/text>
&lt;line class="cv" x1="60" y1="160" x2="700" y2="160"/>
&lt;text x="590" y="155" class="ts">AWS OD (3.63 €)&lt;/text>
&lt;line class="cv" x1="60" y1="167" x2="700" y2="167"/>
&lt;text x="590" y="178" class="ts">GCP OD (3.41 €)&lt;/text>
&lt;line class="cv" x1="60" y1="185" x2="700" y2="185"/>
&lt;text x="576" y="181" class="ts">CoreWeave OD (1.87 €)&lt;/text>
&lt;line class="dsh" x1="240" y1="20" x2="240" y2="200"/>
&lt;text x="210" y="38" class="tl">41 % (AWS)&lt;/text>
&lt;line class="dsh" x1="258" y1="20" x2="258" y2="200"/>
&lt;text x="255" y="52" class="ts">44 % (GCP)&lt;/text>
&lt;line class="dsh" x1="475" y1="20" x2="475" y2="200"/>
&lt;text x="448" y="38" class="ts">80 % (CoreWeave OD)&lt;/text>
&lt;/svg>
&lt;/div>
&lt;h3 id="32-capex-payback">3.2 Capex payback&lt;/h3>
&lt;p>Payback is the time \(T_{\text{pay}}\) in which the cumulative saving against cloud equals the initial capex:&lt;/p>
$$T_{\text{pay}} = \frac{\text{total cluster capex}}{(\text{cloud cost/year}) - (\text{on-prem opex/year})}$$
&lt;p>where the cloud cost/year is computed at the same sustained utilisation.&lt;/p>
&lt;p>For the 11-node cluster (total capex 11 × 178,500 USD ≈ 1,825,000 USD ≈ 1,697,000 €):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Cloud reference&lt;/th>
&lt;th>Sustained utilisation&lt;/th>
&lt;th>Annual saving vs cloud&lt;/th>
&lt;th>Payback&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>AWS on-demand (3.63 EUR/h)&lt;/td>
&lt;td>70 %&lt;/td>
&lt;td>(3.63 − 3.39 EUR) × 4 × 8760 × 0.70 × 11 nodes ≈ &lt;strong>63,900 €/year&lt;/strong>&lt;/td>
&lt;td>~27 months&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AWS on-demand (3.63 EUR/h)&lt;/td>
&lt;td>80 %&lt;/td>
&lt;td>(3.63 − 2.97) × 4 × 8760 × 0.80 × 11 ≈ &lt;strong>203,600 €/year&lt;/strong>&lt;/td>
&lt;td>~10 months&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GCP on-demand (3.41 EUR/h)&lt;/td>
&lt;td>70 %&lt;/td>
&lt;td>(3.41 − 3.39) × 4 × 8760 × 0.70 × 11 ≈ &lt;strong>5,400 €/year&lt;/strong>&lt;/td>
&lt;td>~315 months (not viable)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Azure on-demand (6.49 EUR/h)&lt;/td>
&lt;td>70 %&lt;/td>
&lt;td>(6.49 − 3.39) × 4 × 8760 × 0.70 × 11 ≈ &lt;strong>830,000 €/year&lt;/strong>&lt;/td>
&lt;td>~2 months&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>The 13-month payback quoted in studies such as Lenovo TCO 2026 corresponds to ~80 % utilisation against high-priced hyperscalers (Azure/AWS before the cut). With current prices (post June 2025, AWS at 3.90 USD), the window widens.&lt;/p>
&lt;/blockquote>
&lt;h3 id="33-simple-payback-capex-only-vs-equivalent-cloud">3.3 Simple payback (capex only vs equivalent cloud)&lt;/h3>
&lt;p>If on-prem opex is omitted and only capex is compared with the gross saving:&lt;/p>
$$T_{\text{pay,simple}} = \frac{\text{capex}}{P_{\text{cloud}} \times 4 \times 8{,}760 \times \rho \times N_{\text{nodes}}}$$
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Utilisation&lt;/th>
&lt;th>AWS (3.63 EUR)&lt;/th>
&lt;th>Azure (6.49 EUR)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>50 %&lt;/td>
&lt;td>54 months&lt;/td>
&lt;td>30 months&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>70 %&lt;/td>
&lt;td>39 months&lt;/td>
&lt;td>22 months&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>80 %&lt;/td>
&lt;td>34 months&lt;/td>
&lt;td>19 months&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="4-decision-table-when-to-buy-rent-or-go-hybrid">4. Decision table: when to buy, rent or go hybrid&lt;/h2>
&lt;p>The table below is a five-dimension Pareto. There is no implicit order between columns; the reading depends on the organisation&amp;rsquo;s constraints.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Option&lt;/th>
&lt;th>EUR/GPU-hour (util. 70 %)&lt;/th>
&lt;th>Initial capex&lt;/th>
&lt;th>Required utilisation&lt;/th>
&lt;th>Data sovereignty&lt;/th>
&lt;th>Peak elasticity&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>On-prem purchase, util. ≥ 70 %&lt;/strong>&lt;/td>
&lt;td>3.39&lt;/td>
&lt;td>high (178k USD/node)&lt;/td>
&lt;td>≥ 70 % sustained&lt;/td>
&lt;td>total&lt;/td>
&lt;td>none&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>On-prem purchase, util. &amp;lt; 50 %&lt;/strong>&lt;/td>
&lt;td>&amp;gt; 4.75&lt;/td>
&lt;td>high&lt;/td>
&lt;td>&amp;lt; 50 % → loses vs cloud&lt;/td>
&lt;td>total&lt;/td>
&lt;td>none&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Sovereign EU cloud OD (Scaleway/Nebius EU)&lt;/strong>&lt;/td>
&lt;td>2.00–3.59&lt;/td>
&lt;td>none&lt;/td>
&lt;td>any&lt;/td>
&lt;td>yes (EU)&lt;/td>
&lt;td>total&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>AWS p5 on-demand (post Jun. 2025)&lt;/strong>&lt;/td>
&lt;td>3.63&lt;/td>
&lt;td>none&lt;/td>
&lt;td>any&lt;/td>
&lt;td>no (CLOUD Act)&lt;/td>
&lt;td>total&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>CoreWeave on-demand&lt;/strong>&lt;/td>
&lt;td>1.87&lt;/td>
&lt;td>none&lt;/td>
&lt;td>any&lt;/td>
&lt;td>partial (US)&lt;/td>
&lt;td>total&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>CoreWeave 3-year reserved&lt;/strong>&lt;/td>
&lt;td>1.29–1.39&lt;/td>
&lt;td>financial commitment&lt;/td>
&lt;td>rigid contract&lt;/td>
&lt;td>partial (US)&lt;/td>
&lt;td>none&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>AWS 1-year reserved&lt;/strong>&lt;/td>
&lt;td>~2.17&lt;/td>
&lt;td>1-year commitment&lt;/td>
&lt;td>contract&lt;/td>
&lt;td>no (CLOUD Act)&lt;/td>
&lt;td>none&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Hybrid on-prem base + EU cloud peak&lt;/strong>&lt;/td>
&lt;td>2.50–3.39 (weighted)&lt;/td>
&lt;td>medium&lt;/td>
&lt;td>base ≥ 70 %, elastic peak&lt;/td>
&lt;td>yes (EU)&lt;/td>
&lt;td>elastic peak&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Cut-off criteria that come before the table:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>&lt;strong>GDPR sovereignty&lt;/strong>: if the data is personal or the system is high-risk under the EU AI Act, CoreWeave/AWS are ruled out before any price comparison.&lt;/li>
&lt;li>&lt;strong>Minimum volume to amortise capex&lt;/strong>: below ~2M tokens/day sustained over 3 years, on-prem capex does not amortise against AWS on-demand.&lt;/li>
&lt;li>&lt;strong>Traffic elasticity&lt;/strong>: peaks &amp;gt;3× the base favour a hybrid or pure cloud; a stable base favours on-prem.&lt;/li>
&lt;/ol>
&lt;p>The cost/sovereignty Pareto frontier for GDPR data leaves three options: on-prem, sovereign EU cloud and hybrid. Sustained utilisation and traffic predictability decide between them. See the cross-analysis of the four axes in &lt;a href="https://blog.lo0.es/en/posts/sovereign-on-premise-vs-hyperscalers-data/">sovereign on-premise vs hyperscalers&lt;/a>.&lt;/p>
&lt;hr>
&lt;h2 id="5-sensitivity-analysis">5. Sensitivity analysis&lt;/h2>
&lt;h3 id="51-sizing-vs-hourly-profile-and-peak-factor">5.1 Sizing vs hourly profile and peak factor&lt;/h3>
&lt;p>The number of GPUs grows linearly with \(k_{\text{peak}}\) and inversely with \(\rho_{\max}\):&lt;/p>
$$N_{\text{GPU}} = \left\lceil \frac{D \times L_{\text{out}} \times k_{\text{peak}}}{H \times 3{,}600 \times \rho_{\max} \times \dot{T}_{\text{GPU}}} \right\rceil$$
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Peak factor \(k\)&lt;/th>
&lt;th>SLO 300 ms (\(\rho_{\max}=0.63\))&lt;/th>
&lt;th>SLO 500 ms (\(\rho_{\max}=0.75\))&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>1.2&lt;/td>
&lt;td>8 GPUs (2 nodes)&lt;/td>
&lt;td>6 GPUs (2 nodes)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1.8&lt;/td>
&lt;td>12 GPUs (3 nodes)&lt;/td>
&lt;td>10 GPUs (3 nodes)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2.5&lt;/td>
&lt;td>16 GPUs (4 nodes)&lt;/td>
&lt;td>13 GPUs (4 nodes)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3.5&lt;/td>
&lt;td>22 GPUs (6 nodes)&lt;/td>
&lt;td>18 GPUs (5 nodes)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>(Example simplified to 5M req/day with H=24 to illustrate the sensitivity to the peak factor)&lt;/p>
&lt;/blockquote>
&lt;p>A peak factor of 3.5× triples the number of nodes compared with k=1.2 at the same SLO. Sizing hardware for \(k > 2.5\) leaves GPUs idle more than 70 % of the time; peak cloud is more efficient beyond that threshold.&lt;/p>
&lt;h3 id="52-break-even-vs-sustained-utilisation">5.2 Break-even vs sustained utilisation&lt;/h3>
$$u^{*} = \frac{83{,}300}{4 \times 8{,}760 \times P_{\text{cloud}}}$$
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Cloud price (EUR/GPU-hour)&lt;/th>
&lt;th>\(u^{*}\) break-even&lt;/th>
&lt;th>Scenario&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>6.49 (Azure OD)&lt;/td>
&lt;td>23 %&lt;/td>
&lt;td>On-prem wins almost always&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3.63 (AWS OD)&lt;/td>
&lt;td>41 %&lt;/td>
&lt;td>On-prem wins if util. &amp;gt; 41 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3.41 (GCP OD)&lt;/td>
&lt;td>44 %&lt;/td>
&lt;td>—&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2.33 (AWS 1-yr reserved)&lt;/td>
&lt;td>64 %&lt;/td>
&lt;td>On-prem wins if util. &amp;gt; 64 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1.87 (CoreWeave OD)&lt;/td>
&lt;td>80 %&lt;/td>
&lt;td>Hard to reach in production&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1.39 (CoreWeave 3-yr reserved)&lt;/td>
&lt;td>&amp;gt;100 %&lt;/td>
&lt;td>On-prem never closes the gap&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="53-break-even-vs-energy-price">5.3 Break-even vs energy price&lt;/h3>
&lt;p>Energy accounts for 6–11 % of total TCO. Its impact on the break-even is moderate:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Energy price (EUR/kWh)&lt;/th>
&lt;th>Energy opex/year per node&lt;/th>
&lt;th>EUR/GPU-hour (70 % util.)&lt;/th>
&lt;th>\(u^{*}\) vs AWS OD&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>0.034 (solar PPA)&lt;/td>
&lt;td>1,604 €&lt;/td>
&lt;td>3.22&lt;/td>
&lt;td>38 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0.116 (industrial ES, base)&lt;/td>
&lt;td>5,475 €&lt;/td>
&lt;td>3.39&lt;/td>
&lt;td>41 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0.200 (high tariff)&lt;/td>
&lt;td>9,437 €&lt;/td>
&lt;td>3.57&lt;/td>
&lt;td>43 %&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The difference between the cheapest and the most expensive scenario is only 5 percentage points of break-even. The variable that moves the needle is utilisation, not energy.&lt;/p>
&lt;h3 id="54-break-even-vs-demand-growth">5.4 Break-even vs demand growth&lt;/h3>
&lt;p>If demand grows at an annual rate \(g\), the average utilisation of the cluster (sized for year 1) rises over time until it saturates and has to be expanded:&lt;/p>
$$\rho(t) = \rho_{0} \times (1 + g)^{t}$$
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Annual growth \(g\)&lt;/th>
&lt;th>Time to saturation (\(\rho \to 100\,\%\))&lt;/th>
&lt;th>Decision&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>10 %&lt;/td>
&lt;td>~11 years&lt;/td>
&lt;td>Comfortable purchase&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>30 %&lt;/td>
&lt;td>~4 years&lt;/td>
&lt;td>Purchase with a review at 3 years&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>60 %&lt;/td>
&lt;td>~2 years&lt;/td>
&lt;td>Hybrid: base + elastic cloud&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&amp;gt;100 %&lt;/td>
&lt;td>&amp;lt;1 year&lt;/td>
&lt;td>Pure cloud until it stabilises&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>For growth above 30 % per year, a buy-only strategy means oversizing for the future peak or re-buying hardware in short cycles. The hybrid (on-prem base + cloud for growth) minimises the capex at risk.&lt;/p>
&lt;h3 id="55-headroom-the-cost-of-the-safety-margin">5.5 Headroom: the cost of the safety margin&lt;/h3>
&lt;p>The 15 % headroom in \(N_{\text{nodes}}\) amounts to having ~1.6 extra nodes on average. Its annual cost is:&lt;/p>
$$C_{\text{headroom}} = 0.15 \times 83{,}300\;\text{EUR/node/year} \times N_{\text{nodes,base}} \approx 12{,}500 \times 9 = 112{,}500\;\text{EUR/year}$$
&lt;p>That cost is justified by:&lt;/p>
&lt;ul>
&lt;li>GPU failure rate ~5 % per year (in small clusters, documented by Introl, Apr. 2026)&lt;/li>
&lt;li>Replacement time of 2–8 weeks (depending on market availability)&lt;/li>
&lt;li>Unforeseen peaks of up to 20 % above the estimate&lt;/li>
&lt;/ul>
&lt;p>If the service can degrade gracefully (relaxing the TTFT SLO from 300 ms to 500 ms at extreme peak), headroom can be cut to 10 %, saving ~37,500 EUR/year.&lt;/p>
&lt;hr>
&lt;h2 id="6-sensitivity-map-gpu-hour-and-break-even-on-two-axes">6. Sensitivity map: €/GPU-hour and break-even on two axes&lt;/h2>
&lt;p>The table below crosses utilisation with the opex scenario, showing the all-in EUR/GPU-hour (base scenario, capex/year 55,300 €):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>Low opex (13,000 €/year)&lt;/th>
&lt;th>Base opex (28,000 €/year)&lt;/th>
&lt;th>High opex (75,000 €/year)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Util. 40 %&lt;/strong>&lt;/td>
&lt;td>4.74&lt;/td>
&lt;td>5.93&lt;/td>
&lt;td>9.06&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Util. 55 %&lt;/strong>&lt;/td>
&lt;td>3.45&lt;/td>
&lt;td>4.31&lt;/td>
&lt;td>6.59&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Util. 63 %&lt;/strong>&lt;/td>
&lt;td>3.01&lt;/td>
&lt;td>3.76&lt;/td>
&lt;td>5.75&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Util. 70 %&lt;/strong>&lt;/td>
&lt;td>2.71&lt;/td>
&lt;td>3.39&lt;/td>
&lt;td>5.18&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Util. 80 %&lt;/strong>&lt;/td>
&lt;td>2.37&lt;/td>
&lt;td>2.97&lt;/td>
&lt;td>4.54&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Util. 100 %&lt;/strong>&lt;/td>
&lt;td>1.90&lt;/td>
&lt;td>2.38&lt;/td>
&lt;td>3.63&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The crossing point with the cloud price (AWS OD: 3.63 EUR):&lt;/p>
&lt;ul>
&lt;li>Low-opex scenario: break-even at ~&lt;strong>38 % utilisation&lt;/strong>&lt;/li>
&lt;li>Base-opex scenario: break-even at ~&lt;strong>41 % utilisation&lt;/strong>&lt;/li>
&lt;li>High-opex scenario: break-even at ~&lt;strong>53 % utilisation&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>The biggest lever for lowering the break-even is not hardware capex but opex (staff and colocation above all). See the analysis of utilisation as a FinOps lever in &lt;a href="https://blog.lo0.es/en/posts/gpu-idle-cost-not-on-any-invoice/">GPU utilisation as FinOps&lt;/a>.&lt;/p>
&lt;hr>
&lt;h2 id="7-integration-with-the-rest-of-the-finops-chain">7. Integration with the rest of the FinOps chain&lt;/h2>
&lt;p>The sizing of §1 determines the number of nodes; the TCO of §2 gives the cost per hour; the comparison of §3 gives the break-even. Those three numbers feed directly into the other instruments of the series:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Instrument&lt;/th>
&lt;th>Input from this article&lt;/th>
&lt;th>Output&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a href="https://blog.lo0.es/en/posts/guidellm-validating-slo-under-load-sizing-knee/">GuideLLM — SLO validation under load&lt;/a>&lt;/td>
&lt;td>TTFT/ITL P99 SLO from step 2&lt;/td>
&lt;td>Experimental confirmation of the real throughput per GPU&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://blog.lo0.es/en/posts/capacity-planning-on-premise-llm-inference/">Capacity planning for on-premise inference&lt;/a>&lt;/td>
&lt;td>Demand profile and N GPUs from step 4&lt;/td>
&lt;td>Scaling policy and autoscaling triggers&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://blog.lo0.es/en/posts/gpu-hour-to-cost-per-token-on-prem-vs-cloud/">Cost per token and per request&lt;/a>&lt;/td>
&lt;td>EUR/GPU-hour from step §2.2&lt;/td>
&lt;td>EUR/1M tokens per model and batching&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://blog.lo0.es/en/posts/cloud-gpu-on-demand-reserved-spot-neoclouds-2026-prices/">Cloud GPU commitment and spot&lt;/a>&lt;/td>
&lt;td>Cloud prices from table §2.4&lt;/td>
&lt;td>Optimisation of the complementary cloud tier&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://blog.lo0.es/en/posts/sovereign-on-premise-vs-hyperscalers-data/">Sovereign on-premise vs hyperscalers&lt;/a>&lt;/td>
&lt;td>Break-even from §3&lt;/td>
&lt;td>Final buy/rent decision including the sovereignty axis&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://blog.lo0.es/en/posts/full-tco-on-premise-gpu-cluster-capex-gpu-hour-break-even/">TCO of an on-premise GPU cluster&lt;/a>&lt;/td>
&lt;td>Capex and opex from §2.1&lt;/td>
&lt;td>Detailed TCO model with every line item&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://blog.lo0.es/en/posts/gpu-idle-cost-not-on-any-invoice/">GPU utilisation as FinOps&lt;/a>&lt;/td>
&lt;td>Target utilisation from step 2&lt;/td>
&lt;td>Scheduling levers to raise real utilisation&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="sources">Sources&lt;/h2>
&lt;ul>
&lt;li>Spheron · LLM Inference SLO Engineering: TTFT, ITL, and P99 Latency Budgets for Production AI (2026) — &lt;a href="https://www.spheron.network/blog/llm-inference-slo-ttft-itl-latency-budget-guide-2026/">https://www.spheron.network/blog/llm-inference-slo-ttft-itl-latency-budget-guide-2026/&lt;/a>&lt;/li>
&lt;li>Spheron · vLLM vs TensorRT-LLM vs SGLang: H100 Benchmarks (2026) — &lt;a href="https://www.spheron.network/blog/vllm-vs-tensorrt-llm-vs-sglang-benchmarks/">https://www.spheron.network/blog/vllm-vs-tensorrt-llm-vs-sglang-benchmarks/&lt;/a>&lt;/li>
&lt;li>Spheron · GPU Cloud Pricing 2026: H100 from 1.03 USD/hr, B200 from 2.12 USD/hr — &lt;a href="https://www.spheron.network/blog/gpu-cloud-pricing-comparison-2026/">https://www.spheron.network/blog/gpu-cloud-pricing-comparison-2026/&lt;/a>&lt;/li>
&lt;li>Spheron · LLM Inference On-Premise vs GPU Cloud: 2026 Cost and Break-Even Analysis — &lt;a href="https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/">https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/&lt;/a>&lt;/li>
&lt;li>MLPerf Inference v5.1 — Red Hat: 5 777 tok/s (offline) en Llama 3.1-8B FP8 en H100 — &lt;a href="https://www.redhat.com/en/blog/efficient-and-reproducible-llm-inference-red-hat-mlperf-inference-v51-results">https://www.redhat.com/en/blog/efficient-and-reproducible-llm-inference-red-hat-mlperf-inference-v51-results&lt;/a>&lt;/li>
&lt;li>MLPerf Inference v6.0 Results Explained: GPU Performance Rankings for AI Workloads (2026) — &lt;a href="https://www.spheron.network/blog/mlperf-inference-v6-benchmark-results-2026/">https://www.spheron.network/blog/mlperf-inference-v6-benchmark-results-2026/&lt;/a>&lt;/li>
&lt;li>IntuitionLabs · H100 Rental Prices Compared: 1.49–6.98 USD/hr Across 15+ Cloud Providers (2026) — &lt;a href="https://intuitionlabs.ai/articles/h100-rental-prices-cloud-comparison">https://intuitionlabs.ai/articles/h100-rental-prices-cloud-comparison&lt;/a>&lt;/li>
&lt;li>CloudZero · Cloud GPU Pricing Comparison: AWS vs Azure vs GCP For AI Workloads (2026) — &lt;a href="https://www.cloudzero.com/blog/cloud-gpu-pricing-comparison/">https://www.cloudzero.com/blog/cloud-gpu-pricing-comparison/&lt;/a>&lt;/li>
&lt;li>Lenovo Press · On-Premise vs Cloud: Generative AI Total Cost of Ownership (2026 Edition) — &lt;a href="https://lenovopress.lenovo.com/lp2368-on-premise-vs-cloud-generative-ai-total-cost-of-ownership-2026-edition">https://lenovopress.lenovo.com/lp2368-on-premise-vs-cloud-generative-ai-total-cost-of-ownership-2026-edition&lt;/a>&lt;/li>
&lt;li>Introl · GPU Infrastructure TCO Model: 5-Year Cost Analysis for Enterprise AI (abr. 2026) — &lt;a href="https://introl.com/blog/gpu-infrastructure-tco-5-year-cost-model">https://introl.com/blog/gpu-infrastructure-tco-5-year-cost-model&lt;/a>&lt;/li>
&lt;li>GMI Cloud · NVIDIA H100 GPU Pricing 2026: Rent vs Buy Cost Analysis — &lt;a href="https://www.gmicloud.ai/en/blog/nvidia-h100-gpu-pricing-2026-rent-vs-buy-cost-analysis">https://www.gmicloud.ai/en/blog/nvidia-h100-gpu-pricing-2026-rent-vs-buy-cost-analysis&lt;/a>&lt;/li>
&lt;li>Red Hat · 233 % 3-year ROI and 13 months to payback with Red Hat AI (feb. 2026) — &lt;a href="https://www.redhat.com/en/blog/233-3-year-return-investment-and-13-months-payback-red-hat-ai">https://www.redhat.com/en/blog/233-3-year-return-investment-and-13-months-payback-red-hat-ai&lt;/a>&lt;/li>
&lt;li>VentureBeat · 5 % GPU utilization: the 401 billion USD AI infrastructure problem — &lt;a href="https://venturebeat.com/infrastructure/5-gpu-utilization-the-401-billion-ai-infrastructure-problem-enterprises-cant-keep-ignoring/">https://venturebeat.com/infrastructure/5-gpu-utilization-the-401-billion-ai-infrastructure-problem-enterprises-cant-keep-ignoring/&lt;/a>&lt;/li>
&lt;li>DZone · Queueing Theory for LLM Inference — &lt;a href="https://dzone.com/articles/queueing-theory-for-llm-inference">https://dzone.com/articles/queueing-theory-for-llm-inference&lt;/a>&lt;/li>
&lt;li>GuideLLM · Evaluate LLM deployments for real-world inference (Red Hat Developer, jun. 2025) — &lt;a href="https://developers.redhat.com/articles/2025/06/20/guidellm-evaluate-llm-deployments-real-world-inference">https://developers.redhat.com/articles/2025/06/20/guidellm-evaluate-llm-deployments-real-world-inference&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Full TCO of an on-premise GPU cluster: from capex to the all-in €/GPU-hour and the break-even against cloud</title><link>https://blog.lo0.es/en/posts/full-tco-on-premise-gpu-cluster-capex-gpu-hour-break-even/</link><pubDate>Tue, 16 Jun 2026 06:00:00 +0200</pubDate><guid>https://blog.lo0.es/en/posts/full-tco-on-premise-gpu-cluster-capex-gpu-hour-break-even/</guid><description>&lt;blockquote>
&lt;p>Notation: amounts in &lt;strong>N €&lt;/strong> or &lt;strong>N USD&lt;/strong> (source denominated in dollars); decimal point; comma as thousands separator. The dollar sign is not used (it is the formula delimiter). Data centred on Europe/Spain. Generic example hardware: a cluster of N nodes, each with 4×H100 SXM5 80 GB.&lt;/p>
&lt;/blockquote>
&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>A 4×H100 SXM5 node costs between &lt;strong>150,000 USD and 200,000 USD&lt;/strong> in total capex (GPUs + server + network + storage + prorated rack). Amortised over 3 years with European opex (energy at ~0.116 €/kWh industrial, PUE 1.54 average or 1.2 with liquid, 0.3 FTE of staff), the &lt;strong>all-in&lt;/strong> cost ranges from &lt;strong>3.10 USD/GPU-hour&lt;/strong> (100 % utilisation) to &lt;strong>6.20 USD/GPU-hour&lt;/strong> (50 % utilisation). The break-even against AWS p5 on-demand (~6.88 USD/GPU-hour) is crossed at around &lt;strong>70 % utilisation&lt;/strong>; against a neocloud 3-year reserved (~1.49–2.10 USD/GPU-hour), on-prem never closes the gap in that scenario. Utilisation is the variable that decides the cost axis, not the price of the hardware.&lt;/p>
&lt;hr>
&lt;h2 id="the-model-declared-assumptions">The model: declared assumptions&lt;/h2>
&lt;p>Every calculation below starts from these assumptions. Changing any of them moves the conclusion; the sensitivity section quantifies by how much.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Parameter&lt;/th>
&lt;th>Base value&lt;/th>
&lt;th>Sensitivity range&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Node&lt;/td>
&lt;td>4×H100 SXM5 80 GB (HGX baseboard)&lt;/td>
&lt;td>—&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cluster&lt;/td>
&lt;td>N nodes (per-node model; scales linearly)&lt;/td>
&lt;td>1–32 nodes&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Capex amortisation&lt;/td>
&lt;td>3 years (straight line)&lt;/td>
&lt;td>3–5 years&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GPU utilisation&lt;/td>
&lt;td>70 %&lt;/td>
&lt;td>30 %–100 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Energy price&lt;/td>
&lt;td>0.116 €/kWh (industrial Spain, Sept. 2025)&lt;/td>
&lt;td>0.06–0.20 €/kWh&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PUE&lt;/td>
&lt;td>1.54 (global average, Uptime Institute 2025)&lt;/td>
&lt;td>1.15–1.80&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Exchange rate&lt;/td>
&lt;td>1 USD = 0.93 € (reference Jun. 2026)&lt;/td>
&lt;td>—&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>Energy source: &lt;a href="https://www.globalpetrolprices.com/Spain/electricity_prices/">GlobalPetrolPrices · Spain Business Electricity, Sept. 2025&lt;/a>. PUE source: &lt;a href="https://datacenter.uptimeinstitute.com/rs/711-RIA-145/images/2025.Annual.Survey.Report.pdf">Uptime Institute Global Data Center Survey 2025&lt;/a> — the global average PUE has been stuck at 1.54 for the sixth consecutive year; hyperscalers 1.10–1.15; colocation/enterprise 1.58–1.80; facilities less than 5 years old, 1.48. PUE 1.2 is achievable with direct-to-chip liquid cooling.&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="capex-breakdown-per-4h100-sxm5-node">Capex breakdown per 4×H100 SXM5 node&lt;/h2>
&lt;h3 id="gpus">GPUs&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Component&lt;/th>
&lt;th>Unit price (USD)&lt;/th>
&lt;th>Quantity&lt;/th>
&lt;th>Subtotal (USD)&lt;/th>
&lt;th>Source and date&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>H100 SXM5 80 GB (card)&lt;/td>
&lt;td>30,000–40,000&lt;/td>
&lt;td>4&lt;/td>
&lt;td>120,000–160,000&lt;/td>
&lt;td>&lt;a href="https://www.gmicloud.ai/en/blog/nvidia-h100-gpu-pricing-2026-rent-vs-buy-cost-analysis">GMI Cloud, Apr. 2026&lt;/a> · &lt;a href="https://introl.com/blog/gpu-infrastructure-tco-5-year-cost-model">Introl, Apr. 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The range reflects market variability and volume discounts (5–15 % for orders above 50 units). H100 SXM5 cards require NVIDIA&amp;rsquo;s &lt;strong>HGX baseboard&lt;/strong>; they are not sold loose for direct installation in standard servers.&lt;/p>
&lt;h3 id="server--hgx-baseboard">Server / HGX baseboard&lt;/h3>
&lt;p>A complete 4×H100 SXM node uses NVIDIA&amp;rsquo;s &lt;strong>HGX H100 4-GPU baseboard&lt;/strong> plus a compatible host server. Reference models: Supermicro SYS-421GU-TNXR (4U, dual Intel Xeon 4th Gen, HGX H100 4-GPU) and its Dell equivalent.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Component&lt;/th>
&lt;th>Estimated cost (USD)&lt;/th>
&lt;th>Note&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Server chassis + CPU (2× Xeon) + RAM (512 GB DDR5) + redundant PSU&lt;/td>
&lt;td>18,000–25,000&lt;/td>
&lt;td>Based on a Supermicro SYS-821GE bare without GPUs at ~24,806 USD (&lt;a href="https://www.xicomputer.com/Solutions/Data-Center-Servers/SYS-821GE-TNHR.asp">xicomputer.com&lt;/a>, Jun. 2026); scaled to the 4-GPU&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>HGX H100 4-GPU baseboard&lt;/td>
&lt;td>included in the GPU price&lt;/td>
&lt;td>NVIDIA HGX platform; no separate public price&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Inter-GPU NVLink (inside the node)&lt;/td>
&lt;td>included in the baseboard&lt;/td>
&lt;td>4 GPUs connected by NVLink 4.0 on the HGX baseboard&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>Marketing claim (no independent verification): Supermicro announces datacenter energy cost reductions of up to 40 % with liquid cooling in its HGX H100 servers (&lt;a href="https://www.supermicro.com/en/pressreleases/supermicro-launches-industrys-first-nvidia-hgx-h100-8-and-4-gpu-h100-servers-with-liquid-cooling">Supermicro press release&lt;/a>).&lt;/p>
&lt;/blockquote>
&lt;h3 id="infiniband-ndr-network">InfiniBand NDR network&lt;/h3>
&lt;p>For a multi-node cluster with tensor parallelism across nodes, the GPU-to-GPU network is critical. NDR InfiniBand (400 Gb/s per port) is the de facto standard for HGX clusters.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Component&lt;/th>
&lt;th>Estimated cost per node (USD)&lt;/th>
&lt;th>Source / Note&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>NVIDIA Quantum-2 NDR 400G switch (64 ports, prorated across N nodes)&lt;/td>
&lt;td>2,000–4,000&lt;/td>
&lt;td>Switch ~35,000 USD (&lt;a href="https://introl.com/blog/gpu-infrastructure-tco-5-year-cost-model">Introl, Apr. 2026&lt;/a>); at 16 nodes, ~2,200 USD/node&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>InfiniBand NDR cables/transceivers (4 ports per node × ~1,000 USD/port)&lt;/td>
&lt;td>4,000&lt;/td>
&lt;td>Estimate based on ~1,000 USD per optical transceiver (&lt;a href="https://introl.com/blog/gpu-infrastructure-tco-5-year-cost-model">Introl, Apr. 2026&lt;/a>)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>InfiniBand network (prorated per 4-GPU node)&lt;/strong>&lt;/td>
&lt;td>&lt;strong>~6,000–8,000&lt;/strong>&lt;/td>
&lt;td>—&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>For inference serving inside a single node (4 GPUs with NVLink), the inter-node network is less critical than for multi-node training. For disaggregated prefill-decode workloads spread across nodes, InfiniBand NDR is necessary.&lt;/p>
&lt;/blockquote>
&lt;h3 id="nvme-storage">NVMe storage&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Component&lt;/th>
&lt;th>Estimated cost (USD)&lt;/th>
&lt;th>Note&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Local NVMe (4 TB × 2 U.2/E1.S drives, working datasets and checkpoints)&lt;/td>
&lt;td>2,000–4,000&lt;/td>
&lt;td>~500–1,000 USD/TB enterprise NVMe, 2025&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Shared object storage (NAS/MinIO, prorated per node)&lt;/td>
&lt;td>2,000–5,000&lt;/td>
&lt;td>Varies with the total capacity of the cluster&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Total storage per node&lt;/strong>&lt;/td>
&lt;td>&lt;strong>~4,000–9,000&lt;/strong>&lt;/td>
&lt;td>—&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>Introl models 50 TB per GPU for effective operation in training clusters (&lt;a href="https://introl.com/blog/gpu-infrastructure-tco-5-year-cost-model">Introl, Apr. 2026&lt;/a>); for pure inference the requirement is significantly lower (model weights + logs).&lt;/p>
&lt;/blockquote>
&lt;h3 id="rack-pdu-and-datacenter-connectivity">Rack, PDU and datacenter connectivity&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Component&lt;/th>
&lt;th>Estimated cost per node (USD/year)&lt;/th>
&lt;th>Source&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Rack colocation (high density, 10–15 kW per node)&lt;/td>
&lt;td>5,000–12,000/year&lt;/td>
&lt;td>&lt;a href="https://encoradvisors.com/data-center-colocation-pricing/">Encoradvisors · Colocation Pricing 2026&lt;/a>: high density 3,000–6,000 USD/month per rack; at 2 nodes per rack, ~1,500–3,000 USD/month per node = 18,000–36,000 USD/year in tier-1; lower in Spain&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Rack PDU, electrical cabling (prorated)&lt;/td>
&lt;td>500–1,000 per node (amortised capex)&lt;/td>
&lt;td>Inside the colocation line item or in an own datacenter&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>Colocation in Spain/Europe is structurally cheaper than in US tier-1 markets (New York, Silicon Valley). For an own datacenter, replace it with the cost of your own space plus amortisation of the electrical and cooling infrastructure.&lt;/p>
&lt;/blockquote>
&lt;h3 id="capex-summary-per-4h100-sxm5-node">Capex summary per 4×H100 SXM5 node&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Line item&lt;/th>
&lt;th>Range (USD)&lt;/th>
&lt;th>Midpoint&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>GPUs (4× H100 SXM5)&lt;/td>
&lt;td>120,000–160,000&lt;/td>
&lt;td>140,000&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Server chassis + CPU/RAM/PSU&lt;/td>
&lt;td>18,000–25,000&lt;/td>
&lt;td>21,500&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>InfiniBand NDR network (prorated)&lt;/td>
&lt;td>6,000–8,000&lt;/td>
&lt;td>7,000&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>NVMe + object storage&lt;/td>
&lt;td>4,000–9,000&lt;/td>
&lt;td>6,500&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PDU/rack/other (capex)&lt;/td>
&lt;td>2,000–5,000&lt;/td>
&lt;td>3,500&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Total capex per node&lt;/strong>&lt;/td>
&lt;td>&lt;strong>150,000–207,000&lt;/strong>&lt;/td>
&lt;td>&lt;strong>178,500&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Sources: &lt;a href="https://www.gmicloud.ai/en/blog/nvidia-h100-gpu-pricing-2026-rent-vs-buy-cost-analysis">GMI Cloud (Apr. 2026)&lt;/a>, &lt;a href="https://introl.com/blog/gpu-infrastructure-tco-5-year-cost-model">Introl (Apr. 2026)&lt;/a>, &lt;a href="https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/">Spheron (Apr. 2026)&lt;/a>, &lt;a href="https://www.xicomputer.com/Solutions/Data-Center-Servers/SYS-821GE-TNHR.asp">xicomputer.com (Jun. 2026)&lt;/a>.&lt;/p>
&lt;hr>
&lt;h2 id="opex-breakdown-per-4h100-sxm5-node-annual">Opex breakdown per 4×H100 SXM5 node (annual)&lt;/h2>
&lt;h3 id="energy">Energy&lt;/h3>
&lt;p>A 4×H100 SXM5 node at full load draws approximately:&lt;/p>
$$P_{\text{node}} = 4 \times 700\,\text{W (TDP H100 SXM5)} + 800\,\text{W (server)} \approx 3.6\,\text{kW (IT)}$$
&lt;p>The total datacenter power includes the cooling overhead, expressed by the &lt;strong>PUE&lt;/strong>:&lt;/p>
$$P_{\text{total}} = P_{\text{IT}} \times \text{PUE}$$
$$\text{annual energy cost} = P_{\text{IT}} \times \text{PUE} \times 8{,}760\,\text{h} \times \text{kWh price}$$
&lt;p>With the base values (PUE 1.54; 0.116 €/kWh):&lt;/p>
$$\text{energy/year} = 3.6\,\text{kW} \times 1.54 \times 8{,}760\,\text{h} \times 0.116\,\text{EUR/kWh} \approx 5{,}475\,\text{EUR}$$
&lt;p>With a Spanish solar PPA (Q3 2025 reference price: ~34 €/MWh = 0.034 €/kWh according to &lt;a href="https://www.pv-tech.org/european-solar-ppa-prices-fall-below-35-mwh-q3-2025/">PV Tech, Oct. 2025&lt;/a>):&lt;/p>
$$\text{energy/year (solar PPA)} = 3.6 \times 1.54 \times 8{,}760 \times 0.034 \approx 1{,}604\,\text{EUR}$$
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Energy scenario&lt;/th>
&lt;th>Price (€/kWh)&lt;/th>
&lt;th>Energy cost/year per 4-GPU node&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Spanish solar PPA (Q3 2025)&lt;/td>
&lt;td>0.034&lt;/td>
&lt;td>~1,604 €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Industrial Spain (Sept. 2025)&lt;/td>
&lt;td>0.116&lt;/td>
&lt;td>~5,475 €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>European average (industrial tariff)&lt;/td>
&lt;td>0.160&lt;/td>
&lt;td>~7,550 €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Worst case (no PPA, high tariff)&lt;/td>
&lt;td>0.200&lt;/td>
&lt;td>~9,437 €&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="staff--operations">Staff / operations&lt;/h3>
&lt;p>Staff cost is the most variable line item with cluster size. For a small cluster (2–8 nodes), the rule of thumb is &lt;strong>0.3–0.5 FTE per cluster&lt;/strong> of GPU infrastructure support (&lt;a href="https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/">Spheron, Apr. 2026&lt;/a>).&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Cluster size&lt;/th>
&lt;th>Estimated FTE&lt;/th>
&lt;th>FTE cost (€/year, Western Europe)&lt;/th>
&lt;th>Cost per 4-GPU node (€/year)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>2–4 nodes&lt;/td>
&lt;td>0.3 FTE&lt;/td>
&lt;td>~120,000&lt;/td>
&lt;td>36,000–18,000&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>8–16 nodes&lt;/td>
&lt;td>0.5 FTE&lt;/td>
&lt;td>~120,000&lt;/td>
&lt;td>7,500&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>32+ nodes&lt;/td>
&lt;td>1–2 FTE&lt;/td>
&lt;td>~120,000&lt;/td>
&lt;td>3,750–7,500&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>Indicative salary reference: a GPU infrastructure engineer with CUDA, InfiniBand and Kubernetes knowledge in Western Europe, ~90,000–140,000 €/year fully loaded. Introl&amp;rsquo;s figures (&lt;a href="https://introl.com/blog/gpu-infrastructure-tco-5-year-cost-model">Apr. 2026&lt;/a>) in USD (~275,000 USD/year for the US) reflect the North American market, which is appreciably higher.&lt;/p>
&lt;/blockquote>
&lt;h3 id="maintenance-and-support">Maintenance and support&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Line item&lt;/th>
&lt;th>Annual cost (% of hardware capex)&lt;/th>
&lt;th>Per 4-GPU node (midpoint)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Vendor maintenance / support&lt;/td>
&lt;td>5–10 % of capex&lt;/td>
&lt;td>~7,000–14,000 USD → ~6,500–13,000 €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GPU failure rate (~5 % annual) × replacement cost&lt;/td>
&lt;td>5 % × 4 GPUs × ~35,000 USD = ~7,000 USD expected&lt;/td>
&lt;td>~6,500 € (amortised as a provision)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Minor spares (cables, modules)&lt;/td>
&lt;td>~500–1,000 €&lt;/td>
&lt;td>—&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>Introl quotes GPU failure rates of 2–3 % per year in small clusters; Google Research documented ~9 % annualised in Meta&amp;rsquo;s 16,384-GPU H100 cluster (&lt;a href="https://introl.com/blog/gpu-infrastructure-tco-5-year-cost-model">Introl, Apr. 2026&lt;/a>). 5 % is used here as a conservative intermediate value.&lt;/p>
&lt;/blockquote>
&lt;h3 id="depreciation-for-accounting-purposes">Depreciation (for accounting purposes)&lt;/h3>
&lt;p>Straight-line depreciation turns capex into an annual flow comparable to the cost of committed cloud:&lt;/p>
$$\text{annual depreciation} = \frac{\text{node capex}}{\text{amortisation years}}$$
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Node capex (USD)&lt;/th>
&lt;th>3-year amortisation (USD/year)&lt;/th>
&lt;th>5-year amortisation (USD/year)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>150,000 (minimum)&lt;/td>
&lt;td>50,000&lt;/td>
&lt;td>30,000&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>178,500 (average)&lt;/td>
&lt;td>59,500&lt;/td>
&lt;td>35,700&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>207,000 (maximum)&lt;/td>
&lt;td>69,000&lt;/td>
&lt;td>41,400&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>H100 hardware depreciates fast: secondary-market analyses put the residual value at 20–40 % of the purchase price after 3 years (&lt;a href="https://introl.com/blog/gpu-infrastructure-tco-5-year-cost-model">Introl, Apr. 2026&lt;/a>). The arrival of Blackwell GB200/GB300 accelerates the perceived obsolescence.&lt;/p>
&lt;/blockquote>
&lt;h3 id="annual-opex-summary-per-4h100-sxm5-node-base-scenario-8-node-cluster">Annual opex summary per 4×H100 SXM5 node (base scenario, 8-node cluster)&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Line item&lt;/th>
&lt;th>Base scenario (€/year)&lt;/th>
&lt;th>Range&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Energy (PUE 1.54; 0.116 €/kWh)&lt;/td>
&lt;td>5,475&lt;/td>
&lt;td>1,604–9,437&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Staff (0.5 FTE × 8 nodes, prorated)&lt;/td>
&lt;td>7,500&lt;/td>
&lt;td>3,750–36,000&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Maintenance / support / failures&lt;/td>
&lt;td>9,000&lt;/td>
&lt;td>5,000–15,000&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Rack colocation (Spain, high density)&lt;/td>
&lt;td>6,000&lt;/td>
&lt;td>3,000–15,000&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Total opex per node&lt;/strong>&lt;/td>
&lt;td>&lt;strong>~28,000&lt;/strong>&lt;/td>
&lt;td>&lt;strong>~13,000–75,000&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The extreme range reflects the difference between a well amortised own datacenter with a solar PPA and cheap energy (minimum opex) and tier-1 colocation with market tariffs and senior staff.&lt;/p>
&lt;hr>
&lt;h2 id="deriving-the-all-in-gpu-hour">Deriving the all-in €/GPU-hour&lt;/h2>
&lt;h3 id="formula">Formula&lt;/h3>
$$\text{EUR/GPU-hour all-in} = \frac{\frac{\text{node capex}}{\text{years}} + \text{node annual opex}}{4\,\text{GPUs} \times 8{,}760\,\text{h} \times u}$$
&lt;p>where \(u\) is the average annual utilisation (0 to 1).&lt;/p>
&lt;p>See the cost-per-token identity in &lt;a href="https://blog.lo0.es/en/posts/gpu-hour-to-cost-per-token-on-prem-vs-cloud/">cost per token and per request&lt;/a> for the connection with throughput.&lt;/p>
&lt;h3 id="gpu-hour-table-by-utilisation-and-scenario">€/GPU-hour table by utilisation and scenario&lt;/h3>
&lt;p>&lt;strong>Average capex (178,500 USD → ~166,000 €), 3-year amortisation → 55,300 €/year.&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Utilisation&lt;/th>
&lt;th>Opex/year (base, €)&lt;/th>
&lt;th>Total cost/year (€)&lt;/th>
&lt;th>Useful GPU-hours/year&lt;/th>
&lt;th>&lt;strong>€/GPU-hour&lt;/strong>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>30 %&lt;/td>
&lt;td>28,000&lt;/td>
&lt;td>83,300&lt;/td>
&lt;td>10,512&lt;/td>
&lt;td>&lt;strong>7.93&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>50 %&lt;/td>
&lt;td>28,000&lt;/td>
&lt;td>83,300&lt;/td>
&lt;td>17,520&lt;/td>
&lt;td>&lt;strong>4.75&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>70 %&lt;/td>
&lt;td>28,000&lt;/td>
&lt;td>83,300&lt;/td>
&lt;td>24,528&lt;/td>
&lt;td>&lt;strong>3.39&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>80 %&lt;/td>
&lt;td>28,000&lt;/td>
&lt;td>83,300&lt;/td>
&lt;td>28,032&lt;/td>
&lt;td>&lt;strong>2.97&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>100 %&lt;/td>
&lt;td>28,000&lt;/td>
&lt;td>83,300&lt;/td>
&lt;td>35,040&lt;/td>
&lt;td>&lt;strong>2.38&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Low-opex scenario&lt;/strong> (solar PPA, own datacenter, large cluster): opex/year ~13,000 €.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Utilisation&lt;/th>
&lt;th>Total cost/year (€)&lt;/th>
&lt;th>&lt;strong>€/GPU-hour&lt;/strong>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>50 %&lt;/td>
&lt;td>68,300&lt;/td>
&lt;td>&lt;strong>3.90&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>70 %&lt;/td>
&lt;td>68,300&lt;/td>
&lt;td>&lt;strong>2.78&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>80 %&lt;/td>
&lt;td>68,300&lt;/td>
&lt;td>&lt;strong>2.43&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>100 %&lt;/td>
&lt;td>68,300&lt;/td>
&lt;td>&lt;strong>1.95&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>High-opex scenario&lt;/strong> (market tariff, expensive colocation, small cluster): opex/year ~75,000 €.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Utilisation&lt;/th>
&lt;th>Total cost/year (€)&lt;/th>
&lt;th>&lt;strong>€/GPU-hour&lt;/strong>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>50 %&lt;/td>
&lt;td>130,300&lt;/td>
&lt;td>&lt;strong>7.44&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>70 %&lt;/td>
&lt;td>130,300&lt;/td>
&lt;td>&lt;strong>5.31&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>80 %&lt;/td>
&lt;td>130,300&lt;/td>
&lt;td>&lt;strong>4.65&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>100 %&lt;/td>
&lt;td>130,300&lt;/td>
&lt;td>&lt;strong>3.72&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="from-gpu-hour-to-1m-tokens">From €/GPU-hour to €/1M tokens&lt;/h2>
&lt;p>The cost-per-token identity connects the hardware cost with the inference cost:&lt;/p>
$$\text{EUR/1M tokens} = \frac{\text{EUR/GPU-hour} \times 10^6}{\text{throughput (tok/s)} \times 3{,}600}$$
&lt;p>For reference throughputs on H100 SXM5 with vLLM (see &lt;a href="https://blog.lo0.es/en/posts/capacity-planning-on-premise-llm-inference/">capacity planning for on-premise inference&lt;/a>):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th>Typical throughput (tok/s per GPU)&lt;/th>
&lt;th>Source&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Llama-3 70B FP8, high batch&lt;/td>
&lt;td>~2,800&lt;/td>
&lt;td>Series B benchmarks&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Llama-3 8B FP16, medium batch&lt;/td>
&lt;td>~9,000&lt;/td>
&lt;td>Series B benchmarks&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Mixtral 8×7B, high batch&lt;/td>
&lt;td>~4,500&lt;/td>
&lt;td>Series B benchmarks&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>€/1M tokens table in the base scenario (€/GPU-hour 3.39 at 70 % utilisation):&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th>Throughput (tok/s)&lt;/th>
&lt;th>€/1M tokens&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Llama-3 70B FP8&lt;/td>
&lt;td>2,800&lt;/td>
&lt;td>&lt;strong>~0.336&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Llama-3 8B FP16&lt;/td>
&lt;td>9,000&lt;/td>
&lt;td>&lt;strong>~0.105&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Mixtral 8×7B&lt;/td>
&lt;td>4,500&lt;/td>
&lt;td>&lt;strong>~0.209&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>At 50 % utilisation (€/GPU-hour 4.75):&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th>€/1M tokens&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Llama-3 70B FP8&lt;/td>
&lt;td>&lt;strong>~0.471&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Llama-3 8B FP16&lt;/td>
&lt;td>&lt;strong>~0.147&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Occupancy (batching) multiplies the effective throughput and lowers the €/1M tokens without changing the hardware; it is analysed in &lt;a href="https://blog.lo0.es/en/posts/gpu-idle-cost-not-on-any-invoice/">GPU utilisation as a FinOps lever&lt;/a>.&lt;/p>
&lt;hr>
&lt;h2 id="break-even-on-prem-vs-cloud">Break-even on-prem vs cloud&lt;/h2>
&lt;h3 id="the-break-even-formula">The break-even formula&lt;/h3>
&lt;p>Break-even happens when the total annual on-prem cost equals the annual cloud cost at the same utilisation:&lt;/p>
$$\text{annual cloud cost} = \text{cloud GPU-hour price} \times 4\,\text{GPUs} \times 8{,}760\,\text{h} \times u$$
$$\text{break-even}: \quad \frac{\text{capex/year} + \text{opex/year}}{4 \times 8{,}760 \times u} = \text{cloud GPU-hour price}$$
&lt;p>Solving for the break-even utilisation:&lt;/p>
$$u^* = \frac{\text{capex/year} + \text{opex/year}}{4 \times 8{,}760 \times \text{cloud GPU-hour price}}$$
&lt;h3 id="break-even-table-by-cloud-mode-and-on-prem-scenario">Break-even table by cloud mode and on-prem scenario&lt;/h3>
&lt;p>&lt;strong>Base on-prem scenario&lt;/strong> (capex/year 55,300 €, opex/year 28,000 €, total 83,300 €/year per 4-GPU node):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Cloud reference (price/GPU-hour)&lt;/th>
&lt;th>USD equiv.&lt;/th>
&lt;th>Break-even utilisation \(u^*\)&lt;/th>
&lt;th>Note&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Neocloud on-demand (Lambda/Spheron ~2.90 USD)&lt;/td>
&lt;td>2.90 USD (~2.70 €)&lt;/td>
&lt;td>&lt;strong>&amp;gt;100 %&lt;/strong>, on-prem does not compete&lt;/td>
&lt;td>Neocloud on-demand is cheaper even at full utilisation&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Neocloud 3-year reserved (CoreWeave ~1.49–2.10 USD)&lt;/td>
&lt;td>~1.80 USD (~1.67 €)&lt;/td>
&lt;td>&lt;strong>&amp;gt;100 %&lt;/strong>, impossible&lt;/td>
&lt;td>Neocloud reserved beats on-prem in every scenario of this model&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AWS p5 on-demand (6.88 USD/GPU-hour)&lt;/td>
&lt;td>6.88 USD (~6.40 €)&lt;/td>
&lt;td>&lt;strong>~47 %&lt;/strong>&lt;/td>
&lt;td>Above 47 %, the average on-prem beats AWS on-demand&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AWS p5 3-year reserved (~2.97 USD/GPU-hour)&lt;/td>
&lt;td>2.97 USD (~2.76 €)&lt;/td>
&lt;td>&lt;strong>&amp;gt;100 %&lt;/strong>&lt;/td>
&lt;td>—&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GCP A3 on-demand (~10.98 USD/GPU-hour)&lt;/td>
&lt;td>10.98 USD (~10.21 €)&lt;/td>
&lt;td>&lt;strong>~29 %&lt;/strong>&lt;/td>
&lt;td>Above 29 %, on-prem beats GCP on-demand&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Azure ND H100 v5 on-demand (~12.29 USD/GPU-hour)&lt;/td>
&lt;td>12.29 USD (~11.43 €)&lt;/td>
&lt;td>&lt;strong>~26 %&lt;/strong>&lt;/td>
&lt;td>—&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Low-opex scenario&lt;/strong> (total 68,300 €/year):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Cloud reference&lt;/th>
&lt;th>Break-even utilisation&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>AWS p5 on-demand (6.88 USD ≈ 6.40 €)&lt;/td>
&lt;td>&lt;strong>~38 %&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Neocloud on-demand (2.90 USD ≈ 2.70 €)&lt;/td>
&lt;td>&lt;strong>~91 %&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Neocloud 3-yr reserved (1.80 USD ≈ 1.67 €)&lt;/td>
&lt;td>&lt;strong>&amp;gt;100 %&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>High-opex scenario&lt;/strong> (total 130,300 €/year):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Cloud reference&lt;/th>
&lt;th>Break-even utilisation&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>AWS p5 on-demand (6.88 USD ≈ 6.40 €)&lt;/td>
&lt;td>&lt;strong>~72 %&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GCP A3 on-demand (~10.21 €)&lt;/td>
&lt;td>&lt;strong>~45 %&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Azure on-demand (~11.43 €)&lt;/td>
&lt;td>&lt;strong>~41 %&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="reading-the-break-even-table">Reading the break-even table&lt;/h3>
&lt;ul>
&lt;li>Against &lt;strong>neoclouds&lt;/strong> (on-demand or reserved), on-prem TCO &lt;strong>does not reach break-even&lt;/strong> in any scenario of the base model. Neocloud reserved beats on-prem even at 100 % utilisation, because its hourly price is below the all-in cost of your own hardware. This is consistent with the analysis in &lt;a href="https://blog.lo0.es/en/posts/cloud-gpu-on-demand-reserved-spot-neoclouds-2026-prices/">cloud GPU: on-demand, reserved and spot&lt;/a>.&lt;/li>
&lt;li>Against &lt;strong>on-demand hyperscalers&lt;/strong> (AWS, GCP, Azure), on-prem does have an achievable break-even: around &lt;strong>26–72 %&lt;/strong> utilisation depending on the scenario. At medium-high utilisation (&amp;gt;70 %), on-prem clearly beats AWS/GCP/Azure on-demand.&lt;/li>
&lt;li>The variable that moves the break-even most is &lt;strong>opex&lt;/strong> (staff above all), not hardware capex. A well sized cluster in cheap colocation with PPA energy can lower the threshold by 20 percentage points compared with the high scenario.&lt;/li>
&lt;li>For GDPR data, the break-even against US hyperscalers is skewed: the sovereignty axis rules out US hyperscalers before cost does (see &lt;a href="https://blog.lo0.es/en/posts/sovereign-on-premise-vs-hyperscalers-data/">sovereign on-premise vs hyperscalers&lt;/a>).&lt;/li>
&lt;/ul>
&lt;div class="diagram" style="max-width:760px;margin:1rem auto;">
&lt;svg viewBox="0 0 760 260" role="img" aria-label="Break-even on-prem vs cloud by utilisation: base on-prem crosses AWS on-demand at 47 per cent, GCP at 29 per cent and Azure at 26 per cent utilisation. Neoclouds have no feasible break-even." xmlns="http://www.w3.org/2000/svg">
&lt;style>.ax{fill:none;stroke:currentColor;stroke-width:1}.cv{fill:none;stroke:currentColor;stroke-width:1.8}.dsh{fill:none;stroke:currentColor;stroke-width:1.2;stroke-dasharray:5 3}.ts{font:11px sans-serif;fill:currentColor}.tl{font:600 11.5px sans-serif;fill:currentColor}&lt;/style>
&lt;line class="ax" x1="60" y1="30" x2="60" y2="210"/>
&lt;line class="ax" x1="60" y1="210" x2="730" y2="210"/>
&lt;text x="22" y="125" class="ts" transform="rotate(-90 22 125)">€/GPU-hour&lt;/text>
&lt;text x="350" y="235" class="ts">utilisation (%) →&lt;/text>
&lt;text x="65" y="226" class="ts">0&lt;/text>
&lt;text x="220" y="226" class="ts">30&lt;/text>
&lt;text x="375" y="226" class="ts">60&lt;/text>
&lt;text x="535" y="226" class="ts">90&lt;/text>
&lt;text x="700" y="226" class="ts">100&lt;/text>
&lt;line class="ax" x1="220" y1="207" x2="220" y2="213"/>
&lt;line class="ax" x1="375" y1="207" x2="375" y2="213"/>
&lt;line class="ax" x1="535" y1="207" x2="535" y2="213"/>
&lt;line class="ax" x1="700" y1="207" x2="700" y2="213"/>
&lt;path class="cv" d="M80,40 C150,65 250,110 375,148 C480,178 600,195 710,203"/>
&lt;text x="82" y="36" class="tl">on-prem (fixed capex)&lt;/text>
&lt;line class="cv" x1="60" y1="155" x2="730" y2="155"/>
&lt;text x="632" y="149" class="ts">AWS p5 OD (~6.40 €)&lt;/text>
&lt;line class="cv" x1="60" y1="190" x2="730" y2="190"/>
&lt;text x="620" y="186" class="ts">GCP OD (~10.21 €)&lt;/text>
&lt;line class="cv" x1="60" y1="200" x2="730" y2="200"/>
&lt;text x="620" y="198" class="ts">Azure OD (~11.43 €)&lt;/text>
&lt;line class="cv" x1="60" y1="95" x2="730" y2="95"/>
&lt;text x="632" y="91" class="ts">neocloud OD (~2.70 €)&lt;/text>
&lt;line class="dsh" x1="310" y1="30" x2="310" y2="210"/>
&lt;text x="272" y="48" class="tl">≈47 % (AWS)&lt;/text>
&lt;line class="dsh" x1="187" y1="30" x2="187" y2="210"/>
&lt;text x="148" y="48" class="ts">≈29 % (GCP)&lt;/text>
&lt;/svg>
&lt;/div>
&lt;hr>
&lt;h2 id="sensitivity-analysis">Sensitivity analysis&lt;/h2>
&lt;h3 id="tco-vs-utilisation">TCO vs utilisation&lt;/h3>
&lt;p>The all-in cost per GPU-hour varies inversely with utilisation because capex is fixed:&lt;/p>
$$\frac{d(\text{EUR/GPU-hour})}{du} = -\frac{\text{capex/year} + \text{opex/year}}{4 \times 8{,}760 \times u^2} &lt; 0$$
&lt;p>Going from 50 % to 80 % utilisation cuts the €/GPU-hour by \(\frac{4.75 - 2.97}{4.75} \approx 37\,\%\) in the base scenario. That 37 % reduction requires no hardware change, only more efficient scheduling (see &lt;a href="https://blog.lo0.es/en/posts/gpu-idle-cost-not-on-any-invoice/">GPU utilisation as a FinOps lever&lt;/a>).&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Utilisation&lt;/th>
&lt;th>€/GPU-hour (base scenario)&lt;/th>
&lt;th>Change vs 50 %&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>30 %&lt;/td>
&lt;td>7.93&lt;/td>
&lt;td>+67 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>50 %&lt;/td>
&lt;td>4.75&lt;/td>
&lt;td>reference&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>70 %&lt;/td>
&lt;td>3.39&lt;/td>
&lt;td>−29 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>80 %&lt;/td>
&lt;td>2.97&lt;/td>
&lt;td>−37 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>100 %&lt;/td>
&lt;td>2.38&lt;/td>
&lt;td>−50 %&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="tco-vs-energy-price">TCO vs energy price&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Energy price (€/kWh)&lt;/th>
&lt;th>Energy opex/year&lt;/th>
&lt;th>€/GPU-hour (70 % util.)&lt;/th>
&lt;th>Change vs base&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>0.034 (solar PPA)&lt;/td>
&lt;td>1,604 €&lt;/td>
&lt;td>&lt;strong>3.00&lt;/strong>&lt;/td>
&lt;td>−12 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0.116 (industrial ES, base)&lt;/td>
&lt;td>5,475 €&lt;/td>
&lt;td>&lt;strong>3.39&lt;/strong>&lt;/td>
&lt;td>reference&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0.160 (European average)&lt;/td>
&lt;td>7,550 €&lt;/td>
&lt;td>&lt;strong>3.54&lt;/strong>&lt;/td>
&lt;td>+4 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0.200 (high tariff)&lt;/td>
&lt;td>9,437 €&lt;/td>
&lt;td>&lt;strong>3.67&lt;/strong>&lt;/td>
&lt;td>+8 %&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Energy has a &lt;strong>moderate&lt;/strong> impact on total TCO (8–12 % variation between the extremes), because hardware capex dominates. Over a very long amortisation (5 years) with a solar PPA, however, energy drops from 6 % to 1 % of total TCO and the differential is amplified. The price of energy matters more for the carbon footprint (CSRD) than for TCO when capex is dominant.&lt;/p>
&lt;h3 id="tco-vs-pue">TCO vs PUE&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>PUE&lt;/th>
&lt;th>Cooling overhead&lt;/th>
&lt;th>Energy/year (0.116 €/kWh)&lt;/th>
&lt;th>€/GPU-hour (70 % util.)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>1.15 (liquid cooling, new facilities)&lt;/td>
&lt;td>+15 %&lt;/td>
&lt;td>2,166 €&lt;/td>
&lt;td>&lt;strong>3.21&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1.20 (liquid, modern datacenter)&lt;/td>
&lt;td>+20 %&lt;/td>
&lt;td>2,259 €&lt;/td>
&lt;td>&lt;strong>3.23&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1.48 (facilities &amp;lt;5 years, Uptime 2025)&lt;/td>
&lt;td>+48 %&lt;/td>
&lt;td>3,490 €&lt;/td>
&lt;td>&lt;strong>3.33&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1.54 (global average, Uptime 2025)&lt;/td>
&lt;td>+54 %&lt;/td>
&lt;td>3,627 €&lt;/td>
&lt;td>&lt;strong>3.39&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1.80 (legacy colocation)&lt;/td>
&lt;td>+80 %&lt;/td>
&lt;td>4,260 €&lt;/td>
&lt;td>&lt;strong>3.47&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The difference between PUE 1.15 (liquid) and 1.80 (legacy) is barely &lt;strong>~8 %&lt;/strong> of the €/GPU-hour at 70 % utilisation, because energy is only a fraction of TCO. PUE matters far more for the &lt;strong>absolute energy cost&lt;/strong> and for &lt;strong>CSRD reporting&lt;/strong> than for total TCO when hardware is the dominant component.&lt;/p>
&lt;h3 id="tco-vs-amortisation-years">TCO vs amortisation years&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Amortisation&lt;/th>
&lt;th>Capex/year (average node, USD)&lt;/th>
&lt;th>€/GPU-hour (70 % util., base opex scenario)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>3 years&lt;/td>
&lt;td>59,500 USD (~55,300 €)&lt;/td>
&lt;td>&lt;strong>3.39&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>4 years&lt;/td>
&lt;td>44,625 USD (~41,500 €)&lt;/td>
&lt;td>&lt;strong>2.99&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>5 years&lt;/td>
&lt;td>35,700 USD (~33,200 €)&lt;/td>
&lt;td>&lt;strong>2.72&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Stretching the amortisation from 3 to 5 years lowers the €/GPU-hour by ~&lt;strong>20 %&lt;/strong>, assuming the hardware remains competitive and the resale market supports the residual value. With the refresh cycle accelerated by Blackwell GB200/GB300, a 5-year amortisation carries a higher risk of technological obsolescence.&lt;/p>
&lt;h3 id="sensitivity-heat-map-gpu-hour-at-70--utilisation-base-scenario">Sensitivity heat map (€/GPU-hour at 70 % utilisation, base scenario)&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>PUE 1.15&lt;/th>
&lt;th>PUE 1.54&lt;/th>
&lt;th>PUE 1.80&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>3-yr amort., solar PPA (0.034 €)&lt;/strong>&lt;/td>
&lt;td>2.72&lt;/td>
&lt;td>2.74&lt;/td>
&lt;td>2.76&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>3-yr amort., industrial (0.116 €)&lt;/strong>&lt;/td>
&lt;td>3.21&lt;/td>
&lt;td>3.39&lt;/td>
&lt;td>3.47&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>5-yr amort., industrial (0.116 €)&lt;/strong>&lt;/td>
&lt;td>2.54&lt;/td>
&lt;td>2.72&lt;/td>
&lt;td>2.80&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>3-yr amort., high tariff (0.200 €)&lt;/strong>&lt;/td>
&lt;td>3.44&lt;/td>
&lt;td>3.67&lt;/td>
&lt;td>3.78&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="decision-table-costcontrolsovereignty-pareto">Decision table: cost/control/sovereignty Pareto&lt;/h2>
&lt;p>The table below crosses the four dimensions with no implicit hierarchy; the ordinal reading depends on each organisation&amp;rsquo;s constraints.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Option&lt;/th>
&lt;th>€/GPU-hour&lt;/th>
&lt;th>Initial capex&lt;/th>
&lt;th>Full stack control&lt;/th>
&lt;th>EU sovereignty&lt;/th>
&lt;th>Elasticity&lt;/th>
&lt;th>Operational risk&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>On-prem (util. &amp;gt;70 %, low opex)&lt;/strong>&lt;/td>
&lt;td>2.40–3.00&lt;/td>
&lt;td>high (150–207k USD/node)&lt;/td>
&lt;td>total&lt;/td>
&lt;td>total&lt;/td>
&lt;td>none&lt;/td>
&lt;td>hardware failure, idle&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>On-prem (util. &amp;lt;50 %, base opex)&lt;/strong>&lt;/td>
&lt;td>4.75–7.93&lt;/td>
&lt;td>high&lt;/td>
&lt;td>total&lt;/td>
&lt;td>total&lt;/td>
&lt;td>none&lt;/td>
&lt;td>capex with no return&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Neocloud 3-year reserved (CoreWeave, Lambda)&lt;/strong>&lt;/td>
&lt;td>1.49–2.10 USD&lt;/td>
&lt;td>none&lt;/td>
&lt;td>partial (API)&lt;/td>
&lt;td>depends on the provider&lt;/td>
&lt;td>rigid contract&lt;/td>
&lt;td>minimal interruption&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Neocloud on-demand (Lambda, Spheron)&lt;/strong>&lt;/td>
&lt;td>2.49–3.44 USD&lt;/td>
&lt;td>none&lt;/td>
&lt;td>partial&lt;/td>
&lt;td>depends&lt;/td>
&lt;td>total&lt;/td>
&lt;td>no interruption&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>AWS p5 on-demand&lt;/strong>&lt;/td>
&lt;td>6.88 USD&lt;/td>
&lt;td>none&lt;/td>
&lt;td>minimal&lt;/td>
&lt;td>NO (CLOUD Act)&lt;/td>
&lt;td>total&lt;/td>
&lt;td>no interruption&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>AWS p5 3-year reserved&lt;/strong>&lt;/td>
&lt;td>~2.97 USD&lt;/td>
&lt;td>financial commitment&lt;/td>
&lt;td>minimal&lt;/td>
&lt;td>NO (CLOUD Act)&lt;/td>
&lt;td>rigid&lt;/td>
&lt;td>no interruption&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Sovereign EU cloud (Scaleway, Nebius EU)&lt;/strong>&lt;/td>
&lt;td>2.15–3.85 USD&lt;/td>
&lt;td>none&lt;/td>
&lt;td>partial&lt;/td>
&lt;td>yes (EU)&lt;/td>
&lt;td>total&lt;/td>
&lt;td>no interruption&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Hybrid on-prem base + EU cloud peak&lt;/strong>&lt;/td>
&lt;td>2.00–3.50 (weighted)&lt;/td>
&lt;td>medium&lt;/td>
&lt;td>high&lt;/td>
&lt;td>yes (EU)&lt;/td>
&lt;td>elastic peak&lt;/td>
&lt;td>operational complexity&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>&amp;ldquo;EU sovereignty&amp;rdquo; column: US hyperscalers (AWS, GCP, Azure) are subject to the US CLOUD Act regardless of the datacenter region. Nebius has a Dutch legal entity; CoreWeave is a US company. See the full analysis in &lt;a href="https://blog.lo0.es/en/posts/sovereign-on-premise-vs-hyperscalers-data/">sovereign on-premise vs hyperscalers&lt;/a>.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>&amp;ldquo;Full stack control&amp;rdquo; column: on-prem lets you choose the driver version, the kernel, the NCCL configuration, MIG partitioning, and any system parameter. Cloud options offer control at container/pod level, with the hypervisor and firmware opaque.&lt;/p>
&lt;/blockquote>
&lt;p>The cost/sovereignty Pareto frontier for GDPR data excludes US hyperscalers, leaving: &lt;strong>on-prem&lt;/strong>, &lt;strong>sovereign EU cloud&lt;/strong>, and the &lt;strong>hybrid&lt;/strong>. Among those three, the deciding variable is sustained utilisation and the predictability of traffic (see &lt;a href="https://blog.lo0.es/en/posts/capacity-planning-on-premise-llm-inference/">capacity planning for on-premise LLM inference&lt;/a>).&lt;/p>
&lt;hr>
&lt;h2 id="integration-with-the-series-finops-model">Integration with the series FinOps model&lt;/h2>
&lt;p>The all-in €/GPU-hour of on-prem is the number that feeds the cost allocation pipeline of the series:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Cost-per-token identity&lt;/strong> (&lt;a href="https://blog.lo0.es/en/posts/gpu-hour-to-cost-per-token-on-prem-vs-cloud/">cost per token and per request&lt;/a>): engine throughput × €/GPU-hour → €/1M tokens.&lt;/li>
&lt;li>&lt;strong>Chargeback and showback&lt;/strong> (&lt;a href="https://blog.lo0.es/en/posts/opencost-gpu-cost-allocation-kubernetes/">chargeback and showback in GPU multitenancy&lt;/a>): the all-in €/GPU-hour is the internal price charged to each tenant of the multi-tenant cluster.&lt;/li>
&lt;li>&lt;strong>Utilisation as a lever&lt;/strong> (&lt;a href="https://blog.lo0.es/en/posts/gpu-idle-cost-not-on-any-invoice/">GPU utilisation as FinOps&lt;/a>): raising utilisation from 50 % to 80 % cuts the €/GPU-hour by 37 % without changing the hardware, the highest ROI in on-prem FinOps.&lt;/li>
&lt;li>&lt;strong>Capacity planning&lt;/strong> (&lt;a href="https://blog.lo0.es/en/posts/capacity-planning-on-premise-llm-inference/">capacity planning for on-premise LLM inference&lt;/a>): the number of nodes to buy depends on the percentile of base load you want to cover in iron.&lt;/li>
&lt;li>&lt;strong>Cloud comparison&lt;/strong> (&lt;a href="https://blog.lo0.es/en/posts/cloud-gpu-on-demand-reserved-spot-neoclouds-2026-prices/">cloud GPU: on-demand, reserved and spot&lt;/a>): the all-in €/GPU-hour goes head to head with the cloud price in table A7 to compute the break-even.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="sources">Sources&lt;/h2>
&lt;ul>
&lt;li>Spheron · LLM Inference On-Premise vs GPU Cloud: 2026 Cost and Break-Even Analysis (abr. 2026) — &lt;a href="https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/">https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/&lt;/a>&lt;/li>
&lt;li>Introl · GPU Infrastructure TCO Model: 5-Year Cost Analysis for Enterprise AI (abr. 2026) — &lt;a href="https://introl.com/blog/gpu-infrastructure-tco-5-year-cost-model">https://introl.com/blog/gpu-infrastructure-tco-5-year-cost-model&lt;/a>&lt;/li>
&lt;li>GMI Cloud · NVIDIA H100 GPU Pricing: 2026 Rent vs. Buy Cost Analysis (abr. 2026) — &lt;a href="https://www.gmicloud.ai/en/blog/nvidia-h100-gpu-pricing-2026-rent-vs-buy-cost-analysis">https://www.gmicloud.ai/en/blog/nvidia-h100-gpu-pricing-2026-rent-vs-buy-cost-analysis&lt;/a>&lt;/li>
&lt;li>Uptime Institute · Global Data Center Survey 2025 (PDF oficial) — &lt;a href="https://datacenter.uptimeinstitute.com/rs/711-RIA-145/images/2025.Annual.Survey.Report.pdf">https://datacenter.uptimeinstitute.com/rs/711-RIA-145/images/2025.Annual.Survey.Report.pdf&lt;/a>&lt;/li>
&lt;li>Uptime Institute · Global Data Center PUE Stalls at 1.54 (comunicado, oct. 2025) — &lt;a href="https://mgrid.org/2025/10/01/uptime-institute-data-center-pue-stagnation-2025-liquid-cooling/">https://mgrid.org/2025/10/01/uptime-institute-data-center-pue-stagnation-2025-liquid-cooling/&lt;/a>&lt;/li>
&lt;li>GlobalPetrolPrices · Spain Business Electricity Price (sept. 2025) — &lt;a href="https://www.globalpetrolprices.com/Spain/electricity_prices/">https://www.globalpetrolprices.com/Spain/electricity_prices/&lt;/a>&lt;/li>
&lt;li>PV Tech · European Solar PPA Prices Fall Below 35 €/MWh in Q3 2025 (oct. 2025) — &lt;a href="https://www.pv-tech.org/european-solar-ppa-prices-fall-below-35-mwh-q3-2025/">https://www.pv-tech.org/european-solar-ppa-prices-fall-below-35-mwh-q3-2025/&lt;/a>&lt;/li>
&lt;li>Xi Computers · Supermicro SYS-821GE-TNHR 8U GPU Server Pricing (jun. 2026) — &lt;a href="https://www.xicomputer.com/Solutions/Data-Center-Servers/SYS-821GE-TNHR.asp">https://www.xicomputer.com/Solutions/Data-Center-Servers/SYS-821GE-TNHR.asp&lt;/a>&lt;/li>
&lt;li>Encoradvisors · Data Center Colocation Pricing 2026 — &lt;a href="https://encoradvisors.com/data-center-colocation-pricing/">https://encoradvisors.com/data-center-colocation-pricing/&lt;/a>&lt;/li>
&lt;li>CloudZero · H100 GPU Cost In 2026: Buy, Rent, and Cloud Pricing Compared — &lt;a href="https://www.cloudzero.com/blog/h100-gpu-cost/">https://www.cloudzero.com/blog/h100-gpu-cost/&lt;/a>&lt;/li>
&lt;li>Spheron · AI Inference Power Consumption and GPU Electricity Costs: 2026 Guide — &lt;a href="https://www.spheron.network/blog/ai-inference-power-electricity-cost-2026/">https://www.spheron.network/blog/ai-inference-power-electricity-cost-2026/&lt;/a>&lt;/li>
&lt;li>NVIDIA · DGX SuperPOD H100 Electrical Specifications (documentación oficial) — &lt;a href="https://docs.nvidia.com/dgx-superpod/design-guides/dgx-superpod-data-center-design-h100/latest/electrical.html">https://docs.nvidia.com/dgx-superpod/design-guides/dgx-superpod-data-center-design-h100/latest/electrical.html&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Cloud GPU: on-demand, reserved and spot, and why neoclouds change the board (with 2026 prices)</title><link>https://blog.lo0.es/en/posts/cloud-gpu-on-demand-reserved-spot-neoclouds-2026-prices/</link><pubDate>Tue, 16 Jun 2026 05:00:00 +0200</pubDate><guid>https://blog.lo0.es/en/posts/cloud-gpu-on-demand-reserved-spot-neoclouds-2026-prices/</guid><description>&lt;blockquote>
&lt;p>Notation: amounts in &lt;strong>N USD&lt;/strong> or &lt;strong>N €&lt;/strong>, decimal point. The dollar sign is not used
(on this site it is the formula delimiter). All cloud prices change without notice;
the date of the source is given next to each figure.&lt;/p>
&lt;/blockquote>
&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>An H100 SXM5 on-demand costs between &lt;strong>2.49 USD/GPU-hour&lt;/strong> (Lambda, a neocloud) and &lt;strong>12.29 USD/GPU-hour&lt;/strong>
(Azure ND H100 v5) depending on the provider, a spread of &lt;strong>5× between the neocloud floor and the
hyperscaler ceiling&lt;/strong>. The &lt;em>reserved&lt;/em> mode (a 1-3 year commitment) lowers the price by 20-40 % and is
the number comparable to amortising your own iron; the &lt;em>spot/preemptible&lt;/em> mode takes another
40-65 % off, but with a risk of interruption on less than 2 minutes&amp;rsquo; notice. For the
on-prem vs cloud analysis of this series (article &lt;a href="https://blog.lo0.es/en/posts/sovereign-on-premise-vs-hyperscalers-data/">sovereign on-premise vs hyperscalers&lt;/a>),
neocloud &lt;em>reserved&lt;/em> is the correct reference: at &lt;strong>1.49-2.10 USD/GPU-hour&lt;/strong> with a 3-year
commitment, neoclouds compete head-on with the TCO of your own iron at medium utilisation.&lt;/p>
&lt;hr>
&lt;h2 id="the-three-cloud-gpu-purchasing-models">The three cloud GPU purchasing models&lt;/h2>
&lt;h3 id="on-demand">On-demand&lt;/h3>
&lt;p>Pay per use with no commitment, billed by the hour or by the minute. Maximum flexibility; maximum price.
Applies to: development, experimentation, occasional capacity peaks and any workload whose
lifetime is unpredictable. The absence of commitment has a cost: the on-demand premium
over &lt;em>reserved&lt;/em> ranges from 25 % to 60 % depending on the provider.&lt;/p>
&lt;h3 id="reserved--committed">Reserved / committed&lt;/h3>
&lt;p>A 1 or 3 year contract that guarantees capacity in exchange for a discount on the on-demand rate.
At hyperscalers it goes by the name &lt;em>Reserved Instance&lt;/em> (AWS), &lt;em>Committed Use Discount&lt;/em> (GCP) or
&lt;em>Azure Reservation&lt;/em>. At neoclouds it is negotiated directly and can include the choice of node,
network (Ethernet or InfiniBand) and region. Typical discounts:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Term&lt;/th>
&lt;th>Discount on on-demand (market reference)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>1 year&lt;/td>
&lt;td>20-37 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3 years&lt;/td>
&lt;td>30-45 %&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;em>Reserved&lt;/em> is the model &lt;strong>comparable to on-prem&lt;/strong>: in both cases a fixed cost is incurred
up front (amortised capex on-prem; committed payment in the cloud). The formula for the effective
cost per GPU-hour is the same in both cases:&lt;/p>
$$\text{cost/GPU-hour}_{reserved} = \frac{\text{committed price (USD/GPU-h)} \times 8760}{\text{contracted hours}}$$
&lt;p>Unlike on-prem, cloud reserved includes neither hardware capex nor operational opex,
but neither does it include the benefits of running your own iron at high load (see
&lt;a href="https://blog.lo0.es/en/posts/gpu-idle-cost-not-on-any-invoice/">GPU utilisation as a FinOps lever&lt;/a>).&lt;/p>
&lt;h3 id="spot--preemptible">Spot / preemptible&lt;/h3>
&lt;p>Idle provider capacity offered at a 40-65 % discount on on-demand, on the
condition that it can be reclaimed with 30 seconds to 2 minutes of notice
(&lt;a href="https://aws.amazon.com/ec2/spot/">AWS Spot&lt;/a>, &lt;a href="https://cloud.google.com/compute/docs/instances/spot">GCP Spot VMs&lt;/a>).
The discount is real and reproducible; so is the interruption risk.&lt;/p>
&lt;p>&lt;strong>Workloads suitable for spot:&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Workload&lt;/th>
&lt;th>Requires checkpointing&lt;/th>
&lt;th>Suitable for spot&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>LLM pre-training (checkpoint-resume every 15-30 min)&lt;/td>
&lt;td>yes&lt;/td>
&lt;td>yes&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LoRA/QLoRA fine-tuning with checkpoints&lt;/td>
&lt;td>yes&lt;/td>
&lt;td>yes&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Offline batch inference (embeddings, evaluations)&lt;/td>
&lt;td>no (idempotent)&lt;/td>
&lt;td>yes&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Hyperparameter search&lt;/td>
&lt;td>no (repeatable)&lt;/td>
&lt;td>yes&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Production serving with a latency SLO&lt;/td>
&lt;td>not applicable&lt;/td>
&lt;td>&lt;strong>no&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Production serving with an availability SLO&lt;/td>
&lt;td>not applicable&lt;/td>
&lt;td>&lt;strong>no&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>A fine-tune of a 7B model on 4× H100 over 40 hours on spot achieved real savings of
81 % against the equivalent on-demand (&lt;a href="https://www.spheron.network/blog/gpu-spot-instance-arbitrage-2026/">Spheron Blog, May 2026&lt;/a>).
The typical interruption rate on high-reliability H100 instances is 2-8 % per day, which gives
a 92-98 % probability of completing a 24-hour shift without a cut
(&lt;a href="https://www.thundercompute.com/blog/cloud-gpu-spot-instance-availability">Thunder Compute, Jun. 2026&lt;/a>).&lt;/p>
&lt;hr>
&lt;h2 id="the-neoclouds-gpu-specialised-clouds">The neoclouds: GPU-specialised clouds&lt;/h2>
&lt;h3 id="definition-and-differentiators">Definition and differentiators&lt;/h3>
&lt;p>&lt;em>Neoclouds&lt;/em> (or GPU-specialised clouds) are infrastructure providers focused
exclusively on GPU compute for AI workloads, without the diversity of services of the
hyperscalers. SemiAnalysis coined the term in 2024 and identifies CoreWeave, Nebius, Lambda and
Crusoe at the top tier (&lt;a href="https://newsletter.semianalysis.com/p/the-great-gpu-shortage-rental-capacity">SemiAnalysis, 2024&lt;/a>).&lt;/p>
&lt;p>Structural differences with respect to the hyperscalers:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Dimension&lt;/th>
&lt;th>Neoclouds (CoreWeave, Lambda, Nebius…)&lt;/th>
&lt;th>Hyperscalers (AWS, GCP, Azure)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>H100 on-demand price&lt;/strong>&lt;/td>
&lt;td>2.49-6.16 USD/GPU-hour&lt;/td>
&lt;td>6.88-12.29 USD/GPU-hour&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Interconnect network&lt;/strong>&lt;/td>
&lt;td>InfiniBand NDR 400 Gbps (CoreWeave, Voltage Park), Ethernet 200+ Gbps (Lambda, Nebius)&lt;/td>
&lt;td>Proprietary Ethernet (EFA, GVNIC) or InfiniBand (only p5/H100 on AWS)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Access to new hardware&lt;/strong>&lt;/td>
&lt;td>NVIDIA priority access (CoreWeave is an NVIDIA shareholder); H200/B200 catalogue available earlier&lt;/td>
&lt;td>Slower catalogue; B200 in reserved or limited capacity in 2026&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Contracts&lt;/strong>&lt;/td>
&lt;td>1-36 months, negotiated directly&lt;/td>
&lt;td>Standard RI (1-3 years), no node negotiation&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Additional services&lt;/strong>&lt;/td>
&lt;td>Managed Kubernetes, storage, bare-metal&lt;/td>
&lt;td>Complete ecosystem (IAM, VPC, databases, ML services…)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Egress&lt;/strong>&lt;/td>
&lt;td>Zero-egress or flat fee (Lambda, Voltage Park, Nebius)&lt;/td>
&lt;td>0.08-0.12 USD/GB (AWS, GCP, Azure)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Sovereignty&lt;/strong>&lt;/td>
&lt;td>Varies: Nebius (EU), Crusoe (US, clean), CoreWeave (US/EU)&lt;/td>
&lt;td>US CLOUD Act; no guarantee of EU jurisdiction&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The neocloud market is estimated at &lt;strong>20,000 M USD of revenue in 2026&lt;/strong> and is growing towards
180,000 M USD in 2030 (&lt;a href="https://www.srgresearch.com/articles/neocloud-market-forecast-to-approach-400b-by-2031-driven-by-surging-ai-infrastructure-demand">Synergy Research Group, 2026&lt;/a>).
CoreWeave passed 5,000 M USD of ARR faster than any other cloud platform.
Neoclouds price their H100s &lt;strong>60-85 % below AWS and Azure&lt;/strong> for the same silicon
(&lt;a href="https://www.signisys.com/blog/the-neocloud-revolution-how-20-billion-in-gpu-focused-providers-are-reshaping-the-cloud-market/">Signisys, 2026&lt;/a>).&lt;/p>
&lt;h3 id="profiles-of-the-main-neoclouds">Profiles of the main neoclouds&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Provider&lt;/th>
&lt;th>Main hardware&lt;/th>
&lt;th>Network&lt;/th>
&lt;th>Jurisdiction&lt;/th>
&lt;th>Relevant note&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>CoreWeave&lt;/strong>&lt;/td>
&lt;td>H100, H200, B200, GB200&lt;/td>
&lt;td>InfiniBand NDR (Quantum-2 SHARP)&lt;/td>
&lt;td>US / EU&lt;/td>
&lt;td>NVIDIA shareholder; priority access to hardware; largest neocloud by fleet&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Lambda Labs&lt;/strong>&lt;/td>
&lt;td>H100, A100, B200&lt;/td>
&lt;td>Ethernet&lt;/td>
&lt;td>US&lt;/td>
&lt;td>Zero-egress; no spot; per-minute billing&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Nebius&lt;/strong>&lt;/td>
&lt;td>H100, H200, B200, B300&lt;/td>
&lt;td>InfiniBand&lt;/td>
&lt;td>EU (Amsterdam)&lt;/td>
&lt;td>Spin-off of Yandex Cloud; zero-egress; object egress 0.015 USD/GiB; public on-demand price&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Crusoe&lt;/strong>&lt;/td>
&lt;td>H100, A100, MI300X&lt;/td>
&lt;td>Ethernet&lt;/td>
&lt;td>US&lt;/td>
&lt;td>100 % renewable/flare gas energy; AMD MI300X at 3.45 USD/hr&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Voltage Park&lt;/strong>&lt;/td>
&lt;td>H100&lt;/td>
&lt;td>Ethernet (1.99 USD) / InfiniBand 3200 Gbps (2.49 USD)&lt;/td>
&lt;td>US&lt;/td>
&lt;td>Merged with Lightning AI in Jan. 2026; no minimums; non-profit Navigation Fund&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Together AI&lt;/strong>&lt;/td>
&lt;td>H100, A100&lt;/td>
&lt;td>Ethernet&lt;/td>
&lt;td>US&lt;/td>
&lt;td>Inference platform + raw compute&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>GMI Cloud&lt;/strong>&lt;/td>
&lt;td>H100, H200&lt;/td>
&lt;td>Ethernet&lt;/td>
&lt;td>Asia/US&lt;/td>
&lt;td>H200 on-demand from 2.60 USD/hr&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>RunPod&lt;/strong>&lt;/td>
&lt;td>H100, H200, B200, A100&lt;/td>
&lt;td>Ethernet / Secure Cloud&lt;/td>
&lt;td>Global&lt;/td>
&lt;td>Community + Secure Cloud with SLA; spot available&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>Note: the &amp;ldquo;zero-egress&amp;rdquo;, &amp;ldquo;renewable&amp;rdquo; and priority hardware access claims are marketing from
each provider and have not been verified by independent audits. They are reproduced with that
caveat.&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="2026-price-table-h100-sxm-by-provider-and-mode">2026 price table: H100 SXM by provider and mode&lt;/h2>
&lt;p>&lt;strong>Source and date given for each figure. Prices change without notice.&lt;/strong>&lt;/p>
&lt;h3 id="h100-sxm5-80-gb-hbm3--usd-per-gpu-hour">H100 SXM5 (80 GB HBM3) — USD per GPU-hour&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Provider&lt;/th>
&lt;th>On-demand&lt;/th>
&lt;th>1-year reserved&lt;/th>
&lt;th>3-year reserved&lt;/th>
&lt;th>Spot/Preemptible&lt;/th>
&lt;th>Source and date&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>CoreWeave&lt;/strong>&lt;/td>
&lt;td>~6.16&lt;/td>
&lt;td>~2.10-3.00 (negotiated)&lt;/td>
&lt;td>~1.49-2.10 (negotiated)&lt;/td>
&lt;td>not available&lt;/td>
&lt;td>&lt;a href="https://www.thundercompute.com/blog/coreweave-gpu-pricing-review">Thunder Compute, Jun. 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Lambda Labs&lt;/strong>&lt;/td>
&lt;td>2.49-3.44&lt;/td>
&lt;td>~1.89 (37 % disc.)&lt;/td>
&lt;td>~1.84 (PCIe 3-yr)&lt;/td>
&lt;td>not available&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/lambda-cloud-h100-pricing-2026/">Spheron Blog, May 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Nebius&lt;/strong>&lt;/td>
&lt;td>&lt;strong>3.85&lt;/strong>&lt;/td>
&lt;td>discount up to 35 % (contact)&lt;/td>
&lt;td>discount up to 35 % (contact)&lt;/td>
&lt;td>&lt;strong>2.15&lt;/strong> (preemptible)&lt;/td>
&lt;td>&lt;a href="https://nebius.com/prices">nebius.com/prices, Jun. 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Voltage Park&lt;/strong>&lt;/td>
&lt;td>&lt;strong>1.99&lt;/strong> (Ethernet) / &lt;strong>2.49&lt;/strong> (InfiniBand)&lt;/td>
&lt;td>contact (≥6 months)&lt;/td>
&lt;td>contact&lt;/td>
&lt;td>not available&lt;/td>
&lt;td>&lt;a href="https://www.voltagepark.com/pricing">voltagepark.com/pricing, Jun. 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>RunPod&lt;/strong> (Secure)&lt;/td>
&lt;td>3.29&lt;/td>
&lt;td>not available&lt;/td>
&lt;td>not available&lt;/td>
&lt;td>available (~59 % disc.)&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/gpu-cloud-pricing-comparison-2026/">Spheron Blog, May 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Spheron&lt;/strong>&lt;/td>
&lt;td>2.50&lt;/td>
&lt;td>volume discount (contact)&lt;/td>
&lt;td>volume discount (contact)&lt;/td>
&lt;td>&lt;strong>1.03&lt;/strong>&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/gpu-cloud-pricing-comparison-2026/">Spheron pricing, May 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>AWS&lt;/strong> (p5.48xlarge)&lt;/td>
&lt;td>&lt;strong>6.88&lt;/strong>&lt;/td>
&lt;td>~2.97 (3-yr, ~23.78 USD/h node ÷ 8)&lt;/td>
&lt;td>~2.97&lt;/td>
&lt;td>&lt;strong>~1.14&lt;/strong> (p5 spot, 9.10 USD/h node)&lt;/td>
&lt;td>&lt;a href="https://instances.vantage.sh/aws/ec2/p5.48xlarge">Vantage, Jun. 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>GCP&lt;/strong> (A3 High)&lt;/td>
&lt;td>&lt;strong>10.98&lt;/strong>&lt;/td>
&lt;td>~8.78 (1-yr CUD)&lt;/td>
&lt;td>n/a&lt;/td>
&lt;td>&lt;strong>~3.69&lt;/strong>&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/google-cloud-a3-h100-pricing/">Spheron Blog, May 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Azure&lt;/strong> (ND H100 v5)&lt;/td>
&lt;td>&lt;strong>12.29&lt;/strong>&lt;/td>
&lt;td>~6.15-8.60 (1-3 yr reservation)&lt;/td>
&lt;td>~6.15-8.60&lt;/td>
&lt;td>~2.27 (node spot ÷ 8)&lt;/td>
&lt;td>&lt;a href="https://instances.vantage.sh/azure/vm/nd96isrh100-v5">Vantage Azure, Jun. 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;em>All amounts in USD/GPU-hour, rounded to 2 decimals. The &amp;ldquo;3-year reserved&amp;rdquo; column is
the number comparable to the amortisation cost of your own hardware.&lt;/em>&lt;/p>
&lt;p>&lt;em>AWS p5.48xlarge: an 8× H100 node; on-demand 55.04 USD/h → 6.88 USD/GPU-h; 3-yr reserved 23.78 USD/h
→ 2.97 USD/GPU-h; spot 9.10-25.35 USD/h (varies by region and availability).&lt;/em>
&lt;em>GCP a3-highgpu-8g: 8× H100 SXM5; on-demand 87.84 USD/h → 10.98 USD/GPU-h; spot ~3.69 USD/GPU-h
(us-central1, May 2026).&lt;/em>
&lt;em>Azure ND96isr H100 v5: 8× H100; on-demand 98.32 USD/h → 12.29 USD/GPU-h; node spot 18.17 USD/h
→ ~2.27 USD/GPU-h (source: Vantage, Jun. 2026, us-east-1).&lt;/em>&lt;/p>
&lt;hr>
&lt;h2 id="2026-price-table-h200-and-b200">2026 price table: H200 and B200&lt;/h2>
&lt;h3 id="h200-sxm-141-gb-hbm3e--usd-per-gpu-hour">H200 SXM (141 GB HBM3e) — USD per GPU-hour&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Provider&lt;/th>
&lt;th>On-demand&lt;/th>
&lt;th>Spot/Preemptible&lt;/th>
&lt;th>Source and date&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Nebius&lt;/strong>&lt;/td>
&lt;td>&lt;strong>4.50&lt;/strong>&lt;/td>
&lt;td>&lt;strong>2.45&lt;/strong> (preemptible)&lt;/td>
&lt;td>&lt;a href="https://nebius.com/prices">nebius.com/prices, Jun. 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>GMI Cloud&lt;/strong>&lt;/td>
&lt;td>&lt;strong>2.60&lt;/strong>&lt;/td>
&lt;td>not available&lt;/td>
&lt;td>&lt;a href="https://www.gmicloud.ai/en/blog/h200-gpu-provider-pricing">GMI Cloud, May 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>RunPod&lt;/strong>&lt;/td>
&lt;td>4.39&lt;/td>
&lt;td>not available&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/gpu-cloud-pricing-comparison-2026/">Spheron Blog, May 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Spheron&lt;/strong>&lt;/td>
&lt;td>4.54&lt;/td>
&lt;td>not available&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/gpu-cloud-pricing-comparison-2026/">Spheron pricing, May 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>CoreWeave&lt;/strong>&lt;/td>
&lt;td>&lt;strong>6.31&lt;/strong>&lt;/td>
&lt;td>not available&lt;/td>
&lt;td>&lt;a href="https://www.gmicloud.ai/en/blog/h200-gpu-provider-pricing">GMI Cloud, May 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>AWS&lt;/strong> (p5e)&lt;/td>
&lt;td>~4.98&lt;/td>
&lt;td>limited&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/gpu-cloud-pricing-comparison-2026/">Spheron Blog, May 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Azure&lt;/strong>&lt;/td>
&lt;td>~13.78&lt;/td>
&lt;td>not available&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/gpu-cloud-pricing-comparison-2026/">Spheron Blog, May 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="b200-sxm6-192-gb-hbm3e--usd-per-gpu-hour">B200 SXM6 (192 GB HBM3e) — USD per GPU-hour&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Provider&lt;/th>
&lt;th>On-demand&lt;/th>
&lt;th>Spot&lt;/th>
&lt;th>Source and date&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Lambda Labs&lt;/strong>&lt;/td>
&lt;td>4.99-5.29&lt;/td>
&lt;td>not available&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/gpu-cloud-pricing-comparison-2026/">Spheron Blog, May 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Nebius&lt;/strong> (HGX B200)&lt;/td>
&lt;td>&lt;strong>7.15&lt;/strong>&lt;/td>
&lt;td>&lt;strong>3.95&lt;/strong> (preemptible)&lt;/td>
&lt;td>&lt;a href="https://nebius.com/prices">nebius.com/prices, Jun. 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>RunPod&lt;/strong>&lt;/td>
&lt;td>5.89&lt;/td>
&lt;td>not available&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/gpu-cloud-pricing-comparison-2026/">Spheron Blog, May 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Spheron&lt;/strong>&lt;/td>
&lt;td>6.02&lt;/td>
&lt;td>&lt;strong>2.12&lt;/strong>&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/gpu-cloud-pricing-comparison-2026/">Spheron pricing, May 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>AWS&lt;/strong> (p6-B200)&lt;/td>
&lt;td>~14.24&lt;/td>
&lt;td>~3.24&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/gpu-cloud-pricing-comparison-2026/">Spheron Blog, May 2026&lt;/a>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;em>B200 availability is still limited in Q2 2026; many providers offer only
access by advance reservation. The prices are market estimates subject to rapid change.&lt;/em>&lt;/p>
&lt;hr>
&lt;h2 id="how-it-fits-into-the-on-prem-vs-cloud-comparison">How it fits into the on-prem vs cloud comparison&lt;/h2>
&lt;p>The cloud cost per GPU-hour is the number that goes head to head with the on-prem TCO computed in
&lt;a href="https://blog.lo0.es/en/posts/sovereign-on-premise-vs-hyperscalers-data/">sovereign on-premise vs hyperscalers&lt;/a>.
The relationship between the three purchasing modes and the cost analysis is as follows:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Cloud mode&lt;/th>
&lt;th>What it represents in the comparison&lt;/th>
&lt;th>On-prem equivalent&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>On-demand&lt;/strong>&lt;/td>
&lt;td>marginal cost of peak capacity; floor of build-vs-buy at 100 % utilisation&lt;/td>
&lt;td>— (not comparable: on-prem has no elasticity)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>3-year reserved&lt;/strong>&lt;/td>
&lt;td>the committed cost comparable to capex amortised over 3 years&lt;/td>
&lt;td>node amortisation + fixed annual opex&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Spot&lt;/strong>&lt;/td>
&lt;td>the cloud price floor, but with interruption risk&lt;/td>
&lt;td>— (not comparable: on-prem is not interrupted)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The &lt;a href="https://blog.lo0.es/en/posts/gpu-hour-to-cost-per-token-on-prem-vs-cloud/">cost per token&lt;/a> formula applies the same to
cloud as to on-prem:&lt;/p>
$$\text{cost/1M tokens}_{cloud} = \frac{\text{GPU-hour price (USD)} \times 10^6}{\text{throughput (tok/s)} \times 3600}$$
&lt;p>With an H100 SXM5 at 2,800 tok/s (typical throughput for a 70B model in FP8):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Mode&lt;/th>
&lt;th>Reference price&lt;/th>
&lt;th>Cost/1M tokens (70B, 2800 tok/s)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Neocloud on-demand (Lambda)&lt;/td>
&lt;td>2.49 USD/GPU-h&lt;/td>
&lt;td>~0.247 USD (~0.23 €)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Neocloud 3-yr reserved (CoreWeave)&lt;/td>
&lt;td>~1.49 USD/GPU-h&lt;/td>
&lt;td>~0.148 USD (~0.14 €)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Neocloud spot (Spheron/Nebius)&lt;/td>
&lt;td>~1.03-2.15 USD/GPU-h&lt;/td>
&lt;td>~0.102-0.213 USD&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AWS on-demand (p5)&lt;/td>
&lt;td>6.88 USD/GPU-h&lt;/td>
&lt;td>~0.682 USD (~0.63 €)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AWS 3-yr reserved (p5)&lt;/td>
&lt;td>~2.97 USD/GPU-h&lt;/td>
&lt;td>~0.295 USD (~0.27 €)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GCP spot (A3)&lt;/td>
&lt;td>3.69 USD/GPU-h&lt;/td>
&lt;td>~0.366 USD (~0.34 €)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>On-prem amortised, high util. (80 %)&lt;/strong>&lt;/td>
&lt;td>~2.83 USD/GPU-h all-in&lt;/td>
&lt;td>&lt;strong>~0.281 USD (~0.26 €)&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;em>Reference exchange rate: 1 USD = 0.924 €. The rate fluctuates; comparative calculations must
be done in the same currency.&lt;/em>&lt;/p>
&lt;p>The key reading: at &lt;strong>high utilisation&lt;/strong>, on-prem TCO (~2.83 USD/GPU-hour all-in according to
&lt;a href="https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/">Spheron, 2026&lt;/a>) competes with
the &lt;strong>3-year reserved of the neocloud (~1.49-2.97 USD/GPU-hour)&lt;/strong>. On-prem wins at high
utilisation when the all-in cost drops below 2.5 USD; neocloud reserved wins when
utilisation is medium or low, because it does not pay for idle capex. &lt;strong>Spot lowers the cloud floor
but is not comparable&lt;/strong>: interruption makes it incompatible with continuous serving, which is the
case where on-prem competes.&lt;/p>
&lt;div class="diagram" style="max-width:720px;margin:1rem auto;">
&lt;svg viewBox="0 0 720 230" role="img" aria-label="Cost per GPU-hour by mode: neocloud spot is the cheapest but carries interruption risk; the neocloud 3-year reserved competes with on-prem at high utilisation; hyperscaler on-demand is the most expensive" xmlns="http://www.w3.org/2000/svg">
&lt;style>.ax{fill:none;stroke:currentColor;stroke-width:1}.br{fill:none;stroke:currentColor;stroke-width:1.2}.lb{font:11px sans-serif;fill:currentColor}.tl{font:600 11px sans-serif;fill:currentColor}.sm{font:10px sans-serif;fill:currentColor}&lt;/style>
&lt;line class="ax" x1="180" y1="20" x2="180" y2="195"/>
&lt;line class="ax" x1="180" y1="195" x2="710" y2="195"/>
&lt;text x="185" y="210" class="sm">0&lt;/text>
&lt;text x="283" y="210" class="sm">2&lt;/text>
&lt;text x="381" y="210" class="sm">4&lt;/text>
&lt;text x="479" y="210" class="sm">6&lt;/text>
&lt;text x="577" y="210" class="sm">8&lt;/text>
&lt;text x="675" y="210" class="sm">10&lt;/text>
&lt;text x="380" y="222" class="sm">USD/GPU-hour →&lt;/text>
&lt;line class="ax" x1="283" y1="192" x2="283" y2="197"/>
&lt;line class="ax" x1="381" y1="192" x2="381" y2="197"/>
&lt;line class="ax" x1="479" y1="192" x2="479" y2="197"/>
&lt;line class="ax" x1="577" y1="192" x2="577" y2="197"/>
&lt;line class="ax" x1="675" y1="192" x2="675" y2="197"/>
&lt;text x="5" y="35" class="tl">Spot Spheron H100&lt;/text>
&lt;rect class="br" x="180" y="22" width="52" height="14" fill="currentColor" opacity="0.18"/>
&lt;text x="237" y="33" class="sm">1.03&lt;/text>
&lt;text x="5" y="55" class="tl">Spot Nebius H100&lt;/text>
&lt;rect class="br" x="180" y="42" width="106" height="14" fill="currentColor" opacity="0.18"/>
&lt;text x="291" y="53" class="sm">2.15&lt;/text>
&lt;text x="5" y="75" class="tl">Neo reserved 3-yr&lt;/text>
&lt;text x="5" y="86" class="sm">(CoreWeave)&lt;/text>
&lt;rect class="br" x="180" y="60" width="75" height="14" fill="currentColor" opacity="0.28"/>
&lt;text x="260" y="72" class="sm">1.49&lt;/text>
&lt;text x="5" y="105" class="tl">Lambda on-demand&lt;/text>
&lt;rect class="br" x="180" y="92" width="123" height="14" fill="currentColor" opacity="0.28"/>
&lt;text x="308" y="103" class="sm">2.49&lt;/text>
&lt;text x="5" y="125" class="tl">Voltage Park OD&lt;/text>
&lt;rect class="br" x="180" y="112" width="99" height="14" fill="currentColor" opacity="0.28"/>
&lt;text x="284" y="123" class="sm">1.99&lt;/text>
&lt;text x="5" y="145" class="tl">On-prem TCO (80 %)&lt;/text>
&lt;rect class="br" x="180" y="132" width="140" height="14" fill="currentColor" opacity="0.35"/>
&lt;text x="325" y="143" class="sm">~2.83&lt;/text>
&lt;text x="5" y="165" class="tl">AWS p5 on-demand&lt;/text>
&lt;rect class="br" x="180" y="152" width="341" height="14" fill="currentColor" opacity="0.45"/>
&lt;text x="526" y="163" class="sm">6.88&lt;/text>
&lt;text x="5" y="185" class="tl">Azure ND H100 v5&lt;/text>
&lt;rect class="br" x="180" y="172" width="610" height="14" fill="currentColor" opacity="0.55"/>
&lt;text x="795" y="183" class="sm">12.29&lt;/text>
&lt;text x="185" y="183" class="lb">12.29 USD/GPU-h&lt;/text>
&lt;/svg>
&lt;/div>
&lt;hr>
&lt;h2 id="network-factors-infiniband-vs-ethernet">Network factors: InfiniBand vs Ethernet&lt;/h2>
&lt;p>For distributed training and large-scale inference (tensor parallelism across nodes), the bandwidth
of the interconnect network between GPUs is critical. Neoclouds differ in what they offer:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Provider&lt;/th>
&lt;th>GPU-GPU network&lt;/th>
&lt;th>Bandwidth&lt;/th>
&lt;th>Applies to&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>CoreWeave&lt;/td>
&lt;td>InfiniBand NDR (Quantum-2, SHARP)&lt;/td>
&lt;td>400 Gbps/GPU&lt;/td>
&lt;td>multi-node HGX clusters&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Voltage Park&lt;/td>
&lt;td>InfiniBand Quantum-2&lt;/td>
&lt;td>3200 Gbps (8-GPU node)&lt;/td>
&lt;td>the 2.49 USD/hr tier&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Voltage Park&lt;/td>
&lt;td>Ethernet&lt;/td>
&lt;td>200 Gbps&lt;/td>
&lt;td>the 1.99 USD/hr tier&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Lambda Labs&lt;/td>
&lt;td>Ethernet&lt;/td>
&lt;td>200 Gbps&lt;/td>
&lt;td>all nodes&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Nebius&lt;/td>
&lt;td>InfiniBand&lt;/td>
&lt;td>400 Gbps/GPU&lt;/td>
&lt;td>HGX H100/H200/B200&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AWS p5&lt;/td>
&lt;td>EFA (Elastic Fabric Adapter)&lt;/td>
&lt;td>3200 Gbps (node)&lt;/td>
&lt;td>EC2 clusters&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GCP A3&lt;/td>
&lt;td>GVNIC + RoCE&lt;/td>
&lt;td>200 Gbps/GPU&lt;/td>
&lt;td>A3 High&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Azure ND H100 v5&lt;/td>
&lt;td>InfiniBand NDR&lt;/td>
&lt;td>400 Gbps/GPU&lt;/td>
&lt;td>ND96isr H100 v5&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>InfiniBand NDR with SHARP (in-network collective reduction) cuts the latency of allreduce
operations in distributed training. For inference serving with tensor parallelism inside a node
(NVLink), the inter-node network is less critical; for multi-node training or disaggregated
prefill-decode (&lt;a href="https://blog.lo0.es/en/posts/cost-performance-energy-three-axes-llm-inference/">the three axes of the series&lt;/a>),
the network between nodes matters.&lt;/p>
&lt;hr>
&lt;h2 id="neoclouds-and-sovereignty-what-changes">Neoclouds and sovereignty: what changes&lt;/h2>
&lt;p>Unlike the US hyperscalers subject to the US CLOUD Act, some neoclouds
offer options with jurisdiction outside the US:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Provider&lt;/th>
&lt;th>EU region&lt;/th>
&lt;th>US CLOUD Act applies&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Nebius&lt;/td>
&lt;td>Amsterdam (NL), Frankfurt (DE)&lt;/td>
&lt;td>No (Dutch legal entity)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CoreWeave&lt;/td>
&lt;td>Frankfurt (DE), London (GB)&lt;/td>
&lt;td>Yes (US company)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Lambda Labs&lt;/td>
&lt;td>US only&lt;/td>
&lt;td>Yes&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Voltage Park&lt;/td>
&lt;td>US only&lt;/td>
&lt;td>Yes&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Scaleway&lt;/td>
&lt;td>Paris (FR), Amsterdam (NL)&lt;/td>
&lt;td>No (French company)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>US hyperscalers&lt;/td>
&lt;td>EU regions available&lt;/td>
&lt;td>&lt;strong>Yes&lt;/strong> (CLOUD Act applies)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>For data subject to GDPR, only providers with an EU/EFTA legal entity guarantee the
absence of the CLOUD Act. The full discussion of the sovereignty axis is in
&lt;a href="https://blog.lo0.es/en/posts/sovereign-on-premise-vs-hyperscalers-data/">sovereign on-premise vs hyperscalers&lt;/a>.
For capacity planning with this data, see &lt;a href="https://blog.lo0.es/en/posts/capacity-planning-on-premise-llm-inference/">capacity planning for on-premise inference&lt;/a>.&lt;/p>
&lt;hr>
&lt;h2 id="decision-table-purchasing-mode-by-costrisksovereignty-axis">Decision table: purchasing mode by cost/risk/sovereignty axis&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Mode&lt;/th>
&lt;th>Cost&lt;/th>
&lt;th>Operational risk&lt;/th>
&lt;th>Sovereignty (EU)&lt;/th>
&lt;th>When to use&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Neocloud on-demand&lt;/strong>&lt;/td>
&lt;td>medium (2-4 USD/GPU-h)&lt;/td>
&lt;td>low (no interruption)&lt;/td>
&lt;td>depends on the provider&lt;/td>
&lt;td>development, unpredictable peaks, serving with an SLO&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Neocloud 1-yr reserved&lt;/strong>&lt;/td>
&lt;td>low-medium (1.5-3 USD/GPU-h)&lt;/td>
&lt;td>low (no interruption, guaranteed capacity)&lt;/td>
&lt;td>depends on the provider&lt;/td>
&lt;td>stable serving, scheduled training; validated workload&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Neocloud 3-yr reserved&lt;/strong>&lt;/td>
&lt;td>low (1.5-2.1 USD/GPU-h)&lt;/td>
&lt;td>low, financial commitment&lt;/td>
&lt;td>depends on the provider&lt;/td>
&lt;td>comparable to amortised on-prem; very stable base load&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Neocloud spot&lt;/strong>&lt;/td>
&lt;td>minimum (1-2.5 USD/GPU-h)&lt;/td>
&lt;td>high (interruption &amp;lt;2 min)&lt;/td>
&lt;td>depends on the provider&lt;/td>
&lt;td>batch, fine-tuning with checkpoints, hyperparameter search&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Hyperscaler on-demand&lt;/strong>&lt;/td>
&lt;td>high (6.9-12.3 USD/GPU-h)&lt;/td>
&lt;td>low&lt;/td>
&lt;td>NO (CLOUD Act)&lt;/td>
&lt;td>integration with AWS/GCP/Azure services; no alternative&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Hyperscaler reserved&lt;/strong>&lt;/td>
&lt;td>medium-high (3-9 USD/GPU-h)&lt;/td>
&lt;td>low&lt;/td>
&lt;td>NO (CLOUD Act)&lt;/td>
&lt;td>lock-in to the hyperscaler ecosystem is justified&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Hyperscaler spot&lt;/strong>&lt;/td>
&lt;td>medium (1.1-3.7 USD/GPU-h)&lt;/td>
&lt;td>high (interruption)&lt;/td>
&lt;td>NO (CLOUD Act)&lt;/td>
&lt;td>batch with no sovereignty requirement; more availability than neocloud spot&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Amortised on-prem (&amp;gt;70 % util.)&lt;/strong>&lt;/td>
&lt;td>low (~2.3-2.8 USD/GPU-h all-in)&lt;/td>
&lt;td>low (no interruption)&lt;/td>
&lt;td>&lt;strong>total (your own hardware)&lt;/strong>&lt;/td>
&lt;td>stable, high base load; maximum sovereignty&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;em>On-prem competes with neocloud 3-yr reserved when utilisation exceeds 65-70 % (see
&lt;a href="https://blog.lo0.es/en/posts/gpu-idle-cost-not-on-any-invoice/">GPU utilisation as FinOps&lt;/a>).&lt;/em>&lt;/p>
&lt;hr>
&lt;h2 id="computing-the-spot-saving-an-example-with-checkpointing">Computing the spot saving: an example with checkpointing&lt;/h2>
&lt;p>Reference parameters to validate whether spot is worth it:&lt;/p>
$$\text{spot saving} = (\text{OD price} - \text{spot price}) \times \text{total GPU-hours} - \text{retry cost}$$
$$\text{retry cost} = \text{interruption rate} \times \text{lost work (GPU-hours)} \times \text{spot price}$$
&lt;p>With checkpointing every 30 minutes, work lost per interruption is ≤ 0.5 GPU-hours per event.
At an interruption rate of 4 % per day on one H100 GPU:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Item&lt;/th>
&lt;th>Reference value&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>On-demand price (neocloud)&lt;/td>
&lt;td>2.50 USD/GPU-h&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Spot price&lt;/td>
&lt;td>1.03 USD/GPU-h&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Gross saving per GPU-hour&lt;/td>
&lt;td>1.47 USD&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Expected interruptions in 40 h (4 %/day)&lt;/td>
&lt;td>~0.67 events&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Re-run work per event (0.5 h × 1.03 USD)&lt;/td>
&lt;td>~0.34 USD&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Net saving on a 40 h × 4 GPU job&lt;/strong>&lt;/td>
&lt;td>&lt;strong>~234 USD&lt;/strong> (against 400 USD on-demand)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The 58 % saving only materialises if the job implements checkpoint-resume correctly.
Without checkpoints, a single interruption forces a restart from scratch: the saving turns into
an overcost from duplicated work.&lt;/p>
&lt;hr>
&lt;h2 id="integration-into-the-series-tco-model">Integration into the series TCO model&lt;/h2>
&lt;p>The cloud €/GPU-hour is the number that feeds the FinOps analysis of the series. The chain:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Cost of cloud iron&lt;/strong> (this price table) → equivalent to amortised capex + opex of on-prem.&lt;/li>
&lt;li>&lt;strong>Throughput&lt;/strong> (measured with genai-perf, track B of the series) → the denominator.&lt;/li>
&lt;li>&lt;strong>Cost per token&lt;/strong> (&lt;a href="https://blog.lo0.es/en/posts/gpu-hour-to-cost-per-token-on-prem-vs-cloud/">cost per token and per request&lt;/a>) → the final comparative metric.&lt;/li>
&lt;li>&lt;strong>Utilisation&lt;/strong> (&lt;a href="https://blog.lo0.es/en/posts/gpu-idle-cost-not-on-any-invoice/">GPU utilisation as FinOps&lt;/a>) → in on-demand cloud, utilisation does not penalise cost (you pay for what you use); in reserved and in on-prem it does penalise, because the cost is fixed.&lt;/li>
&lt;/ol>
&lt;p>The correct comparison is &lt;strong>neocloud 3-yr reserved vs on-prem amortised over 3 years&lt;/strong>, with the same
assumptions on throughput, egress and utilisation. Spot is the cloud price floor but not the
comparable floor: its interruptible nature excludes it from any comparison with an on-prem cluster
that serves continuous traffic.&lt;/p>
&lt;hr>
&lt;h2 id="sources">Sources&lt;/h2>
&lt;ul>
&lt;li>Spheron Blog · GPU Cloud Pricing 2026: H100 from 1,03 USD/hr, B200 from 2,12 USD/hr (15+ providers), may. 2026 — &lt;a href="https://www.spheron.network/blog/gpu-cloud-pricing-comparison-2026/">https://www.spheron.network/blog/gpu-cloud-pricing-comparison-2026/&lt;/a>&lt;/li>
&lt;li>Vantage · p5.48xlarge pricing and specs (AWS), actualizado jun. 2026 — &lt;a href="https://instances.vantage.sh/aws/ec2/p5.48xlarge">https://instances.vantage.sh/aws/ec2/p5.48xlarge&lt;/a>&lt;/li>
&lt;li>Nebius · NVIDIA GPU Pricing (página oficial, jun. 2026) — &lt;a href="https://nebius.com/prices">https://nebius.com/prices&lt;/a>&lt;/li>
&lt;li>Voltage Park · H100 GPU Pricing (página oficial, jun. 2026) — &lt;a href="https://www.voltagepark.com/pricing">https://www.voltagepark.com/pricing&lt;/a>&lt;/li>
&lt;li>Thunder Compute · CoreWeave GPU Pricing Review (jun. 2026) — &lt;a href="https://www.thundercompute.com/blog/coreweave-gpu-pricing-review">https://www.thundercompute.com/blog/coreweave-gpu-pricing-review&lt;/a>&lt;/li>
&lt;li>Thunder Compute · NVIDIA H100 Pricing (Jun 2026) — &lt;a href="https://www.thundercompute.com/blog/nvidia-h100-pricing">https://www.thundercompute.com/blog/nvidia-h100-pricing&lt;/a>&lt;/li>
&lt;li>Spheron Blog · Lambda Cloud H100 Pricing 2026 — &lt;a href="https://www.spheron.network/blog/lambda-cloud-h100-pricing-2026/">https://www.spheron.network/blog/lambda-cloud-h100-pricing-2026/&lt;/a>&lt;/li>
&lt;li>Spheron Blog · AWS H100 Pricing 2026: P5 Instance Cost — &lt;a href="https://www.spheron.network/blog/aws-h100-pricing-2026/">https://www.spheron.network/blog/aws-h100-pricing-2026/&lt;/a>&lt;/li>
&lt;li>Spheron Blog · Google Cloud A3 H100 Pricing 2026 — &lt;a href="https://www.spheron.network/blog/google-cloud-a3-h100-pricing/">https://www.spheron.network/blog/google-cloud-a3-h100-pricing/&lt;/a>&lt;/li>
&lt;li>Spheron Blog · Azure H100 Pricing 2026: ND H100 v5 — &lt;a href="https://www.spheron.network/blog/azure-h100-pricing/">https://www.spheron.network/blog/azure-h100-pricing/&lt;/a>&lt;/li>
&lt;li>GMI Cloud · H200 GPU Provider Pricing 2026 (CoreWeave, Lambda, Nebius, GMI) — &lt;a href="https://www.gmicloud.ai/en/blog/h200-gpu-provider-pricing">https://www.gmicloud.ai/en/blog/h200-gpu-provider-pricing&lt;/a>&lt;/li>
&lt;li>Spheron Blog · GPU Spot Instance Arbitrage 2026 — &lt;a href="https://www.spheron.network/blog/gpu-spot-instance-arbitrage-2026/">https://www.spheron.network/blog/gpu-spot-instance-arbitrage-2026/&lt;/a>&lt;/li>
&lt;li>Thunder Compute · Cloud GPU Spot Instance Availability and Interruption Rates — &lt;a href="https://www.thundercompute.com/blog/cloud-gpu-spot-instance-availability">https://www.thundercompute.com/blog/cloud-gpu-spot-instance-availability&lt;/a>&lt;/li>
&lt;li>Synergy Research Group · Neocloud Market Forecast to Approach 400B USD by 2031 — &lt;a href="https://www.srgresearch.com/articles/neocloud-market-forecast-to-approach-400b-by-2031-driven-by-surging-ai-infrastructure-demand">https://www.srgresearch.com/articles/neocloud-market-forecast-to-approach-400b-by-2031-driven-by-surging-ai-infrastructure-demand&lt;/a>&lt;/li>
&lt;li>Signisys · The Neocloud Revolution: How 20 Billion USD in GPU Providers Reshapes Cloud Market — &lt;a href="https://www.signisys.com/blog/the-neocloud-revolution-how-20-billion-in-gpu-focused-providers-are-reshaping-the-cloud-market/">https://www.signisys.com/blog/the-neocloud-revolution-how-20-billion-in-gpu-focused-providers-are-reshaping-the-cloud-market/&lt;/a>&lt;/li>
&lt;li>SemiAnalysis · The Great GPU Shortage: Rental Capacity (neocloud taxonomy original) — &lt;a href="https://newsletter.semianalysis.com/p/the-great-gpu-shortage-rental-capacity">https://newsletter.semianalysis.com/p/the-great-gpu-shortage-rental-capacity&lt;/a>&lt;/li>
&lt;li>Spheron Blog · LLM Inference On-Premise vs GPU Cloud: 2026 Cost and Break-Even — &lt;a href="https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/">https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/&lt;/a>&lt;/li>
&lt;li>IntuitionLabs · H100 Rental Prices Compared 1,49-6,98 USD/hr Across 15+ Cloud Providers (2026) — &lt;a href="https://intuitionlabs.ai/articles/h100-rental-prices-cloud-comparison">https://intuitionlabs.ai/articles/h100-rental-prices-cloud-comparison&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>GPU idle: the cost that shows up on no invoice but is paid by the whole TCO</title><link>https://blog.lo0.es/en/posts/gpu-idle-cost-not-on-any-invoice/</link><pubDate>Mon, 15 Jun 2026 05:00:00 +0200</pubDate><guid>https://blog.lo0.es/en/posts/gpu-idle-cost-not-on-any-invoice/</guid><description>&lt;blockquote>
&lt;p>Notation: amounts in &lt;strong>euros (N €)&lt;/strong>, decimals with a point. The dollar sign is not used
(on this site it is a formula delimiter).&lt;/p>
&lt;/blockquote>
&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>The cost per token of on-premise inference is \( \text{€/GPU-hour} \div \text{throughput} \). Throughput is a direct function of the GPU&amp;rsquo;s &lt;strong>useful occupancy&lt;/strong>. On a generic node of 4×H100 SXM with a reference cost of ~11 € per GPU-hour (depreciation plus energy plus infrastructure), the cost curve over occupancy looks like this:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Useful occupancy&lt;/th>
&lt;th>Effective throughput (tok/s)&lt;/th>
&lt;th>Cost per 1M tokens&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>20 %&lt;/td>
&lt;td>~700&lt;/td>
&lt;td>~43 €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>40 %&lt;/td>
&lt;td>~1,400&lt;/td>
&lt;td>~21 €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>70 %&lt;/td>
&lt;td>~2,500&lt;/td>
&lt;td>~12 €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>85 % (practical ceiling)&lt;/td>
&lt;td>~3,000&lt;/td>
&lt;td>~10 €&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Doubling occupancy from 20 % to 40 % halves the cost per token, without buying more iron.&lt;/strong> At 70 % the cost competes with European cloud providers (~2.2 €/GPU-hour on-demand at Scaleway). At 20 % on-prem is four times dearer than renting. The lever is not the model, nor the precision: it is how many paid-for GPU-hours produce serving tokens.&lt;/p>
&lt;hr>
&lt;h2 id="1--the-fundamental-identity">1 · The fundamental identity&lt;/h2>
&lt;p>The cost per million tokens (CPM) in your own inference is not a list price. It is:&lt;/p>
$$
\text{CPM} = \frac{C_{\text{GPU}} \cdot N_{\text{GPU}}}{T_{\text{ef}} \times 3600 / 10^6}
$$
&lt;p>where \( C_{\text{GPU}} \) is the cost per GPU-hour (€/h), \( N_{\text{GPU}} \) the number of GPUs assigned to the service and \( T_{\text{ef}} \) the effective throughput (tok/s). Making the dependence on occupancy explicit:&lt;/p>
$$
T_{\text{ef}} = T_{\text{pico}} \times \rho
$$
&lt;p>where \( \rho \in [0,1] \) is the &lt;strong>useful occupancy rate&lt;/strong> (the fraction of time in which the GPU is processing real serving tokens). The resulting identity:&lt;/p>
$$
\text{CPM} = \frac{C_{\text{GPU}} \cdot N_{\text{GPU}}}{T_{\text{pico}} \times \rho \times 3600 / 10^6}
$$
&lt;p>The direct consequence: CPM is &lt;strong>inversely proportional to&lt;/strong> \( \rho \). Doubling \( \rho \) halves CPM. The numerator (cost of the iron) does not change.&lt;/p>
&lt;p>The posts &lt;a href="https://blog.lo0.es/en/posts/gpu-hour-to-cost-per-token-on-prem-vs-cloud/">coste-por-token-y-por-request&lt;/a> and &lt;a href="https://blog.lo0.es/en/posts/capacity-planning-on-premise-llm-inference/">capacity-planning-inferencia-llm-on-premise&lt;/a> cover how to calculate \( C_{\text{GPU}} \) and the reference peak throughput. This article deals with \( \rho \): how to measure it, what limits it and how to raise it.&lt;/p>
&lt;hr>
&lt;h2 id="2--why-the-standard-metric-lies-dcgm_fi_dev_gpu_util">2 · Why the standard metric lies: &lt;code>DCGM_FI_DEV_GPU_UTIL&lt;/code>&lt;/h2>
&lt;p>The field &lt;code>DCGM_FI_DEV_GPU_UTIL&lt;/code> (field ID 203) appears in &lt;code>nvidia-smi&lt;/code> as &amp;ldquo;GPU-Util&amp;rdquo;. Its official definition in the DCGM documentation:&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;GPU Utilization&amp;rdquo; — percentage of time during which one or more kernels were executing on the GPU.&lt;/p>
&lt;/blockquote>
&lt;p>The problem for LLM inference: &lt;strong>the decode phase is memory-bound&lt;/strong>. The GPU runs a kernel reading weights from HBM token by token; therefore &lt;code>GPU_UTIL&lt;/code> registers close to 100 % even though the tensor cores are at 15 % of their capacity. The field measures &lt;em>activity&lt;/em>, not &lt;em>useful work&lt;/em>.&lt;/p>
&lt;p>The distinction is critical for FinOps: an operator who sees &lt;code>GPU_UTIL 98 %&lt;/code> assumes &amp;ldquo;saturated GPU, no room for more load&amp;rdquo;. The reality may be &amp;ldquo;the tensor cores are at 20 % and the bottleneck is HBM&amp;rdquo;, which leaves room for continuous batching or additional bin-packing.&lt;/p>
&lt;p>The metrics that measure real occupancy are those of the &lt;code>_FI_PROF_*&lt;/code> subsystem, available in DCGM 3.x with the profiling module enabled:&lt;/p>
&lt;h3 id="table-of-relevant-dcgm-fields">Table of relevant DCGM fields&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>DCGM field&lt;/th>
&lt;th>Field ID&lt;/th>
&lt;th>What it measures&lt;/th>
&lt;th>Unit&lt;/th>
&lt;th>Operational note&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>DCGM_FI_DEV_GPU_UTIL&lt;/code>&lt;/td>
&lt;td>203&lt;/td>
&lt;td>% time with ≥1 active kernel&lt;/td>
&lt;td>%&lt;/td>
&lt;td>&lt;strong>Misleading&lt;/strong> in LLM decode&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DCGM_FI_PROF_SM_ACTIVE&lt;/code>&lt;/td>
&lt;td>1002&lt;/td>
&lt;td>Ratio of cycles with ≥1 active warp per SM&lt;/td>
&lt;td>0–1&lt;/td>
&lt;td>Compute activity, not occupancy&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DCGM_FI_PROF_SM_OCCUPANCY&lt;/code>&lt;/td>
&lt;td>1003&lt;/td>
&lt;td>Resident warps / theoretical maximum per SM&lt;/td>
&lt;td>0–1&lt;/td>
&lt;td>Real intra-SM parallelism&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DCGM_FI_PROF_PIPE_TENSOR_ACTIVE&lt;/code>&lt;/td>
&lt;td>1004&lt;/td>
&lt;td>% cycles with tensor cores (HMMA) active&lt;/td>
&lt;td>0–1&lt;/td>
&lt;td>The real compute efficiency metric&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DCGM_FI_PROF_DRAM_ACTIVE&lt;/code>&lt;/td>
&lt;td>1005&lt;/td>
&lt;td>% cycles with HBM transferring&lt;/td>
&lt;td>0–1&lt;/td>
&lt;td>Memory saturation&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DCGM_FI_PROF_PCIE_TX_BYTES&lt;/code>&lt;/td>
&lt;td>—&lt;/td>
&lt;td>TX bytes over PCIe&lt;/td>
&lt;td>bytes/s&lt;/td>
&lt;td>Useful in PCIe inference&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DCGM_FI_DEV_FB_USED&lt;/code>&lt;/td>
&lt;td>252&lt;/td>
&lt;td>HBM used&lt;/td>
&lt;td>MiB&lt;/td>
&lt;td>VRAM budget&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DCGM_FI_DEV_POWER_USAGE&lt;/code>&lt;/td>
&lt;td>—&lt;/td>
&lt;td>Real consumption&lt;/td>
&lt;td>W&lt;/td>
&lt;td>For real energy cost&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DCGM_FI_DEV_CLOCK_THROTTLE_REASONS&lt;/code>&lt;/td>
&lt;td>—&lt;/td>
&lt;td>Bitmap of throttle causes&lt;/td>
&lt;td>bitmap&lt;/td>
&lt;td>Detects silent degradation&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The &lt;code>_PROF_*&lt;/code> fields require the DCGM profiling module and the right driver permissions. They are documented exhaustively in the NVIDIA DCGM field ID reference and in the sibling post &lt;a href="https://blog.lo0.es/en/posts/gpu-observability-llm-inference-twelve-dcgm-vllm-metrics/">observabilidad-gpu-dcgm-llm&lt;/a>.&lt;/p>
&lt;h3 id="characteristic-reading-in-llm-decode-llama-70b-fp8-on-h100">Characteristic reading in LLM decode (Llama 70B FP8 on H100)&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Metric&lt;/th>
&lt;th>Typical decode value&lt;/th>
&lt;th>Interpretation&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>DCGM_FI_DEV_GPU_UTIL&lt;/code>&lt;/td>
&lt;td>95–99 %&lt;/td>
&lt;td>&lt;strong>A lie&lt;/strong>: there is an active kernel&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DCGM_FI_PROF_SM_ACTIVE&lt;/code>&lt;/td>
&lt;td>0.45–0.65&lt;/td>
&lt;td>SMs with warps 45–65 % of the time&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DCGM_FI_PROF_SM_OCCUPANCY&lt;/code>&lt;/td>
&lt;td>0.30–0.55&lt;/td>
&lt;td>Resident warps at 30–55 % of the maximum&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DCGM_FI_PROF_PIPE_TENSOR_ACTIVE&lt;/code>&lt;/td>
&lt;td>0.10–0.25&lt;/td>
&lt;td>Tensor cores active only 10–25 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DCGM_FI_PROF_DRAM_ACTIVE&lt;/code>&lt;/td>
&lt;td>0.75–0.90&lt;/td>
&lt;td>HBM busy 75–90 % of the time&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Reading: decode is &lt;strong>memory-bound&lt;/strong>. The model weights are loaded from HBM for each token; HBM is saturated but the tensor cores wait. All of the &amp;ldquo;utilisation&amp;rdquo; in &lt;code>nvidia-smi&lt;/code> comes from memory reads, not from compute.&lt;/p>
&lt;hr>
&lt;h2 id="3--mfu-and-hfu-occupancy-expressed-in-flops">3 · MFU and HFU: occupancy expressed in FLOPs&lt;/h2>
&lt;p>The canonical compute efficiency metric is &lt;strong>MFU (Model FLOPs Utilization)&lt;/strong>, defined in the PaLM paper (Chowdhery et al., arXiv 2204.02311, section 4):&lt;/p>
$$
\text{MFU} = \frac{T_{\text{obs}} \times C_{\text{modelo}}}{P_{\text{pico}}}
$$
&lt;p>where \( T_{\text{obs}} \) is the observed throughput (tok/s) and \( P_{\text{pico}} \) is the theoretical peak performance of the hardware (FLOP/s).&lt;/p>
&lt;p>where \( C_{\text{modelo}} \) is the number of FLOPs per token in a complete forward pass. For a dense transformer with \( P \) parameters, the usual approximation (forward plus backward = 6P FLOPs per token; forward only = 2P):&lt;/p>
$$
C_{\text{modelo}} \approx 2P \quad \text{(inference)}
$$
$$
C_{\text{modelo}} \approx 6P \quad \text{(training, forward + backward)}
$$
&lt;p>&lt;strong>HFU (Hardware FLOPs Utilization)&lt;/strong> measures the FLOPs &lt;strong>actually executed&lt;/strong> in hardware, including activation recomputation (gradient checkpointing). In training with recompute:&lt;/p>
$$
C_{\text{hardware}} \approx 8P \quad \text{(forward × 2 + backward × 4)}
$$
&lt;p>therefore HFU &amp;gt; MFU when there is recompute; they are identical without it.&lt;/p>
&lt;h3 id="typical-mfu-values">Typical MFU values&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Regime&lt;/th>
&lt;th>Hardware&lt;/th>
&lt;th>Typical MFU&lt;/th>
&lt;th>Limiting regime&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Training (large batch, BF16)&lt;/td>
&lt;td>H100 SXM&lt;/td>
&lt;td>35–50 %&lt;/td>
&lt;td>compute-bound&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Training PaLM 540B&lt;/td>
&lt;td>TPU v4&lt;/td>
&lt;td>46.2 %&lt;/td>
&lt;td>compute-bound&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Prefill inference (large batch)&lt;/td>
&lt;td>H100 SXM&lt;/td>
&lt;td>25–45 %&lt;/td>
&lt;td>compute-bound&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Decode inference (bs=1)&lt;/td>
&lt;td>H100 SXM&lt;/td>
&lt;td>3–8 %&lt;/td>
&lt;td>memory-bound&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Decode inference (continuous batching, bs=32–64)&lt;/td>
&lt;td>H100 SXM&lt;/td>
&lt;td>15–30 %&lt;/td>
&lt;td>memory-bound, attenuated&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Decode with batch size 1 has a single-digit MFU because the hardware spends most of its time waiting for HBM to deliver weights. Raising the batch size (continuous batching) amortises the weight read across more simultaneous tokens and raises MFU.&lt;/p>
&lt;h3 id="the-roofline-model">The Roofline model&lt;/h3>
&lt;p>The roofline places each operation in the space (arithmetic intensity, throughput):&lt;/p>
&lt;div class="diagram" style="max-width:680px;margin:1rem auto;">
&lt;svg viewBox="0 0 680 320" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Roofline model for LLM inference: compute-bound prefill vs memory-bound decode">
&lt;style>.ax{stroke:currentColor;stroke-width:1.4;fill:none}.lbl{font:11px sans-serif;fill:currentColor}.ttl{font:700 13px sans-serif;fill:currentColor}.roof{stroke:currentColor;stroke-width:2;fill:none}.pt{fill:currentColor}&lt;/style>
&lt;text x="340" y="22" text-anchor="middle" class="ttl">Roofline: prefill vs decode on H100 SXM&lt;/text>
&lt;line x1="60" y1="270" x2="640" y2="270" class="ax"/>
&lt;line x1="60" y1="270" x2="60" y2="30" class="ax"/>
&lt;text x="350" y="298" text-anchor="middle" class="lbl">Arithmetic intensity (FLOP/byte)&lt;/text>
&lt;text x="14" y="155" text-anchor="middle" class="lbl" transform="rotate(-90 14 155)">Throughput (TFLOP/s)&lt;/text>
&lt;line x1="60" y1="270" x2="220" y2="60" class="roof"/>
&lt;line x1="220" y1="60" x2="640" y2="60" class="roof"/>
&lt;text x="230" y="52" class="lbl">~990 TFLOP/s (H100 BF16)&lt;/text>
&lt;text x="65" y="200" class="lbl" fill="currentColor">mem. bandwidth&lt;/text>
&lt;text x="65" y="213" class="lbl" fill="currentColor">~3.35 TB/s&lt;/text>
&lt;line x1="215" y1="50" x2="215" y2="270" stroke="currentColor" stroke-width="1" stroke-dasharray="4 3"/>
&lt;text x="200" y="284" class="lbl">~295&lt;/text>
&lt;text x="148" y="284" class="lbl">FLOP/byte ridge&lt;/text>
&lt;circle cx="130" cy="175" r="7" class="pt"/>
&lt;text x="138" y="171" class="lbl">decode bs=1&lt;/text>
&lt;text x="138" y="184" class="lbl">MFU ~5 %&lt;/text>
&lt;circle cx="185" cy="120" r="7" class="pt"/>
&lt;text x="193" y="116" class="lbl">decode bs=32&lt;/text>
&lt;text x="193" y="129" class="lbl">MFU ~20 %&lt;/text>
&lt;circle cx="300" cy="65" r="7" class="pt"/>
&lt;text x="308" y="61" class="lbl">prefill bs=128&lt;/text>
&lt;text x="308" y="74" class="lbl">MFU ~38 %&lt;/text>
&lt;/svg>
&lt;/div>
&lt;p>Decode with batch size 1 falls in the memory-bound zone to the left of the ridge point. Raising the batch (continuous batching) moves the point to the right and up, closer to the roofline.&lt;/p>
&lt;hr>
&lt;h2 id="4--tco-sensitivity-to-occupancy">4 · TCO sensitivity to occupancy&lt;/h2>
&lt;p>Assumptions for the example: a generic node with &lt;strong>4×H100 SXM 80 GB&lt;/strong>, total node cost ~44 €/hour (5-year depreciation of ~220,000 €, energy ~4×700 W at ~0.12 €/kWh, plus rack/colocation infrastructure; see &lt;a href="https://blog.lo0.es/en/posts/gpu-hour-to-cost-per-token-on-prem-vs-cloud/">coste-por-token-y-por-request&lt;/a> for the detail of the identity). Reference peak throughput with Llama 70B FP8 plus continuous batching: ~3,500 tok/s aggregate.&lt;/p>
$$
\text{CPM}(\rho) = \frac{44\ \text{€/h}}{3500 \times \rho \times 3600 / 10^6}
= \frac{44 \times 10^6}{3500 \times \rho \times 3600}
= \frac{3.49}{\rho}\ \text{€/1M tok}
$$
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Occupancy \( \rho \)&lt;/th>
&lt;th>Effective throughput (tok/s)&lt;/th>
&lt;th>On-prem CPM&lt;/th>
&lt;th>On-demand cloud CPM (~2.2 €/GPU-h)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>20 %&lt;/td>
&lt;td>700&lt;/td>
&lt;td>&lt;strong>~17.5 €&lt;/strong>&lt;/td>
&lt;td>~6.3 €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>40 %&lt;/td>
&lt;td>1,400&lt;/td>
&lt;td>&lt;strong>~8.7 €&lt;/strong>&lt;/td>
&lt;td>~6.3 €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>60 %&lt;/td>
&lt;td>2,100&lt;/td>
&lt;td>&lt;strong>~5.8 €&lt;/strong>&lt;/td>
&lt;td>~6.3 €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>70 %&lt;/td>
&lt;td>2,450&lt;/td>
&lt;td>&lt;strong>~5.0 €&lt;/strong>&lt;/td>
&lt;td>~6.3 €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>85 %&lt;/td>
&lt;td>2,975&lt;/td>
&lt;td>&lt;strong>~4.1 €&lt;/strong>&lt;/td>
&lt;td>~6.3 €&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>On-prem / cloud crossover point&lt;/strong>: with this example hardware, the on-prem cost advantage over a comparable European cloud starts at \( \rho \approx 55\text{-}60\,\% \). Below that, idle makes on-prem dearer than renting. The cloud column is flat because cloud bills per hour used, not per the throughput you extract from it.&lt;/p>
&lt;blockquote>
&lt;p>Example figures with generic hardware. The real numbers depend on the depreciation price, the local cost of energy, the model and the precision. The structure of the curve, CPM inversely proportional to \( \rho \), is universal.&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="5--idle-metrics-where-occupancy-is-lost">5 · Idle metrics: where occupancy is lost&lt;/h2>
&lt;p>Before applying levers, you need to know which kind of idle dominates. Three categories:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Type of idle&lt;/th>
&lt;th>Symptom in metrics&lt;/th>
&lt;th>Usual cause&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Scheduling idle&lt;/strong>&lt;/td>
&lt;td>&lt;code>DCGM_FI_DEV_POWER_USAGE&lt;/code> low, &lt;code>SM_ACTIVE&lt;/code> &amp;lt; 0.05&lt;/td>
&lt;td>No requests in the queue; GPU waiting for work&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Batching idle&lt;/strong>&lt;/td>
&lt;td>&lt;code>SM_ACTIVE&lt;/code> high, &lt;code>PIPE_TENSOR_ACTIVE&lt;/code> low, &lt;code>DRAM_ACTIVE&lt;/code> low&lt;/td>
&lt;td>Batch too small; prefill stall between requests&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Memory-bound idle&lt;/strong>&lt;/td>
&lt;td>&lt;code>DRAM_ACTIVE&lt;/code> high, &lt;code>PIPE_TENSOR_ACTIVE&lt;/code> low&lt;/td>
&lt;td>Normal decode; HBM is the bottleneck; insufficient batch size&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Scheduling idle is the most expensive and the most directly attacked with bin-packing. Batching idle is attacked with continuous batching. Memory-bound idle in decode does not disappear entirely (it is the physics of the transformer), but it is attenuated with a larger batch size.&lt;/p>
&lt;p>The inference engine metrics that complement the diagnosis from the service side are documented in &lt;a href="https://blog.lo0.es/en/posts/gpu-observability-llm-inference-twelve-dcgm-vllm-metrics/">observabilidad-gpu-dcgm-llm&lt;/a> and &lt;a href="https://blog.lo0.es/en/posts/anatomy-dcgm-vllm-metrics-analogies-documented-anomalies/">anatomia-metricas-dcgm-vllm-anomalias&lt;/a>.&lt;/p>
&lt;hr>
&lt;h2 id="6--levers-for-raising-occupancy">6 · Levers for raising occupancy&lt;/h2>
&lt;h3 id="table-of-lever--effect--when-it-applies">Table of lever × effect × when it applies&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Lever&lt;/th>
&lt;th>Effect on \( \rho \)&lt;/th>
&lt;th>When it applies&lt;/th>
&lt;th>Operational complexity&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Continuous batching&lt;/strong>&lt;/td>
&lt;td>High: removes idle between requests; raises decode MFU from ~5 % to ~20 %&lt;/td>
&lt;td>Always in inference; enabled by default in vLLM&lt;/td>
&lt;td>Low (engine parameter)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Scheduler bin-packing&lt;/strong> (kube-scheduler / Kueue)&lt;/td>
&lt;td>High: concentrates loads on fewer nodes; frees whole nodes for shutdown or rebalancing&lt;/td>
&lt;td>Clusters with load that varies over the day&lt;/td>
&lt;td>Medium (scheduler policy)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>MIG&lt;/strong> (Multi-Instance GPU)&lt;/td>
&lt;td>Medium: fills GPUs with light loads that previously lived alone on a whole GPU&lt;/td>
&lt;td>Heterogeneous loads: embeddings + reranker + guardrail + large model&lt;/td>
&lt;td>High (hot repartitioning not available)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Time-slicing&lt;/strong>&lt;/td>
&lt;td>Low to medium: raises occupancy in dev/bursts; no isolation&lt;/td>
&lt;td>Consumer GPUs (RTX 5090/4090); low-risk multi-tenant dev&lt;/td>
&lt;td>Low&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>MPS&lt;/strong>&lt;/td>
&lt;td>Medium: concurrent execution of multiple small processes; reduces context-switch overhead&lt;/td>
&lt;td>Many small concurrent kernels on a datacenter GPU; trust between loads&lt;/td>
&lt;td>Medium&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Chunked prefill&lt;/strong>&lt;/td>
&lt;td>Medium: interleaves prefill and decode; reduces the TTFT spike and raises throughput&lt;/td>
&lt;td>Loads with a mix of short and long prompts&lt;/td>
&lt;td>Low (vLLM flag)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Quantisation&lt;/strong> (FP16→FP8→INT4)&lt;/td>
&lt;td>Indirect: raises peak throughput, which lowers CPM for the same \( \rho \)&lt;/td>
&lt;td>Models with quantised kernel support (native Hopper FP8)&lt;/td>
&lt;td>Medium&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Autoscaling&lt;/strong> (KEDA)&lt;/td>
&lt;td>Keeps \( \rho \) high by scaling replicas according to the queue&lt;/td>
&lt;td>Variable and predictable load; cluster with spare capacity&lt;/td>
&lt;td>Medium&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The post &lt;a href="https://blog.lo0.es/en/posts/sharing-one-gpu-time-slicing-mps-mig/">compartir-gpu-time-slicing-mps-mig&lt;/a> details the three sharing mechanisms (time-slicing, MPS, MIG) with VRAM budgets.&lt;/p>
&lt;h3 id="bin-packing-with-kueue">Bin-packing with Kueue&lt;/h3>
&lt;p>Kueue (sigs.k8s.io/kueue) is the Kubernetes-native queue manager for GPU jobs. Its model of &lt;strong>cohorts&lt;/strong> and &lt;strong>nominal quotas&lt;/strong> allows active bin-packing: jobs accumulate in a queue and are launched only when there is a node that can take them whole, instead of fragmenting the load across partially occupied nodes.&lt;/p>
&lt;p>&lt;code>BestFit&lt;/code> packing in the &lt;code>ClusterQueue&lt;/code> is configured with:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">kueue.x-k8s.io/v1beta1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ClusterQueue&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">gpu-prod&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">preemption&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">reclaimWithinCohort&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Any&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">withinClusterQueue&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">LowerPriority&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">resourceGroups&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">coveredResources&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;nvidia.com/gpu&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">flavors&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">h100-sxm&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">resources&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;nvidia.com/gpu&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">nominalQuota&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">16&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The priority-based preemption policy ensures that production jobs displace experimentation ones when there is scarcity, keeping occupancy on the production nodes high.&lt;/p>
&lt;h3 id="mig-as-a-bin-packing-lever-inside-the-gpu">MIG as a bin-packing lever inside the GPU&lt;/h3>
&lt;p>MIG makes it possible to fill an H100 with light loads that would otherwise live alone on a whole GPU. A profile of &lt;code>3×2g.20gb + 1×1g.10gb&lt;/code> on an H100 can host simultaneously a 7B FP8 model (~14 GB of weights), two embedding services and an INT4 guardrail, all with hardware isolation. Without MIG, each of those loads would take a whole GPU with an individual \( \rho \) below 10 %.&lt;/p>
&lt;p>The profiles available on the H100 80 GB (SXM5) according to NVIDIA&amp;rsquo;s MIG User Guide:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>MIG profile&lt;/th>
&lt;th>Compute slices&lt;/th>
&lt;th>Memory&lt;/th>
&lt;th>Max. instances&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>1g.10gb&lt;/code>&lt;/td>
&lt;td>1/7 SMs&lt;/td>
&lt;td>10 GB&lt;/td>
&lt;td>7&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>1g.20gb&lt;/code>&lt;/td>
&lt;td>1/7 SMs&lt;/td>
&lt;td>20 GB&lt;/td>
&lt;td>4&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>2g.20gb&lt;/code>&lt;/td>
&lt;td>2/7 SMs&lt;/td>
&lt;td>20 GB&lt;/td>
&lt;td>3&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>3g.40gb&lt;/code>&lt;/td>
&lt;td>3/7 SMs&lt;/td>
&lt;td>40 GB&lt;/td>
&lt;td>2&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>4g.40gb&lt;/code>&lt;/td>
&lt;td>4/7 SMs&lt;/td>
&lt;td>40 GB&lt;/td>
&lt;td>1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>7g.80gb&lt;/code>&lt;/td>
&lt;td>7/7 SMs&lt;/td>
&lt;td>80 GB&lt;/td>
&lt;td>1 (whole GPU)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>MIG is not available on consumer GPUs (RTX 5090, RTX 4090). Time-slicing is the only sharing option on that hardware.&lt;/p>
&lt;hr>
&lt;h2 id="7--continuous-batching-the-effect-on-mfu">7 · Continuous batching: the effect on MFU&lt;/h2>
&lt;p>Continuous batching (also called iteration-level scheduling or in-flight batching) is the mechanism with the greatest impact on occupancy in inference. The idea: instead of waiting for a complete batch to finish before launching the next, the engine evaluates the pipeline after each token and replaces completed sequences with new requests from the waiting queue.&lt;/p>
&lt;p>Quantified effect in vLLM:&lt;/p>
&lt;ul>
&lt;li>Without batching (bs=1, static): decode MFU ~3–8 %; GPU idle between requests.&lt;/li>
&lt;li>With continuous batching (dynamic bs 16–64): decode MFU ~15–30 %; the GPU almost never waits.&lt;/li>
&lt;li>On pure prefill loads with a large batch (bs=128+): prefill MFU 25–45 %; it approaches the compute roofline.&lt;/li>
&lt;/ul>
&lt;p>The vLLM parameter &lt;code>--max-num-seqs&lt;/code> controls the maximum number of sequences in the concurrent batch. Raising it increases occupancy until HBM becomes the bottleneck (watch for &lt;code>DCGM_FI_PROF_DRAM_ACTIVE&lt;/code> &amp;gt; 90 % sustained).&lt;/p>
&lt;hr>
&lt;h2 id="8--hardware-and-scale-what-applies-to-what">8 · Hardware and scale: what applies to what&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Hardware&lt;/th>
&lt;th>MIG&lt;/th>
&lt;th>MPS&lt;/th>
&lt;th>Time-slicing&lt;/th>
&lt;th>Continuous batching&lt;/th>
&lt;th>Note&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>H100 SXM / H200&lt;/td>
&lt;td>Yes (7 inst.)&lt;/td>
&lt;td>Yes&lt;/td>
&lt;td>Yes&lt;/td>
&lt;td>Yes&lt;/td>
&lt;td>On-prem datacenter reference&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>A100 SXM/PCIe&lt;/td>
&lt;td>Yes (7 inst.)&lt;/td>
&lt;td>Yes&lt;/td>
&lt;td>Yes&lt;/td>
&lt;td>Yes&lt;/td>
&lt;td>Previous generation; HBM2e&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>L40S / L40&lt;/td>
&lt;td>No&lt;/td>
&lt;td>Yes&lt;/td>
&lt;td>Yes&lt;/td>
&lt;td>Yes&lt;/td>
&lt;td>Ada Lovelace; no MIG; good price/VRAM&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RTX 5090&lt;/td>
&lt;td>No&lt;/td>
&lt;td>Yes (limited)&lt;/td>
&lt;td>Yes&lt;/td>
&lt;td>Yes&lt;/td>
&lt;td>Consumer; no MIG; does not scale in production&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RTX 4090&lt;/td>
&lt;td>No&lt;/td>
&lt;td>Yes (limited)&lt;/td>
&lt;td>Yes&lt;/td>
&lt;td>Yes&lt;/td>
&lt;td>Consumer; 24 GB VRAM; no MIG&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The RTX 5090 and RTX 4090 illustrate the case of &lt;strong>hardware that does not scale&lt;/strong> for multi-tenant with isolation: they do not support MIG, VRAM is scarce for models above 7B with a wide KV-cache, and TDP (600 W / 450 W) is high relative to throughput. For production inference at scale, the 4×H100 SXM is the reference node of this series.&lt;/p>
&lt;hr>
&lt;h2 id="9--finops-diagnostic-flow-from-high-cpm-to-the-cause">9 · FinOps diagnostic flow: from high CPM to the cause&lt;/h2>
&lt;div class="diagram" style="max-width:720px;margin:1rem auto;">
&lt;svg viewBox="0 0 720 380" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="FinOps diagnostic flow from high CPM to the right lever">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.3}.dec{fill:none;stroke:currentColor;stroke-width:1.3}.tl{font:600 11px sans-serif;fill:currentColor}.ts{font:10px sans-serif;fill:currentColor}.ar{fill:none;stroke:currentColor;stroke-width:1.2;marker-end:url(#fm)}&lt;/style>
&lt;defs>&lt;marker id="fm" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="currentColor"/>&lt;/marker>&lt;/defs>
&lt;rect class="bx" x="270" y="10" width="180" height="42" rx="6"/>
&lt;text x="360" y="28" text-anchor="middle" class="tl">High CPM detected&lt;/text>
&lt;text x="360" y="44" text-anchor="middle" class="ts">OpenCost + LiteLLM&lt;/text>
&lt;path class="ar" d="M360,52 L360,80"/>
&lt;polygon class="dec" points="360,80 440,110 360,140 280,110"/>
&lt;text x="360" y="105" text-anchor="middle" class="tl">POWER_USAGE&lt;/text>
&lt;text x="360" y="120" text-anchor="middle" class="ts">low?&lt;/text>
&lt;path class="ar" d="M440,110 L530,110"/>
&lt;text x="537" y="114" class="ts">Yes → scheduling idle&lt;/text>
&lt;rect class="bx" x="530" y="128" width="170" height="34" rx="5"/>
&lt;text x="615" y="142" text-anchor="middle" class="ts">Bin-packing / Kueue&lt;/text>
&lt;text x="615" y="155" text-anchor="middle" class="ts">Autoscaling down&lt;/text>
&lt;path class="ar" d="M360,140 L360,168"/>
&lt;text x="270" y="114" class="ts">No&lt;/text>
&lt;polygon class="dec" points="360,168 450,198 360,228 270,198"/>
&lt;text x="360" y="193" text-anchor="middle" class="tl">PIPE_TENSOR_ACTIVE&lt;/text>
&lt;text x="360" y="208" text-anchor="middle" class="ts">low (&amp;lt;0.10)?&lt;/text>
&lt;path class="ar" d="M450,198 L530,198"/>
&lt;text x="537" y="202" class="ts">Yes → batching idle&lt;/text>
&lt;rect class="bx" x="530" y="216" width="170" height="34" rx="5"/>
&lt;text x="615" y="230" text-anchor="middle" class="ts">Continuous batching&lt;/text>
&lt;text x="615" y="243" text-anchor="middle" class="ts">Raise --max-num-seqs&lt;/text>
&lt;path class="ar" d="M360,228 L360,256"/>
&lt;text x="270" y="202" class="ts">No&lt;/text>
&lt;polygon class="dec" points="360,256 450,286 360,316 270,286"/>
&lt;text x="360" y="280" text-anchor="middle" class="tl">DRAM_ACTIVE high&lt;/text>
&lt;text x="360" y="296" text-anchor="middle" class="ts">and batch already large?&lt;/text>
&lt;path class="ar" d="M450,286 L530,286"/>
&lt;text x="537" y="290" class="ts">Yes → memory-bound&lt;/text>
&lt;rect class="bx" x="530" y="304" width="170" height="34" rx="5"/>
&lt;text x="615" y="318" text-anchor="middle" class="ts">FP8/INT4 quantisation&lt;/text>
&lt;text x="615" y="331" text-anchor="middle" class="ts">Smaller model / TP&lt;/text>
&lt;path class="ar" d="M360,316 L360,344"/>
&lt;text x="270" y="290" class="ts">No&lt;/text>
&lt;rect class="bx" x="270" y="344" width="180" height="30" rx="5"/>
&lt;text x="360" y="364" text-anchor="middle" class="ts">MIG / MPS / multi-model bin-pack&lt;/text>
&lt;/svg>
&lt;/div>
&lt;hr>
&lt;h2 id="10--worked-example-impact-of-continuous-batching-on-cpm">10 · Worked example: impact of continuous batching on CPM&lt;/h2>
&lt;p>Starting point: Llama 70B FP8 on 4×H100 SXM, a load of 8 requests/s with 512 output tokens on average, without continuous batching (static bs = 8):&lt;/p>
&lt;ul>
&lt;li>Observed throughput: ~900 tok/s (decode dominant).&lt;/li>
&lt;li>\( \rho_{\text{efectiva}} \approx 900 / 3500 \approx 0.26 \).&lt;/li>
&lt;li>CPM: \( 44 / (900 \times 3600 / 10^6) \approx 13.6\ \text{€} \).&lt;/li>
&lt;/ul>
&lt;p>Same load, enabling continuous batching with &lt;code>--max-num-seqs 64&lt;/code>:&lt;/p>
&lt;ul>
&lt;li>Observed throughput: ~2,300 tok/s (the dynamic batch fills the gaps).&lt;/li>
&lt;li>\( \rho_{\text{efectiva}} \approx 2300 / 3500 \approx 0.66 \).&lt;/li>
&lt;li>CPM: \( 44 / (2300 \times 3600 / 10^6) \approx 5.3\ \text{€} \).&lt;/li>
&lt;/ul>
&lt;p>CPM reduction: from 13.6 € to 5.3 €, &lt;strong>–61 %&lt;/strong>, with no hardware change and without touching the model.&lt;/p>
&lt;p>The vLLM parameters relevant for raising effective throughput:&lt;/p>
&lt;pre tabindex="0">&lt;code>--max-num-seqs 64 # maximum concurrent batch
--max-num-batched-tokens 16384 # total tokens per iteration
--enable-chunked-prefill # interleaves prefill and decode
&lt;/code>&lt;/pre>&lt;hr>
&lt;h2 id="see-also">See also&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/gpu-hour-to-cost-per-token-on-prem-vs-cloud/">coste-por-token-y-por-request&lt;/a> — the identity CPM = €/GPU-hour ÷ throughput with data from LiteLLM and OpenCost.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/gpu-observability-llm-inference-twelve-dcgm-vllm-metrics/">observabilidad-gpu-dcgm-llm&lt;/a> — the twelve DCGM metrics and five vLLM ones that make up the cockpit.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/anatomy-dcgm-vllm-metrics-analogies-documented-anomalies/">anatomia-metricas-dcgm-vllm-anomalias&lt;/a> — a deep dive into the DCGM metrics with anomalies documented in production.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/sharing-one-gpu-time-slicing-mps-mig/">compartir-gpu-time-slicing-mps-mig&lt;/a> — time-slicing, MPS and MIG with worked VRAM budgets.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/capacity-planning-on-premise-llm-inference/">capacity-planning-inferencia-llm-on-premise&lt;/a> — sizing the cluster from peak throughput and target occupancy.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/opencost-gpu-cost-allocation-kubernetes/">opencost-cost-allocation-kubernetes&lt;/a> — how OpenCost calculates the \( C_{\text{GPU}} \) that goes into the CPM identity.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/cloud-gpu-on-demand-reserved-spot-neoclouds-2026-prices/">Cloud GPU: a comparison of prices, commitment and sovereign neoclouds&lt;/a> — the alternative €/GPU-hour when on-premise occupancy does not justify the CAPEX: spot and reserved prices from European neoclouds.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/full-tco-on-premise-gpu-cluster-capex-gpu-hour-break-even/">TCO of an on-premise GPU cluster: depreciation, energy and infrastructure&lt;/a> — where the \( C_{\text{GPU}} \) of the identity comes from when the iron is your own: CAPEX, depreciation, energy and operations.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="sources">Sources&lt;/h2>
&lt;ul>
&lt;li>NVIDIA — &lt;em>DCGM Field Identifiers reference (v3.1)&lt;/em>, lista completa de &lt;code>DCGM_FI_*&lt;/code> con field IDs y definiciones. &lt;a href="https://docs.nvidia.com/datacenter/dcgm/3.1/dcgm-api/dcgm-api-field-ids.html">https://docs.nvidia.com/datacenter/dcgm/3.1/dcgm-api/dcgm-api-field-ids.html&lt;/a>&lt;/li>
&lt;li>NVIDIA — &lt;em>GPU Profiling Metrics (Run:ai / DCGM)&lt;/em>, definiciones de &lt;code>DCGM_FI_PROF_SM_ACTIVE&lt;/code> (1002), &lt;code>SM_OCCUPANCY&lt;/code> (1003), &lt;code>PIPE_TENSOR_ACTIVE&lt;/code> (1004). &lt;a href="https://run-ai-docs.nvidia.com/self-hosted/platform-management/monitor-performance/gpu-profiling-metrics">https://run-ai-docs.nvidia.com/self-hosted/platform-management/monitor-performance/gpu-profiling-metrics&lt;/a>&lt;/li>
&lt;li>Chowdhery et al. — &lt;em>PaLM: Scaling Language Modeling with Pathways&lt;/em> (MFU definition, sección 4). arXiv 2204.02311. &lt;a href="https://arxiv.org/abs/2204.02311">https://arxiv.org/abs/2204.02311&lt;/a>&lt;/li>
&lt;li>NVIDIA — &lt;em>Multi-Instance GPU (MIG) User Guide&lt;/em> (perfiles H100 SXM5, particionado, aislamiento). &lt;a href="https://docs.nvidia.com/datacenter/tesla/mig-user-guide/">https://docs.nvidia.com/datacenter/tesla/mig-user-guide/&lt;/a>&lt;/li>
&lt;li>NVIDIA — &lt;em>Supported MIG Profiles&lt;/em> (catálogo completo H100 80 GB). &lt;a href="https://docs.nvidia.com/datacenter/tesla/mig-user-guide/supported-mig-profiles.html">https://docs.nvidia.com/datacenter/tesla/mig-user-guide/supported-mig-profiles.html&lt;/a>&lt;/li>
&lt;li>NVIDIA — &lt;em>Time-Slicing GPUs in Kubernetes&lt;/em> (GPU Operator 24.9.0). &lt;a href="https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/24.9.0/gpu-sharing.html">https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/24.9.0/gpu-sharing.html&lt;/a>&lt;/li>
&lt;li>NVIDIA — &lt;em>Multi-Process Service (MPS) Overview&lt;/em> (kernel concurrency, SM allocation). &lt;a href="https://docs.nvidia.com/deploy/mps/latest/index.html">https://docs.nvidia.com/deploy/mps/latest/index.html&lt;/a>&lt;/li>
&lt;li>Kueue — &lt;em>Overview&lt;/em> (sigs.k8s.io/kueue, bin-packing, cohorts, quotas). &lt;a href="https://kueue.sigs.k8s.io/docs/overview/">https://kueue.sigs.k8s.io/docs/overview/&lt;/a>&lt;/li>
&lt;li>vLLM Blog — &lt;em>vLLM v0.6.0: 2.7x Throughput Improvement and 5x Latency Reduction&lt;/em> (continuous batching, chunked prefill). &lt;a href="https://blog.vllm.ai/2024/09/05/perf-update.html">https://blog.vllm.ai/2024/09/05/perf-update.html&lt;/a>&lt;/li>
&lt;li>GMI Cloud — &lt;em>NVIDIA H100 GPU Pricing: 2026 Rent vs. Buy Cost Analysis&lt;/em>. &lt;a href="https://www.gmicloud.ai/en/blog/nvidia-h100-gpu-pricing-2026-rent-vs-buy-cost-analysis">https://www.gmicloud.ai/en/blog/nvidia-h100-gpu-pricing-2026-rent-vs-buy-cost-analysis&lt;/a>&lt;/li>
&lt;li>Saxena et al. — &lt;em>LLM Inference Unveiled: Survey and Roofline Model Insights&lt;/em>. arXiv 2402.16363. &lt;a href="https://arxiv.org/abs/2402.16363">https://arxiv.org/abs/2402.16363&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>GPU chargeback and showback in multi-tenancy: how to split cluster cost between teams</title><link>https://blog.lo0.es/en/posts/gpu-chargeback-showback-multi-tenancy/</link><pubDate>Mon, 15 Jun 2026 04:00:00 +0200</pubDate><guid>https://blog.lo0.es/en/posts/gpu-chargeback-showback-multi-tenancy/</guid><description>&lt;blockquote>
&lt;p>Notation: amounts in &lt;strong>euros (N €)&lt;/strong>, decimals with a point. The dollar sign is not used
(on this site it is a formula delimiter).&lt;/p>
&lt;/blockquote>
&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>A cluster of 4×H100 SXM at ~1.40 €/GPU-hour (depreciated capex plus energy) shared between three
teams with different occupancy produces a monthly chargeback report of 3 rows and an additional
idle row that nobody claimed. Without tooling, that idle cost vanishes, diluted into the total.
With OpenCost + LiteLLM + Kueue, attribution operates on three orthogonal planes: the &lt;strong>iron&lt;/strong>
(OpenCost, €/GPU-hour per namespace/label), &lt;strong>token consumption&lt;/strong> (LiteLLM, €/token per
key/team/model) and the &lt;strong>scheduler quota&lt;/strong> (Kueue, GPUs reserved per ClusterQueue). Crossing the
three produces the number that goes to finance: team B consumed X million tokens at Y €/1M tok,
the GPU cost it Z €, and it still has 2 GPUs borrowed from the cohort that will cost it W € if it
holds on to them next month.&lt;/p>
&lt;hr>
&lt;h2 id="showback-vs-chargeback-the-finops-foundation-definition">Showback vs chargeback: the FinOps Foundation definition&lt;/h2>
&lt;p>The distinction is not one of technology but of &lt;strong>accounting formality&lt;/strong>
(&lt;a href="https://www.finops.org/framework/capabilities/invoicing-chargeback/">FinOps Foundation — Invoicing &amp;amp; Chargeback&lt;/a>,
&lt;a href="https://www.finops.org/framework/previous-capabilities/analysis-showback/">FinOps Foundation — Data Analysis and Showback&lt;/a>):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Concept&lt;/th>
&lt;th>Definition&lt;/th>
&lt;th>Moves money&lt;/th>
&lt;th>Requires&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Showback&lt;/strong>&lt;/td>
&lt;td>visibility of consumption and its cost per team; the report arrives, the budget does not change&lt;/td>
&lt;td>No&lt;/td>
&lt;td>metrics + attribution&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Chargeback&lt;/strong>&lt;/td>
&lt;td>the cost is transferred to the team&amp;rsquo;s or product&amp;rsquo;s P&amp;amp;L as real expenditure&lt;/td>
&lt;td>Yes&lt;/td>
&lt;td>showback + accounting policy + financial system&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Two points from the framework worth pinning down:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Showback is a requirement of any FinOps practice; chargeback is optional&lt;/strong> and depends on
the organisation&amp;rsquo;s accounting policy supporting transfers between cost centres.&lt;/li>
&lt;li>&lt;strong>Neither is &amp;ldquo;more mature&amp;rdquo; than the other.&lt;/strong> The narrative that chargeback is the &amp;ldquo;grown-up&amp;rdquo;
version is false according to the framework itself. The natural sequence is: showback → trust
in the data → chargeback if policy allows it.&lt;/li>
&lt;/ol>
&lt;h3 id="when-to-use-each">When to use each&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Situation&lt;/th>
&lt;th>Recommended mode&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>First attribution cycle; teams do not trust the data yet&lt;/td>
&lt;td>Showback&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Teams with their own budget in a financial system&lt;/td>
&lt;td>Chargeback&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Shared cluster with no P&amp;amp;L separation per team&lt;/td>
&lt;td>Showback with visible idle cost&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Teams with a committed GPU availability SLA&lt;/td>
&lt;td>Chargeback (real reservation)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Research/experimentation workloads with no formal budget&lt;/td>
&lt;td>Showback + threshold alert&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="gpu-cost-attribution-with-opencost">GPU cost attribution with OpenCost&lt;/h2>
&lt;h3 id="allocation-api-attribution-parameters">Allocation API: attribution parameters&lt;/h3>
&lt;p>The OpenCost &lt;code>/allocation&lt;/code> API is the piece that turns Prometheus metrics into a cost report per
dimension (&lt;a href="https://opencost.io/docs/integrations/api/">OpenCost — API&lt;/a>):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Parameter&lt;/th>
&lt;th>Values&lt;/th>
&lt;th>Use in multi-tenancy&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>window&lt;/code>&lt;/td>
&lt;td>&lt;code>today&lt;/code>, &lt;code>7d&lt;/code>, &lt;code>lastmonth&lt;/code>, RFC3339 range&lt;/td>
&lt;td>window of the monthly report&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>aggregate&lt;/code>&lt;/td>
&lt;td>&lt;code>namespace&lt;/code>, &lt;code>label:LABEL&lt;/code>, &lt;code>annotation:NAME&lt;/code>, &lt;code>pod&lt;/code>, &lt;code>controller&lt;/code>&lt;/td>
&lt;td>attribution dimension&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>includeIdle&lt;/code>&lt;/td>
&lt;td>&lt;code>true&lt;/code> / &lt;code>false&lt;/code>&lt;/td>
&lt;td>adds an &lt;code>__idle__&lt;/code> row to the report&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>shareIdle&lt;/code>&lt;/td>
&lt;td>&lt;code>true&lt;/code> / &lt;code>false&lt;/code>&lt;/td>
&lt;td>distributes idle among the non-idle allocations (proportional to non-idle cost)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>idleByNode&lt;/code>&lt;/td>
&lt;td>&lt;code>true&lt;/code> / &lt;code>false&lt;/code>&lt;/td>
&lt;td>computes idle per node instead of per cluster&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>filter&lt;/code>&lt;/td>
&lt;td>&lt;code>namespace:&amp;quot;llm-prod&amp;quot;&lt;/code>, &lt;code>label:equipo:&amp;quot;datos&amp;quot;&lt;/code>&lt;/td>
&lt;td>restrict to one specific team&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Query for a monthly report per team, with idle visible as a separate row:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">curl -G http://localhost:9003/allocation &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -d &lt;span class="nv">window&lt;/span>&lt;span class="o">=&lt;/span>lastmonth &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -d &lt;span class="nv">aggregate&lt;/span>&lt;span class="o">=&lt;/span>label:equipo &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -d &lt;span class="nv">includeIdle&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">true&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -d &lt;span class="nv">shareIdle&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">false&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -d &lt;span class="nv">resolution&lt;/span>&lt;span class="o">=&lt;/span>10m
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>With &lt;code>shareIdle=false&lt;/code> (the default), OpenCost returns idle as a separate &lt;code>__idle__&lt;/code> entry, which
makes visible how much was paid for unused capacity. With &lt;code>shareIdle=true&lt;/code>, that idle is
distributed among the tenants in proportion to their non-idle cost: each absorbs its share of the
waste, which is the right behaviour for a chargeback that penalises whoever is responsible for
the idle.&lt;/p>
&lt;h3 id="labelling-inference-pods">Labelling inference pods&lt;/h3>
&lt;p>For &lt;code>aggregate=label:equipo&lt;/code> to work, the inference pods need the right label. Two methods:&lt;/p>
&lt;p>&lt;strong>Label in the pod spec&lt;/strong> (the most direct):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># vLLM Deployment for the datos team&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">equipo&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">datos&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">producto&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">rag-prod&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">modelo&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">llama-70b&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">entorno&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">prod&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Annotation&lt;/strong> (when the label is already taken by another convention):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># aggregate as an annotation&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">aggregate&lt;/span>&lt;span class="o">=&lt;/span>annotation:finops.io/equipo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>OpenCost supports &lt;code>aggregate=label:KEY&lt;/code> and &lt;code>aggregate=annotation:KEY&lt;/code> with the same syntax; the
choice between the two depends on the cluster&amp;rsquo;s labelling convention.&lt;/p>
&lt;h3 id="shared-costs-and-idle-the-three-modes">Shared costs and idle: the three modes&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>API mode&lt;/th>
&lt;th>Behaviour&lt;/th>
&lt;th>When&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>includeIdle=false&lt;/code> (default)&lt;/td>
&lt;td>Ignores idle; total cost looks lower than it is&lt;/td>
&lt;td>Never in production&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>includeIdle=true, shareIdle=false&lt;/code>&lt;/td>
&lt;td>Idle in a separate &lt;code>__idle__&lt;/code> row&lt;/td>
&lt;td>Showback: visibility of waste&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>includeIdle=true, shareIdle=true&lt;/code>&lt;/td>
&lt;td>Idle distributed among tenants proportionally&lt;/td>
&lt;td>Chargeback: the team pays its share of idle&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>shareIdle=true, idleByNode=true&lt;/code>&lt;/td>
&lt;td>Idle distributed per node (finer-grained if there are dedicated nodes)&lt;/td>
&lt;td>Chargeback with heterogeneous nodes&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>For infrastructure with nodes dedicated per team, &lt;code>idleByNode=true&lt;/code> is fairer: the idle of a node
dedicated to team A is not transferred to team B.&lt;/p>
&lt;hr>
&lt;h2 id="litellm-as-the-attribution-point-for-token-consumption">LiteLLM as the attribution point for token consumption&lt;/h2>
&lt;h3 id="virtual-keys-and-teams">Virtual keys and teams&lt;/h3>
&lt;p>LiteLLM materialises token chargeback with four mechanisms
(&lt;a href="https://docs.litellm.ai/docs/proxy/virtual_keys">LiteLLM — Virtual Keys&lt;/a>,
&lt;a href="https://docs.litellm.ai/docs/proxy/team_budgets">LiteLLM — Setting Team Budgets&lt;/a>,
&lt;a href="https://docs.litellm.ai/docs/proxy/cost_tracking">LiteLLM — Spend Tracking&lt;/a>):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Mechanism&lt;/th>
&lt;th>What it does&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Virtual key&lt;/strong> with &lt;code>max_budget&lt;/code>&lt;/td>
&lt;td>monthly budget per key; the key is blocked once it is exhausted&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>&lt;code>budget_duration&lt;/code>&lt;/strong>&lt;/td>
&lt;td>reset window: &lt;code>30d&lt;/code>, &lt;code>7d&lt;/code>, &lt;code>24h&lt;/code>; the proxy runs a daily cron that resets according to the duration&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>&lt;code>team_id&lt;/code>&lt;/strong>&lt;/td>
&lt;td>groups keys; spend accumulates in &lt;code>LiteLLM_TeamTable&lt;/code> per team&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>&lt;code>tags&lt;/code>&lt;/strong>&lt;/td>
&lt;td>tag each request; they allow budgets per tag (cost centre)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Spend logs&lt;/strong>&lt;/td>
&lt;td>one row per request with &lt;code>team_id&lt;/code>, &lt;code>model&lt;/code>, &lt;code>prompt_tokens&lt;/code>, &lt;code>completion_tokens&lt;/code>, &lt;code>response_cost&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="configuring-teams-with-a-monthly-budget">Configuring teams with a monthly budget&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># litellm-config.yaml — per-team budgets with the declared on-prem cost&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">model_list&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">model_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">llama-3-70b-onprem&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">litellm_params&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">model&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">openai/llama-3-70b&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">api_base&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">http://vllm-svc:8000/v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">input_cost_per_token&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0.00000140&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 1.40 €/1M tok (€/GPU-hour ÷ throughput)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">output_cost_per_token&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0.00000140&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">model_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">llama-3-8b-onprem&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">litellm_params&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">model&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">openai/llama-3-8b&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">api_base&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">http://vllm-8b-svc:8000/v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">input_cost_per_token&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0.00000035&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 0.35 €/1M tok (1 GPU, higher throughput)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">output_cost_per_token&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0.00000035&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">litellm_settings&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">success_callback&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;langfuse&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># or any external logger&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># Teams (via the /team/new API or config)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># POST /team/new&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># {&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># &amp;#34;team_alias&amp;#34;: &amp;#34;equipo-datos&amp;#34;,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># &amp;#34;max_budget&amp;#34;: 800, # 800 € per month&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># &amp;#34;budget_duration&amp;#34;: &amp;#34;30d&amp;#34;,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># &amp;#34;tpm_limit&amp;#34;: 500000, # max tokens/min&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># &amp;#34;rpm_limit&amp;#34;: 1000 # max requests/min&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># }&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>input_cost_per_token&lt;/code> key comes from the identity in &lt;a href="https://blog.lo0.es/en/posts/gpu-hour-to-cost-per-token-on-prem-vs-cloud/">article A4&lt;/a>:
the €/GPU-hour from OpenCost divided by the benchmark throughput. With that value, the
&lt;code>response_cost&lt;/code> of each request reflects the real cost of the on-prem iron for that model.&lt;/p>
&lt;h3 id="endpoints-for-querying-spend-per-team">Endpoints for querying spend per team&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Team spend accumulated over the period&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">GET /team/info?team_id&lt;span class="o">=&lt;/span>equipo-datos
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Team spend per model (aggregated spend_logs table)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">GET /global/spend/logs?team_id&lt;span class="o">=&lt;/span>equipo-datos&lt;span class="p">&amp;amp;&lt;/span>&lt;span class="nv">start_date&lt;/span>&lt;span class="o">=&lt;/span>2026-06-01&lt;span class="p">&amp;amp;&lt;/span>&lt;span class="nv">end_date&lt;/span>&lt;span class="o">=&lt;/span>2026-06-30
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Total spend across all teams&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">GET /global/spend/teams
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The response includes &lt;code>spend&lt;/code> (accumulated cost in the configured currency), &lt;code>total_tokens&lt;/code>,
&lt;code>prompt_tokens&lt;/code>, &lt;code>completion_tokens&lt;/code> and a per-model breakdown, the direct fields for the monthly
chargeback report.&lt;/p>
&lt;h3 id="joining-gpu-hour-iron-with-tokens-per-team">Joining €/GPU-hour (iron) with tokens per team&lt;/h3>
&lt;p>The two attribution planes, OpenCost (iron) and LiteLLM (tokens), are crossed with one key: the
&lt;code>model_name&lt;/code> and the &lt;code>namespace&lt;/code>. The join happens outside the tools (in a BI pipeline or a SQL
query over the LiteLLM database and the data exported from OpenCost):&lt;/p>
&lt;pre tabindex="0">&lt;code>coste_por_token_real = OpenCost.GPU_cost_namespace / LiteLLM.total_tokens_team
&lt;/code>&lt;/pre>&lt;p>When the on-prem cost is properly declared in &lt;code>input_cost_per_token&lt;/code>, LiteLLM already does this
calculation internally and the &lt;code>response_cost&lt;/code> is correct. The explicit join serves to validate:
if the sum of LiteLLM&amp;rsquo;s &lt;code>response_cost&lt;/code> per team does not match the GPU cost OpenCost assigns to
the namespace, there is drift in &lt;code>input_cost_per_token&lt;/code> that has to be corrected (changed
throughput, a new optimisation, more replicas).&lt;/p>
&lt;hr>
&lt;h2 id="kueue-as-the-gpu-budgetquota-mechanism">Kueue as the GPU budget/quota mechanism&lt;/h2>
&lt;h3 id="clusterqueue-localqueue-and-cohorts">ClusterQueue, LocalQueue and cohorts&lt;/h3>
&lt;p>Kueue introduces the &lt;strong>quota-based scheduling&lt;/strong> layer on top of Kubernetes
(&lt;a href="https://kueue.sigs.k8s.io/docs/concepts/cluster_queue/">Kueue — Cluster Queue&lt;/a>,
&lt;a href="https://kueue.sigs.k8s.io/docs/concepts/cohort/">Kueue — Cohort&lt;/a>,
&lt;a href="https://kueue.sigs.k8s.io/docs/concepts/fair_sharing/">Kueue — Fair Sharing&lt;/a>):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Object&lt;/th>
&lt;th>Scope&lt;/th>
&lt;th>What it does&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>ResourceFlavor&lt;/code>&lt;/td>
&lt;td>cluster&lt;/td>
&lt;td>maps resources to a group of nodes (e.g. H100 nodes)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ClusterQueue&lt;/code>&lt;/td>
&lt;td>cluster&lt;/td>
&lt;td>defines &lt;code>nominalQuota&lt;/code>, &lt;code>borrowingLimit&lt;/code>, &lt;code>lendingLimit&lt;/code> per resource/flavor&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>LocalQueue&lt;/code>&lt;/td>
&lt;td>namespace&lt;/td>
&lt;td>entry point for the team&amp;rsquo;s workloads; points to a &lt;code>ClusterQueue&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Cohort&lt;/code>&lt;/td>
&lt;td>cluster&lt;/td>
&lt;td>groups ClusterQueues that can lend quota to each other&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="quota-concepts">Quota concepts&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>&lt;code>nominalQuota&lt;/code>&lt;/strong>: GPUs guaranteed to the ClusterQueue at all times.&lt;/li>
&lt;li>&lt;strong>&lt;code>borrowingLimit&lt;/code>&lt;/strong>: maximum additional GPUs it can borrow from the cohort when others are not
using them.&lt;/li>
&lt;li>&lt;strong>&lt;code>lendingLimit&lt;/code>&lt;/strong>: GPUs from its &lt;code>nominalQuota&lt;/code> that it allows to be lent to others (if not
specified, it can lend all unused ones).&lt;/li>
&lt;li>&lt;strong>Fair Sharing&lt;/strong>: a mechanism that orders pending workloads by the historical resource usage of
their LocalQueue, giving preference to whoever has consumed less. Compatible with hierarchical
cohorts since Kueue v0.11.&lt;/li>
&lt;/ul>
&lt;h3 id="example-yaml-3-teams-in-the-llm-platform-cohort">Example YAML: 3 teams in the llm-platform cohort&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># ResourceFlavor mapping to the H100 SXM nodes&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">kueue.x-k8s.io/v1beta2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ResourceFlavor&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">h100-sxm&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">nodeLabels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">accelerator&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">h100-sxm&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># ClusterQueue for the datos team — 2 guaranteed GPUs, can borrow up to 2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">kueue.x-k8s.io/v1beta2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ClusterQueue&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cq-datos&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cohort&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">llm-platform&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">namespaceSelector&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matchLabels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">kubernetes.io/metadata.name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ns-datos&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">queueingStrategy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">BestEffortFIFO&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">resourceGroups&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">coveredResources&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;nvidia.com/gpu&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">flavors&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">h100-sxm&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">resources&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;nvidia.com/gpu&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">nominalQuota&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">borrowingLimit&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">2&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># can use up to 4 GPUs in total&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">preemption&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">reclaimWithinCohort&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">LowerPriority&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">withinClusterQueue&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">LowerPriority&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># ClusterQueue for the ia team — 1 guaranteed GPU, lends what it does not use&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">kueue.x-k8s.io/v1beta2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ClusterQueue&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cq-ia&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cohort&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">llm-platform&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">namespaceSelector&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matchLabels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">kubernetes.io/metadata.name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ns-ia&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">resourceGroups&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">coveredResources&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;nvidia.com/gpu&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">flavors&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">h100-sxm&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">resources&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;nvidia.com/gpu&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">nominalQuota&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">lendingLimit&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># lends its GPU when it is not using it&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># ClusterQueue for the plataforma team — 1 guaranteed GPU, low priority&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">kueue.x-k8s.io/v1beta2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ClusterQueue&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cq-plataforma&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cohort&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">llm-platform&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">namespaceSelector&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matchLabels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">kubernetes.io/metadata.name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ns-plataforma&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">resourceGroups&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">coveredResources&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;nvidia.com/gpu&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">flavors&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">h100-sxm&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">resources&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;nvidia.com/gpu&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">nominalQuota&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">borrowingLimit&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">3&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># can use all 4 if the rest are free&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># LocalQueue in each namespace — entry point for the workloads&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">kueue.x-k8s.io/v1beta2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">LocalQueue&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">lq-datos&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">namespace&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ns-datos&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">clusterQueue&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cq-datos&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="how-kueue-materialises-the-gpu-budget">How Kueue materialises the &amp;ldquo;GPU budget&amp;rdquo;&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>FinOps concept&lt;/th>
&lt;th>Kueue mechanism&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Guaranteed budget&lt;/td>
&lt;td>&lt;code>nominalQuota&lt;/code>: the team always has these GPUs available&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Maximum spend limit&lt;/td>
&lt;td>&lt;code>nominalQuota + borrowingLimit&lt;/code>: absolute ceiling of admissible GPUs&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Lending idle capacity&lt;/td>
&lt;td>cohort + &lt;code>lendingLimit&lt;/code>: others take what this one does not use&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Fair sharing between teams&lt;/td>
&lt;td>&lt;code>Fair Sharing&lt;/code> + &lt;code>WorkloadPriorityClass&lt;/code>: those who have consumed most wait longest&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Reclaiming your own quota&lt;/td>
&lt;td>&lt;code>preemption.reclaimWithinCohort: LowerPriority&lt;/code>: the owner of the lent GPU reclaims it by evicting lower-priority workloads&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The &lt;code>nominalQuota&lt;/code> is the expression of the budget in GPUs: if the team has 2 nominal GPUs and the
price is 1.40 €/GPU-hour, its maximum guaranteed spend is&lt;/p>
\[
\text{maximum nominal spend} = 2 \times 1.40 \times 720 = 2{,}016 \text{ €/month}
\]
&lt;p>The &lt;code>borrowingLimit&lt;/code> sets the potential overspend if it borrows from the cohort (and that
borrowing can be charged with the same formula, multiplying the borrowed usage hours by
1.40 €/GPU-hour).&lt;/p>
&lt;hr>
&lt;h2 id="cost-model-an-example-with-4h100-sxm-and-3-teams">Cost model: an example with 4×H100 SXM and 3 teams&lt;/h2>
&lt;h3 id="node-price">Node price&lt;/h3>
&lt;p>Generic hardware: a server with 4×H100 SXM 80 GB. 2026 market prices: a single H100 SXM costs
between 27,000 and 40,000 € depending on the source
(&lt;a href="https://www.gmicloud.ai/en/blog/nvidia-h100-gpu-pricing-2026-rent-vs-buy-cost-analysis">GMI Cloud — H100 GPU Pricing 2026&lt;/a>);
a 4×H100 server sits in the 120,000–180,000 € range including chassis, power supplies and NVLink.
We use 140,000 € as a generic assumption for the complete server (4 GPUs plus infrastructure).&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Component&lt;/th>
&lt;th>Calculation&lt;/th>
&lt;th>€/hour node&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Depreciated capex (140,000 €, 4 years, 90 % availability)&lt;/td>
&lt;td>140,000 ÷ (4 × 8,760 × 0.9)&lt;/td>
&lt;td>~4.43&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Energy (4 × 700 W TDP × PUE 1.4 × 0.12 €/kWh)&lt;/td>
&lt;td>4 × 0.7 × 1.4 × 0.12&lt;/td>
&lt;td>~0.47&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Operations / networking / maintenance&lt;/td>
&lt;td>estimate&lt;/td>
&lt;td>~0.70&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Total 4×H100 node&lt;/strong>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;strong>~5.60 €/h&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Per GPU&lt;/strong>&lt;/td>
&lt;td>5.60 ÷ 4&lt;/td>
&lt;td>&lt;strong>~1.40 €/GPU-hour&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Cost-per-token formula for the 70B model (sustained throughput ~2,000 tok/s at TP=4):&lt;/p>
$$\text{€/1M tokens} = \frac{5.60 \text{ €/h}}{2{,}000 \text{ tok/s} \times 3{,}600 \text{ s/h}} \times 10^6 \approx 0.78 \text{ €/1M tokens}$$
&lt;h3 id="monthly-scenario-3-teams-heterogeneous-utilisation">Monthly scenario: 3 teams, heterogeneous utilisation&lt;/h3>
&lt;p>Reference month (720 hours). The 4 GPUs are nominally assigned: 2 to &lt;strong>Datos&lt;/strong>, 1 to &lt;strong>IA&lt;/strong>, 1 to
&lt;strong>Plataforma&lt;/strong>. Real occupancy varies:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Team&lt;/th>
&lt;th>Nominal GPUs&lt;/th>
&lt;th>Average occupancy&lt;/th>
&lt;th>GPU-hours used&lt;/th>
&lt;th>Idle GPU-hours&lt;/th>
&lt;th>GPU cost (€)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Datos&lt;/td>
&lt;td>2&lt;/td>
&lt;td>75 %&lt;/td>
&lt;td>1,080&lt;/td>
&lt;td>360&lt;/td>
&lt;td>~1,512&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>IA&lt;/td>
&lt;td>1&lt;/td>
&lt;td>60 %&lt;/td>
&lt;td>432&lt;/td>
&lt;td>288&lt;/td>
&lt;td>~605&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Plataforma&lt;/td>
&lt;td>1&lt;/td>
&lt;td>35 %&lt;/td>
&lt;td>252&lt;/td>
&lt;td>468&lt;/td>
&lt;td>~353&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Cluster idle&lt;/strong>&lt;/td>
&lt;td>—&lt;/td>
&lt;td>—&lt;/td>
&lt;td>—&lt;/td>
&lt;td>&lt;strong>1,116&lt;/strong>&lt;/td>
&lt;td>&lt;strong>~1,562&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Node total&lt;/strong>&lt;/td>
&lt;td>4&lt;/td>
&lt;td>—&lt;/td>
&lt;td>1,764&lt;/td>
&lt;td>1,116&lt;/td>
&lt;td>&lt;strong>~4,032&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>Total node cost per month: 5.60 €/h × 720 h = 4,032 €.
Cost per GPU-hour used: 5.60 ÷ 4 = 1.40 €.&lt;/p>
&lt;/blockquote>
&lt;p>With &lt;code>shareIdle=false&lt;/code> (showback), the 1,562 € of idle appears as a separate row in the report and
nobody pays for it directly. With &lt;code>shareIdle=true&lt;/code> (proportional chargeback), it is split among
the three in proportion to their non-idle cost: Datos absorbs ~857 €, IA ~333 €, Plataforma ~192 €
of additional idle.&lt;/p>
&lt;h3 id="token-consumption-per-team-litellm-spend-logs">Token consumption per team (LiteLLM spend logs)&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Team&lt;/th>
&lt;th>Model&lt;/th>
&lt;th>Tokens/month&lt;/th>
&lt;th>Cost/token&lt;/th>
&lt;th>Token cost (€)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Datos&lt;/td>
&lt;td>llama-70b (TP=4)&lt;/td>
&lt;td>380 M&lt;/td>
&lt;td>0.78 €/1M&lt;/td>
&lt;td>~296&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>IA&lt;/td>
&lt;td>llama-8b (1 GPU)&lt;/td>
&lt;td>210 M&lt;/td>
&lt;td>0.35 €/1M&lt;/td>
&lt;td>~74&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Plataforma&lt;/td>
&lt;td>llama-8b + batch&lt;/td>
&lt;td>80 M&lt;/td>
&lt;td>0.35 €/1M&lt;/td>
&lt;td>~28&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="monthly-chargeback-report-showback--tokens">Monthly chargeback report (showback + tokens)&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Team&lt;/th>
&lt;th>Iron GPU cost (€)&lt;/th>
&lt;th>Assigned idle cost (€)&lt;/th>
&lt;th>LiteLLM token cost (€)&lt;/th>
&lt;th>Total chargeable (€)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Datos&lt;/td>
&lt;td>1,512&lt;/td>
&lt;td>857&lt;/td>
&lt;td>296&lt;/td>
&lt;td>&lt;strong>2,665&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>IA&lt;/td>
&lt;td>605&lt;/td>
&lt;td>333&lt;/td>
&lt;td>74&lt;/td>
&lt;td>&lt;strong>1,012&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Plataforma&lt;/td>
&lt;td>353&lt;/td>
&lt;td>192&lt;/td>
&lt;td>28&lt;/td>
&lt;td>&lt;strong>573&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Idle (without shareIdle)&lt;/td>
&lt;td>1,562&lt;/td>
&lt;td>—&lt;/td>
&lt;td>—&lt;/td>
&lt;td>visible, not charged&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="attribution-dimensions--policy-table">Attribution dimensions × policy table&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Attribution dimension&lt;/th>
&lt;th>Tool&lt;/th>
&lt;th>Showback&lt;/th>
&lt;th>Chargeback&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>namespace&lt;/td>
&lt;td>OpenCost &lt;code>aggregate=namespace&lt;/code>&lt;/td>
&lt;td>&lt;code>/allocation?aggregate=namespace&amp;amp;includeIdle=true&lt;/code>&lt;/td>
&lt;td>&lt;code>shareIdle=true&lt;/code> + transfer to P&amp;amp;L&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>pod label&lt;/td>
&lt;td>OpenCost &lt;code>aggregate=label:equipo&lt;/code>&lt;/td>
&lt;td>report per label&lt;/td>
&lt;td>same with shareIdle&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>virtual key / team&lt;/td>
&lt;td>LiteLLM &lt;code>/global/spend/teams&lt;/code>&lt;/td>
&lt;td>token dashboard&lt;/td>
&lt;td>hard &lt;code>max_budget&lt;/code> per key&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ClusterQueue (reserved GPU)&lt;/td>
&lt;td>Kueue &lt;code>nominalQuota&lt;/code>&lt;/td>
&lt;td>observe usage vs quota&lt;/td>
&lt;td>committed budget in GPUs&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>request tag&lt;/td>
&lt;td>LiteLLM &lt;code>tag_budgets&lt;/code>&lt;/td>
&lt;td>per cost centre&lt;/td>
&lt;td>budget per tag&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>pod annotation&lt;/td>
&lt;td>OpenCost &lt;code>aggregate=annotation:KEY&lt;/code>&lt;/td>
&lt;td>report per annotation&lt;/td>
&lt;td>same with shareIdle&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="end-to-end-data-flow">End-to-end data flow&lt;/h2>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 300" role="img" aria-label="GPU chargeback attribution flow: from the inference pod to the three planes OpenCost, LiteLLM and Kueue, and to the monthly report" xmlns="http://www.w3.org/2000/svg">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.3}.dsh{fill:none;stroke:currentColor;stroke-width:1.3;stroke-dasharray:5 3}.tl{font:600 12px sans-serif;fill:currentColor}.ts{font:11px sans-serif;fill:currentColor}.ar{fill:none;stroke:currentColor;stroke-width:1.3;marker-end:url(#chm)}&lt;/style>
&lt;defs>&lt;marker id="chm" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="currentColor"/>&lt;/marker>&lt;/defs>
&lt;rect class="bx" x="20" y="30" width="160" height="50" rx="6"/>
&lt;text x="32" y="52" class="tl">vLLM pod&lt;/text>
&lt;text x="32" y="68" class="ts">label:equipo=datos&lt;/text>
&lt;path class="ar" d="M180,55 L220,80"/>
&lt;path class="ar" d="M180,55 L220,145"/>
&lt;path class="ar" d="M180,55 L220,210"/>
&lt;rect class="bx" x="220" y="60" width="175" height="50" rx="6"/>
&lt;text x="232" y="82" class="tl">OpenCost (iron)&lt;/text>
&lt;text x="232" y="98" class="ts">€/GPU-hour × namespace/label&lt;/text>
&lt;rect class="bx" x="220" y="125" width="175" height="50" rx="6"/>
&lt;text x="232" y="147" class="tl">LiteLLM (tokens)&lt;/text>
&lt;text x="232" y="163" class="ts">€/token × team_id/key/model&lt;/text>
&lt;rect class="bx" x="220" y="190" width="175" height="50" rx="6"/>
&lt;text x="232" y="212" class="tl">Kueue (scheduler)&lt;/text>
&lt;text x="232" y="228" class="ts">nominalQuota / cohort / fair-share&lt;/text>
&lt;path class="ar" d="M395,85 L440,140"/>
&lt;path class="ar" d="M395,150 L440,150"/>
&lt;path class="ar" d="M395,215 L440,160"/>
&lt;rect class="bx" x="440" y="115" width="185" height="70" rx="6"/>
&lt;text x="452" y="137" class="tl">Monthly report&lt;/text>
&lt;text x="452" y="153" class="ts">€/team (iron + idle + tokens)&lt;/text>
&lt;text x="452" y="169" class="ts">borrowed GPUs · budget left&lt;/text>
&lt;path class="ar" d="M625,150 L665,150"/>
&lt;rect class="dsh" x="665" y="115" width="100" height="70" rx="6"/>
&lt;text x="677" y="143" class="tl">Showback&lt;/text>
&lt;text x="677" y="159" class="ts">or&lt;/text>
&lt;text x="677" y="175" class="tl">Chargeback&lt;/text>
&lt;text x="20" y="275" class="ts">The three planes are orthogonal: OpenCost sees the iron, LiteLLM sees the tokens, Kueue sees the scheduler quota.&lt;/text>
&lt;text x="20" y="292" class="ts">Crossing the three gives the complete number: €/team with idle, tokens and borrowed GPUs.&lt;/text>
&lt;/svg>
&lt;/div>
&lt;hr>
&lt;h2 id="fair-sharing-and-preemption-how-kueue-reclaims-quota">Fair sharing and preemption: how Kueue reclaims quota&lt;/h2>
&lt;p>When the Datos team has exhausted its 2 nominal GPUs and borrows those of Plataforma (which is
not using them), Kueue records that loan. As soon as Plataforma launches a new workload:&lt;/p>
&lt;ol>
&lt;li>Kueue detects that &lt;code>cq-plataforma&lt;/code> is below its &lt;code>nominalQuota&lt;/code>.&lt;/li>
&lt;li>With &lt;code>preemption.reclaimWithinCohort: LowerPriority&lt;/code>, Kueue evicts the Datos workload that was
on the borrowed GPU, if its priority is lower.&lt;/li>
&lt;li>The evicted workload returns to the &lt;code>cq-datos&lt;/code> queue and is readmitted once quota frees up.&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>Fair Sharing&lt;/strong> orders the pending queue by the LocalQueue&amp;rsquo;s accumulated historical usage: if
Datos has consumed more GPU-hours than IA in the recent period, IA&amp;rsquo;s next workloads have
admission priority over new ones from Datos. This implements an equitable split without blocking
anyone permanently.&lt;/p>
&lt;hr>
&lt;h2 id="configuring-opencost-for-the-node-price">Configuring OpenCost for the node price&lt;/h2>
&lt;p>For the numbers in the previous section to be reproducible, the node price has to be declared
explicitly (the default value underestimates on-prem GPU,
&lt;a href="https://github.com/opencost/opencost/issues/3781">issue #3781&lt;/a>):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># OpenCost Helm values.yaml — on-prem 4×H100 node, 5.60 €/h&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">opencost&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">customPricing&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">enabled&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">provider&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">custom&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">costModel&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">description&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;On-prem 4xH100 SXM node&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">CPU&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;0.025&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># €/CPU-hour (minor component)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">RAM&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;0.003&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># €/GB-hour&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">GPU&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;1.40&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># €/GPU-hour ← the one that drives the split&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">storage&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;0.0002&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># €/GB-hour&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Verify after configuring:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Check the price resolved per node&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl http://localhost:9003/allNodePricing
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Attribution query per team label, last month, idle separate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">curl -G http://localhost:9003/allocation &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -d &lt;span class="nv">window&lt;/span>&lt;span class="o">=&lt;/span>lastmonth &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -d &lt;span class="nv">aggregate&lt;/span>&lt;span class="o">=&lt;/span>label:equipo &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -d &lt;span class="nv">includeIdle&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">true&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -d &lt;span class="nv">shareIdle&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">false&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> -d &lt;span class="nv">accumulate&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">true&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="reference-promql-for-chargeback-dashboards">Reference PromQL for chargeback dashboards&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-promql" data-lang="promql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># GPU cost assigned per team (label:equipo) — €/hour&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">sum&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">by&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nv">label_equipo&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nv">container_gpu_allocation&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">on&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nv">node&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">group_left&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nv">node_gpu_hourly_cost&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1"># Idle GPU-hours per node (idle &amp;gt; 15 min)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">sum&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">by&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nv">node&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kr">avg_over_time&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nv">DCGM_FI_DEV_GPU_UTIL&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s">15m&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">on&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nv">node&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">group_left&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nv">node_gpu_hourly_cost&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1"># Accumulated monthly cost per team (sum over window)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kr">sum_over_time&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">sum&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">by&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nv">label_equipo&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nv">container_gpu_allocation&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">on&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nv">node&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">group_left&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nv">node_gpu_hourly_cost&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s">30d&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="s">1h&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="sources">Sources&lt;/h2>
&lt;ul>
&lt;li>FinOps Foundation — Invoicing &amp;amp; Chargeback Capability — &lt;a href="https://www.finops.org/framework/capabilities/invoicing-chargeback/">https://www.finops.org/framework/capabilities/invoicing-chargeback/&lt;/a>&lt;/li>
&lt;li>FinOps Foundation — Data Analysis and Showback — &lt;a href="https://www.finops.org/framework/previous-capabilities/analysis-showback/">https://www.finops.org/framework/previous-capabilities/analysis-showback/&lt;/a>&lt;/li>
&lt;li>FinOps Foundation — Allocation Capability — &lt;a href="https://www.finops.org/framework/capabilities/allocation/">https://www.finops.org/framework/capabilities/allocation/&lt;/a>&lt;/li>
&lt;li>OpenCost — API (Allocation API, parámetros window/aggregate/shareIdle/idleByNode) — &lt;a href="https://opencost.io/docs/integrations/api/">https://opencost.io/docs/integrations/api/&lt;/a>&lt;/li>
&lt;li>OpenCost — API Examples — &lt;a href="https://opencost.io/docs/integrations/api-examples/">https://opencost.io/docs/integrations/api-examples/&lt;/a>&lt;/li>
&lt;li>OpenCost — GitHub (issue #3781, infra-precio GPU on-prem por defecto) — &lt;a href="https://github.com/opencost/opencost/issues/3781">https://github.com/opencost/opencost/issues/3781&lt;/a>&lt;/li>
&lt;li>LiteLLM — Virtual Keys (&lt;code>max_budget&lt;/code>, &lt;code>budget_duration&lt;/code>, &lt;code>team_id&lt;/code>) — &lt;a href="https://docs.litellm.ai/docs/proxy/virtual_keys">https://docs.litellm.ai/docs/proxy/virtual_keys&lt;/a>&lt;/li>
&lt;li>LiteLLM — Setting Team Budgets — &lt;a href="https://docs.litellm.ai/docs/proxy/team_budgets">https://docs.litellm.ai/docs/proxy/team_budgets&lt;/a>&lt;/li>
&lt;li>LiteLLM — Spend Tracking — &lt;a href="https://docs.litellm.ai/docs/proxy/cost_tracking">https://docs.litellm.ai/docs/proxy/cost_tracking&lt;/a>&lt;/li>
&lt;li>LiteLLM — Budgets &amp;amp; Rate Limits — &lt;a href="https://docs.litellm.ai/docs/proxy/users">https://docs.litellm.ai/docs/proxy/users&lt;/a>&lt;/li>
&lt;li>LiteLLM — Setting Tag Budgets — &lt;a href="https://docs.litellm.ai/docs/proxy/tag_budgets">https://docs.litellm.ai/docs/proxy/tag_budgets&lt;/a>&lt;/li>
&lt;li>LiteLLM — Budget Reset Times — &lt;a href="https://docs.litellm.ai/docs/proxy/budget_reset_and_tz">https://docs.litellm.ai/docs/proxy/budget_reset_and_tz&lt;/a>&lt;/li>
&lt;li>Kueue — Cluster Queue (nominalQuota, borrowingLimit, lendingLimit, preemption) — &lt;a href="https://kueue.sigs.k8s.io/docs/concepts/cluster_queue/">https://kueue.sigs.k8s.io/docs/concepts/cluster_queue/&lt;/a>&lt;/li>
&lt;li>Kueue — Cohort — &lt;a href="https://kueue.sigs.k8s.io/docs/concepts/cohort/">https://kueue.sigs.k8s.io/docs/concepts/cohort/&lt;/a>&lt;/li>
&lt;li>Kueue — Fair Sharing — &lt;a href="https://kueue.sigs.k8s.io/docs/concepts/fair_sharing/">https://kueue.sigs.k8s.io/docs/concepts/fair_sharing/&lt;/a>&lt;/li>
&lt;li>Kueue — Administer Cluster Quotas — &lt;a href="https://kueue.sigs.k8s.io/docs/tasks/manage/administer_cluster_quotas/">https://kueue.sigs.k8s.io/docs/tasks/manage/administer_cluster_quotas/&lt;/a>&lt;/li>
&lt;li>GMI Cloud — NVIDIA H100 GPU Pricing 2026 — &lt;a href="https://www.gmicloud.ai/en/blog/nvidia-h100-gpu-pricing-2026-rent-vs-buy-cost-analysis">https://www.gmicloud.ai/en/blog/nvidia-h100-gpu-pricing-2026-rent-vs-buy-cost-analysis&lt;/a>&lt;/li>
&lt;li>IntuitionLabs — NVIDIA AI GPU Prices H100 Cost Guide — &lt;a href="https://intuitionlabs.ai/articles/nvidia-ai-gpu-pricing-guide">https://intuitionlabs.ai/articles/nvidia-ai-gpu-pricing-guide&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="see-also">See also&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/opencost-gpu-cost-allocation-kubernetes/">OpenCost in depth: how GPU cost is allocated in Kubernetes&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/kubecost-vs-opencost-vs-alternatives/">Kubecost vs OpenCost vs alternatives&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/gpu-hour-to-cost-per-token-on-prem-vs-cloud/">From GPU-hour to cost per token&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/finops-multi-tenancy-gpu-cluster-who-pays/">FinOps and GPU cluster multi-tenancy: who pays for what&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/sharing-one-gpu-time-slicing-mps-mig/">Sharing a GPU: time-slicing, MPS and MIG&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Sovereign on-premise vs hyperscalers: the case with data (cost, energy, performance and sovereignty)</title><link>https://blog.lo0.es/en/posts/sovereign-on-premise-vs-hyperscalers-data/</link><pubDate>Sun, 14 Jun 2026 05:30:00 +0200</pubDate><guid>https://blog.lo0.es/en/posts/sovereign-on-premise-vs-hyperscalers-data/</guid><description>&lt;blockquote>
&lt;p>Notation: amounts in &lt;strong>euros (N €)&lt;/strong>, decimals with a point; when a source quotes dollars it is marked
&amp;ldquo;USD&amp;rdquo;. Data centred on Europe and Spain. The dollar symbol is not used (formula delimiter).&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-this-article-covers">What this article covers&lt;/h2>
&lt;p>&lt;strong>Synthesis&lt;/strong> article of the series (S2), and the heart of the proposal: the case with data for
sovereign on-premise against the hyperscalers and the European cloud. Up to here, each track
measured its own axis, cost per token (FinOps), goodput (benchmarking), energy and carbon; here the
four are crossed, with a fourth dimension no US technical comparison puts up front: &lt;strong>data
sovereignty&lt;/strong>. The aim is to answer, with numbers and not with ideology, the question that underpins
any investment in an AI platform: &lt;strong>serve on your own iron, on a European cloud or on a
hyperscaler?&lt;/strong> And to do it honestly: on-prem &lt;strong>does not always win&lt;/strong>, and saying when it wins and
when it does not is what makes the recommendation credible.&lt;/p>
&lt;hr>
&lt;h2 id="the-framework-four-axes-not-one-number">The framework: four axes, not one number&lt;/h2>
&lt;p>The mistake in almost every comparison is reducing the decision to the hourly cost of a GPU. The real
decision crosses &lt;strong>four axes&lt;/strong>, and only by seeing them together do you decide well:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Axis&lt;/th>
&lt;th>Question&lt;/th>
&lt;th>Who measures it in the series&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Cost (TCO)&lt;/td>
&lt;td>how much does it cost to serve, all in?&lt;/td>
&lt;td>FinOps (A2–A8)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Performance&lt;/td>
&lt;td>does it meet the SLO, at what goodput?&lt;/td>
&lt;td>Benchmarking (B2–B8)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Energy and carbon&lt;/td>
&lt;td>how many watts and grams per token?&lt;/td>
&lt;td>Energy (C2–C8)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Sovereignty&lt;/td>
&lt;td>under which jurisdiction does the data live?&lt;/td>
&lt;td>GDPR / EU AI Act / CSRD&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The first three are quantifiable and meet in the cost per token; the fourth is a &lt;strong>constraint&lt;/strong>
that can rule out an option however cheap it is. The synthesis consists of scoring each option on all
four and deciding on the Pareto frontier, not on whichever axis happens to suit.&lt;/p>
&lt;hr>
&lt;h2 id="the-cost-axis-tco-and-break-even">The cost axis: TCO and break-even&lt;/h2>
&lt;p>The real on-premise cost is amortised capex plus opex, and its cost per effective hour depends on
&lt;strong>utilisation&lt;/strong>:&lt;/p>
$$\text{effective cost/GPU-hour (on-prem)} = \frac{\text{annual amortised capex} + \text{annual opex}}{8760 \times \text{utilisation}}$$
&lt;p>This formula is the key to the whole debate: &lt;strong>the cost per useful hour of on-prem rises as
utilisation falls&lt;/strong>, because capex is paid whether the GPU is working or idle. The 2026 data:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Data point&lt;/th>
&lt;th>Value&lt;/th>
&lt;th>Source&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>On-prem cost 8×H100 (floor, high util.)&lt;/td>
&lt;td>~2.83 USD/GPU-hour &lt;em>all-in&lt;/em>&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/">Spheron&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Annual on-prem cost (floor)&lt;/td>
&lt;td>~237,000 USD/year&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/">Spheron&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AWS H100 (p5.48xlarge)&lt;/td>
&lt;td>4.10–6.88 USD/GPU-hour&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/">Spheron&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AWS 8-GPU on-demand annual (100 % util)&lt;/td>
&lt;td>287,000–482,000 USD/year&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/">Spheron&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Sovereign European cloud (Lyceum/Scaleway)&lt;/td>
&lt;td>from 2–2.73 €/GPU-hour, zero-egress&lt;/td>
&lt;td>&lt;a href="https://lyceum.technology/magazine/eu-sovereign-inference-platform-comparison/">Lyceum&lt;/a>, &lt;a href="https://www.scaleway.com/en/h100/">Scaleway&lt;/a>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The &lt;strong>break-even&lt;/strong> against AWS on-demand falls around 50–83 % utilisation depending on region and
tariff; &lt;strong>below ~70 % utilisation, the cloud wins&lt;/strong> on TCO, and above it, on-prem
(&lt;a href="https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/">Spheron&lt;/a>). For very
high-utilisation workloads, on-prem pays for itself in less than 4 months (&lt;a href="https://lenovopress.lenovo.com/lp2368-on-premise-vs-cloud-generative-ai-total-cost-of-ownership-2026-edition">Lenovo&lt;/a>).&lt;/p>
&lt;h3 id="three-year-tco-the-full-calculation-for-an-8h100-node">Three-year TCO: the full calculation for an 8×H100 node&lt;/h3>
&lt;p>Abstract numbers do not convince a committee; a three-year model with declared line items does. Take a
sovereign 8×H100 node in Spain and compare it, for the same work, with AWS and with a European
cloud. Declared assumptions: amortisation over 3 years, energy at a solar PPA of 32.5 €/MWh
(with grid backup), PUE 1.3, and two utilisation scenarios (50 % and 80 %).&lt;/p>
&lt;p>&lt;strong>On-premise (own node), annual line items:&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Line item&lt;/th>
&lt;th>Annual value&lt;/th>
&lt;th>Note&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Amortised capex (node ~270,000 € / 3 years)&lt;/td>
&lt;td>~90,000 €&lt;/td>
&lt;td>8×H100 server + network + storage&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Energy (≈10.4 kW × PUE 1.3 × 8760 h)&lt;/td>
&lt;td>~3,850 € (at 32.5 €/MWh)&lt;/td>
&lt;td>with solar PPA; at grid tariff, ~12–18 k €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Operation, cooling, maintenance&lt;/td>
&lt;td>~25,000 €&lt;/td>
&lt;td>prorated staff, support, spares&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Datacenter space (rack, connectivity)&lt;/td>
&lt;td>~12,000 €&lt;/td>
&lt;td>colocation or own datacenter&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Annual total&lt;/td>
&lt;td>~131,000 €&lt;/td>
&lt;td>independent of utilisation&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>At a fixed 131,000 €/year, the cost per token depends only on how many tokens you generate,
that is, on utilisation:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Utilisation&lt;/th>
&lt;th>Useful GPU-hours/year&lt;/th>
&lt;th>Cost/1M tokens&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>50 %&lt;/td>
&lt;td>~35,000&lt;/td>
&lt;td>~2.9 €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>65 %&lt;/td>
&lt;td>~45,500&lt;/td>
&lt;td>~2.2 €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>80 %&lt;/td>
&lt;td>~56,000&lt;/td>
&lt;td>~1.8 € (with cheap grid, ~1.1 €)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Sovereign European cloud (Scaleway/Lyceum), pay per use:&lt;/strong> at ~2.2 €/GPU-hour with zero-egress, the
cost per token is &lt;strong>constant with utilisation&lt;/strong> (you only pay for what you use): ~1.5–2.2 €/1M tokens
depending on model and batching, with no capex and no idle risk.&lt;/p>
&lt;p>&lt;strong>Hyperscaler (AWS p5), on-demand:&lt;/strong> at 4.10–6.88 USD/GPU-hour (≈3.8–6.4 €), the cost per token works
out at ~2–3.5 €/1M tokens, and on top of that you have to add the egress, quite apart from the
fact that for GDPR data the sovereignty axis rules it out already.&lt;/p>
&lt;p>The reading of the model is the thesis of the whole of S2: &lt;strong>at 50 % utilisation, on-prem (~2.9 €) does
not beat the European cloud (~1.8 €); at 80 % with cheap energy (~1.1 €), it beats it comfortably&lt;/strong>. The
crossover sits, as the literature says, around 65–70 %. Investing in on-prem is, fundamentally, a
bet that you will sustain high utilisation, and that bet is won with scheduling, not with hardware.&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 250" role="img" aria-label="Break-even on-prem vs cloud by utilisation: the cost per useful hour of on-prem falls with utilisation and crosses that of the cloud at around 65-70 per cent" xmlns="http://www.w3.org/2000/svg">
&lt;style>.ax{fill:none;stroke:currentColor;stroke-width:1}.cv{fill:none;stroke:currentColor;stroke-width:1.6}.dsh{fill:none;stroke:currentColor;stroke-width:1;stroke-dasharray:4 3}.tl{font:600 12px sans-serif;fill:currentColor}.ts{font:11px sans-serif;fill:currentColor}&lt;/style>
&lt;line class="ax" x1="60" y1="40" x2="60" y2="200"/>
&lt;line class="ax" x1="60" y1="200" x2="720" y2="200"/>
&lt;text x="20" y="130" class="ts" transform="rotate(-90 20 130)">cost/useful hour&lt;/text>
&lt;text x="330" y="228" class="ts">utilisation (%) →&lt;/text>
&lt;path class="cv" d="M90,55 C200,90 320,130 430,150 C540,165 640,172 700,175"/>
&lt;text x="95" y="50" class="ts">on-prem (fixed capex)&lt;/text>
&lt;line class="cv" x1="60" y1="150" x2="720" y2="150"/>
&lt;text x="600" y="143" class="ts">cloud (≈ flat)&lt;/text>
&lt;line class="dsh" x1="430" y1="40" x2="430" y2="200"/>
&lt;text x="392" y="56" class="tl">break-even ~65-70%&lt;/text>
&lt;text x="110" y="190" class="ts">low util.: cloud wins&lt;/text>
&lt;text x="520" y="190" class="ts">high util.: on-prem wins&lt;/text>
&lt;text x="60" y="245" class="ts">On-prem only wins to the right of the break-even; fixed capex penalises it at low utilisation.&lt;/text>
&lt;/svg>
&lt;/div>
&lt;hr>
&lt;h2 id="the-uncomfortable-reality-the-utilisation-almost-nobody-reaches">The uncomfortable reality: the utilisation almost nobody reaches&lt;/h2>
&lt;p>Here is the honest figure missing from the &amp;ldquo;on-prem is always cheaper&amp;rdquo; speeches: &lt;strong>most inference teams
in production operate at 40–65 % GPU utilisation&lt;/strong>, because of traffic variability and the limits of
batching; the 80–90 % assumption that makes on-prem attractive &lt;strong>is rarely reached outside batch-only
pipelines&lt;/strong> (&lt;a href="https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/">Spheron&lt;/a>).&lt;/p>
&lt;p>This changes the naive conclusion: if your real utilisation is 50 %, on-prem &lt;strong>is not cheaper&lt;/strong> than
the cloud, because the capex you pay for the idle GPU eats you. That is why utilisation is not a
detail, it is &lt;strong>the variable that decides the cost axis&lt;/strong>, and it connects directly with the FinOps
track (the idle of A2, the chargeback of A5) and with scheduling: &lt;strong>raising utilisation is what makes
on-prem pay&lt;/strong>. A badly used cluster of your own is more expensive than the cloud; a well scheduled one
is far cheaper. The cost question is not &amp;ldquo;on-prem or cloud?&amp;rdquo;, it is &amp;ldquo;&lt;strong>can I sustain high
utilisation?&lt;/strong>&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="the-hidden-costs-of-the-cloud-egress">The hidden costs of the cloud: egress&lt;/h2>
&lt;p>The cloud has its own small print: &lt;strong>egress costs&lt;/strong> (taking data out of the provider). In the
hyperscalers, moving data out or between regions is billed, and in AI workloads with a lot of data
movement (datasets, checkpoints, embeddings) it can be a significant line item that does not appear in
the GPU-hour price. The advantage of the sovereign European cloud: most of them (Lyceum among
others) have adopted the zero-egress model, they do not charge for moving data out or between
regions (&lt;a href="https://lyceum.technology/magazine/eu-sovereign-inference-platform-comparison/">Lyceum&lt;/a>). When
comparing, the real hyperscaler cost is GPU-hour + egress + other charges, not just the GPU-hour;
ignoring it artificially inflates the hyperscaler&amp;rsquo;s competitiveness.&lt;/p>
&lt;p>An example of the order of magnitude: a platform moving 50 TB/month outbound (datasets,
checkpoints, responses served to systems outside the provider) at a typical egress tariff of
~0.08–0.09 €/GB pays ~4,000–4,500 €/month, that is ~50,000 €/year on egress alone, a line item
the size of a third of the cost of a node of your own, invisible in the GPU-hour price. On the European
cloud with zero-egress that line item is zero; on-prem, internal traffic is not billed either.
That is why a fair comparison must model egress according to the real data pattern: for workloads with
a lot of outbound movement, it can invert the ranking between hyperscaler and European cloud. The
cloud bill is not the GPU-hour; it is the GPU-hour plus everything you move.&lt;/p>
&lt;p>To this is added contract and lock-in risk: the hyperscaler&amp;rsquo;s on-demand GPU tariffs can change,
commitment discounts (reserved/savings plans) tie you for 1–3 years, and migrating out, because of
egress and coupling to proprietary services, has a real exit cost. On-prem and the European cloud with
standard APIs (Kubernetes, S3 compatible) reduce that coupling: the same manifest and the same vLLM run
on your cluster or on Scaleway without rewriting. Operational sovereignty, being able to move the
workload without rebuilding it, is a value that does not appear in the tariff but weighs on a
three-year decision.&lt;/p>
&lt;hr>
&lt;h2 id="the-performance-axis-the-provider-does-not-decide-goodput-does">The performance axis: the provider does not decide, goodput does&lt;/h2>
&lt;p>One point that simplifies the synthesis: &lt;strong>performance does not depend on the provider, it depends on
the hardware and the configuration&lt;/strong>. An H100 gives the same goodput on your cluster, on Scaleway or on
AWS, served with the same vLLM and the same config. What decides performance is the &lt;strong>goodput under
your SLO&lt;/strong> (track B), not who hosts the GPU. So, in a comparison at equal hardware, the performance
axis neutralises itself: what changes between options is cost, energy and sovereignty. The
exception: if a provider gives you access to newer hardware (B200, GB200) ahead of your on-prem buying
cycle, then the cloud can win on performance per GPU, a real argument in favour of the cloud for
staying at the hardware frontier without capex.&lt;/p>
&lt;hr>
&lt;h2 id="the-energy-axis-the-european-and-spanish-advantage">The energy axis: the European and Spanish advantage&lt;/h2>
&lt;p>Here on-prem (or cloud) in Spain or France has a structural advantage over a hyperscaler in a dirty
region. Recalling the data from the energy track:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Location&lt;/th>
&lt;th>Grid carbon (gCO₂/kWh)&lt;/th>
&lt;th>Price (indicative)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>France (nuclear)&lt;/td>
&lt;td>~20–60&lt;/td>
&lt;td>low and stable&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Spain (renewables + gas)&lt;/td>
&lt;td>~150–170&lt;/td>
&lt;td>low, volatile; solar PPA ~32.5 €/MWh&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Germany&lt;/td>
&lt;td>~363&lt;/td>
&lt;td>high&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Hyperscaler (region depends on provider)&lt;/td>
&lt;td>depends; often not selectable&lt;/td>
&lt;td>provider tariff&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The same workload in &lt;strong>France emits ~9× less carbon per token than in Germany&lt;/strong>, and in Spain, with a
solar PPA at 32.5 €/MWh (an all-time low), the electricity cost, 30–50 % of the TCO, is low and,
under contract, predictable. A sovereign cluster in Spain or France controls where the energy is
consumed and with what carbon; a hyperscaler gives you the region it gives you, often with no choice of
grid intensity. For CSRD reporting, that selectability is a quantifiable advantage of on-prem and the
European cloud.&lt;/p>
&lt;p>In concrete numbers: the example 8×H100 node (~10.4 kW × PUE 1.3 ≈ 118,000 kWh/year) emits, depending
on the grid, ~2.4 t CO₂/year in France (~20 gCO₂/kWh) against ~43 t CO₂/year in Germany
(~363 gCO₂/kWh), the same machine, the same work, ~18× the difference in reportable footprint just
by choosing the location. That decision, which a hyperscaler in an imposed region does not let you
take, is exactly what on-prem and the sovereign European cloud put in your hands. The energy axis is
not an environmental detail: it is cost (the price of the kWh), compliance (CSRD) and sovereignty
(control of location) all at once.&lt;/p>
&lt;hr>
&lt;h2 id="the-sovereignty-axis-the-one-that-does-not-depend-on-utilisation">The sovereignty axis: the one that does not depend on utilisation&lt;/h2>
&lt;p>And here is the axis that invalidates the cheapest option if the data is sensitive. US hyperscalers
are subject to the &lt;strong>US CLOUD Act&lt;/strong>: US authorities can demand data held by a US company &lt;strong>even if it
sits in a European datacenter&lt;/strong>. For data subject to the &lt;strong>GDPR&lt;/strong>, that is a compliance risk. &lt;strong>Sovereign
European clouds&lt;/strong> operate under &lt;strong>EU/EFTA jurisdiction&lt;/strong>, providing data residency and GDPR compliance,
and are exempt from the US CLOUD Act
(&lt;a href="https://lyceum.technology/magazine/sovereign-cloud-providers-2026/">Lyceum · sovereign providers&lt;/a>).
On-prem of your own is the maximum degree of sovereignty: the data does not leave your cluster.&lt;/p>
&lt;p>The key difference from the other axes: sovereignty &lt;strong>does not depend on utilisation or on volume&lt;/strong>.
However much a hyperscaler cheapens the GPU-hour, for GDPR data &lt;strong>it is not an option&lt;/strong>, the
jurisdiction risk is not offset by price. It links with &lt;a href="https://blog.lo0.es/en/posts/technical-controls-ens-iso-42001-eu-ai-act-cross-mapping/">the ENS × ISO 42001 × EU AI Act controls&lt;/a>
and &lt;a href="https://blog.lo0.es/en/posts/eu-ai-act-technical-dossier-on-premise-llm-architecture/">the EU AI Act mapping&lt;/a>: compliance is a
hard constraint, not an axis to optimise.&lt;/p>
&lt;p>The four instruments that turn sovereignty into a concrete constraint rather than a slogan:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Instrument&lt;/th>
&lt;th>What it requires&lt;/th>
&lt;th>Implication for the architecture&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>US CLOUD Act&lt;/td>
&lt;td>gives the US access to data held by US companies, wherever it is&lt;/td>
&lt;td>a US hyperscaler does not guarantee jurisdictional residency even if the datacenter is in the EU&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GDPR&lt;/td>
&lt;td>residency and processing of personal data under EU law&lt;/td>
&lt;td>requires an EU/EFTA provider or your own iron for personal data&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>EU AI Act&lt;/td>
&lt;td>traceability, risk management and records for AI systems&lt;/td>
&lt;td>favours the full control of the stack (logs, datasets, models) that on-prem gives&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CSRD&lt;/td>
&lt;td>verifiable reporting of environmental footprint&lt;/td>
&lt;td>the selectable energy (clean grid, PPA) of on-prem and the European cloud is auditable&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The operational conclusion: for a European entity processing personal data or deploying high-risk AI,
&lt;strong>three of the four technical axes can favour the hyperscaler and it can still lose&lt;/strong>, because the
fourth axis, sovereignty, acts as a prior filter. That is why S2 orders the decision like this:
&lt;strong>first the sovereignty filter&lt;/strong> (which rules out the hyperscaler for GDPR data), &lt;strong>then the
optimisation of cost, performance and energy&lt;/strong> among the options that pass the filter (sovereign
on-prem and European cloud).&lt;/p>
&lt;hr>
&lt;h2 id="the-scorecard-the-three-options-scored">The scorecard: the three options scored&lt;/h2>
&lt;p>Crossing the four axes for the three realistic options of a European platform (order-of-magnitude,
illustrative figures):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Option&lt;/th>
&lt;th>Cost/1M tok&lt;/th>
&lt;th>Break-even&lt;/th>
&lt;th>Energy/carbon&lt;/th>
&lt;th>Sovereignty&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Sovereign on-prem (ES/FR)&lt;/td>
&lt;td>~1.1 € (high util.) / ~3 € (low)&lt;/td>
&lt;td>&amp;gt;65–70 % util.&lt;/td>
&lt;td>controllable (clean grid, PPA)&lt;/td>
&lt;td>total (EU)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Sovereign European cloud&lt;/td>
&lt;td>~1.5–2.2 €&lt;/td>
&lt;td>no capex, pay per use&lt;/td>
&lt;td>EU, zero-egress&lt;/td>
&lt;td>high (EU)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Hyperscaler (US)&lt;/td>
&lt;td>~2–3.5 € + egress&lt;/td>
&lt;td>no capex&lt;/td>
&lt;td>imposed region&lt;/td>
&lt;td>not EU (CLOUD Act)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 220" role="img" aria-label="Scorecard: sovereign on-prem, sovereign European cloud and hyperscaler scored on cost, energy and sovereignty; the hyperscaler loses on sovereignty" xmlns="http://www.w3.org/2000/svg">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.3}.tl{font:600 12px sans-serif;fill:currentColor}.ts{font:11px sans-serif;fill:currentColor}&lt;/style>
&lt;rect class="bx" x="20" y="40" width="230" height="130" rx="6"/>
&lt;text x="32" y="62" class="tl">Sovereign on-prem (ES/FR)&lt;/text>
&lt;text x="32" y="84" class="ts">cost: lowest IF util. is high&lt;/text>
&lt;text x="32" y="104" class="ts">energy: controllable (PPA, grid)&lt;/text>
&lt;text x="32" y="124" class="ts">sovereignty: TOTAL&lt;/text>
&lt;text x="32" y="148" class="ts">risk: capex + utilisation&lt;/text>
&lt;rect class="bx" x="275" y="40" width="230" height="130" rx="6"/>
&lt;text x="287" y="62" class="tl">Sovereign European cloud&lt;/text>
&lt;text x="287" y="84" class="ts">cost: medium, no capex&lt;/text>
&lt;text x="287" y="104" class="ts">energy: EU, zero-egress&lt;/text>
&lt;text x="287" y="124" class="ts">sovereignty: HIGH (EU)&lt;/text>
&lt;text x="287" y="148" class="ts">risk: pay-per-use price&lt;/text>
&lt;rect class="bx" x="530" y="40" width="230" height="130" rx="6"/>
&lt;text x="542" y="62" class="tl">Hyperscaler (US)&lt;/text>
&lt;text x="542" y="84" class="ts">cost: medium + egress&lt;/text>
&lt;text x="542" y="104" class="ts">energy: imposed region&lt;/text>
&lt;text x="542" y="124" class="ts">sovereignty: NOT EU ✗&lt;/text>
&lt;text x="542" y="148" class="ts">ruled out for GDPR data&lt;/text>
&lt;text x="20" y="200" class="ts">For GDPR data the hyperscaler is out on sovereignty, however cheap. The real decision is on-prem vs European cloud.&lt;/text>
&lt;/svg>
&lt;/div>
&lt;p>The reading of the scorecard: for data subject to the GDPR, &lt;strong>the US hyperscaler is ruled out by the
sovereignty axis&lt;/strong>, however competitive its tariff. The real decision comes down to &lt;strong>sovereign on-prem
vs sovereign European cloud&lt;/strong>, and there it is decided by &lt;strong>utilisation&lt;/strong> and &lt;strong>volume&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h2 id="when-each-option-wins">When each option wins&lt;/h2>
&lt;p>The honest recommendation, by scenario:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Scenario&lt;/th>
&lt;th>Winning option&lt;/th>
&lt;th>Why&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>High, sustained volume (util. &amp;gt;65–70 %), GDPR data&lt;/td>
&lt;td>Sovereign on-prem&lt;/td>
&lt;td>lowest cost/token + total sovereignty&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Variable or growing volume, GDPR data&lt;/td>
&lt;td>Sovereign European cloud&lt;/td>
&lt;td>sovereignty without capex/idle risk&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Low volume or sporadic peak&lt;/td>
&lt;td>European cloud (per use)&lt;/td>
&lt;td>you do not amortise the capex&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>No sovereignty requirement, hardware frontier&lt;/td>
&lt;td>Hyperscaler&lt;/td>
&lt;td>access to new hardware without capex&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Hybrid (base + peak)&lt;/td>
&lt;td>On-prem + European cloud (burst)&lt;/td>
&lt;td>cheap base of your own, sovereign elastic peak&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>On-prem makes sense when there is &lt;strong>very high, predictable utilisation (80 %+), strict sovereignty
requirements, or a hyperscaler contract that works out expensive&lt;/strong> (&lt;a href="https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/">Spheron&lt;/a>).
For sovereign platforms with a sustained base load, the winning pattern is usually the &lt;strong>hybrid&lt;/strong>:
sovereign on-prem for the high-utilisation base (where the cost per token is unbeatable) and
sovereign European cloud for the peak and for growth (elastic, no capex, keeping EU jurisdiction).
The best of both without giving up sovereignty.&lt;/p>
&lt;h3 id="sizing-the-hybrid-how-much-on-iron-how-much-on-cloud">Sizing the hybrid: how much on iron, how much on cloud&lt;/h3>
&lt;p>The hybrid is not &amp;ldquo;a bit of each&amp;rdquo;; it is sized with one figure: the &lt;strong>base load percentile&lt;/strong>. The rule
is to put on on-prem the load that is almost always present (the load that keeps the GPU at
75–85 %) and to send to the European cloud only the peaks that, if covered with iron, would leave
GPUs idle most of the time. An example with a realistic traffic profile:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Load band&lt;/th>
&lt;th>% of the time&lt;/th>
&lt;th>Where to serve&lt;/th>
&lt;th>Why&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Base (p0–p70)&lt;/td>
&lt;td>always&lt;/td>
&lt;td>on-prem (1 node 8×H100 at ~80 %)&lt;/td>
&lt;td>minimum cost/token, high util. guaranteed&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Middle (p70–p95)&lt;/td>
&lt;td>daily peak hours&lt;/td>
&lt;td>on-prem if it fits, otherwise European cloud&lt;/td>
&lt;td>elasticity without idle capex&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Peak (p95–p100)&lt;/td>
&lt;td>sporadic&lt;/td>
&lt;td>sovereign European cloud (burst)&lt;/td>
&lt;td>absurd to buy iron for a rare peak&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>With this split, the base amortises your own node at high utilisation (~1.1–1.8 €/1M tokens) and the
peak is paid per use with no idle penalty (~1.5–2.2 €/1M tokens), all under EU jurisdiction. The
expensive mistake is the opposite: sizing the on-prem for the peak, in which case the GPU spends
most of its time idle at 30–40 %, the cost per token shoots up above 3 € and the cloud would have been
cheaper. &lt;strong>You size the iron for the base, not for the peak&lt;/strong>; the peak is exactly what the cloud does
well. This principle connects with the capacity planning and the scheduling (Kueue/Volcano) of the
series: the hybrid only works if the scheduler fills your own node before overflowing to the cloud.&lt;/p>
&lt;hr>
&lt;h2 id="assumptions-and-sensitivity">Assumptions and sensitivity&lt;/h2>
&lt;p>The whole comparison hangs on assumptions that have to be declared, because moving them moves the
conclusion:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Assumption&lt;/th>
&lt;th>If it rises&lt;/th>
&lt;th>Effect&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Utilisation&lt;/td>
&lt;td>50 % → 80 %&lt;/td>
&lt;td>on-prem goes from losing to clearly winning&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Energy price&lt;/td>
&lt;td>expensive region → France/PPA&lt;/td>
&lt;td>lowers on-prem TCO and carbon&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Amortisation period&lt;/td>
&lt;td>24 → 36 months&lt;/td>
&lt;td>lowers the on-prem cost per hour&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Volume&lt;/td>
&lt;td>&amp;lt; 2M tok/day → much more&lt;/td>
&lt;td>crosses the break-even towards on-prem&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Egress (hyperscaler)&lt;/td>
&lt;td>low → high&lt;/td>
&lt;td>makes the hyperscaler dearer than the European cloud&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The rule: &lt;strong>no on-prem vs cloud comparison is valid without fixing these assumptions&lt;/strong>. One that says
&amp;ldquo;on-prem is 3× cheaper&amp;rdquo; without declaring the assumed utilisation is propaganda; one that fixes
utilisation, energy price, period and volume is a data point. The dossier must present the case with
explicit assumptions and a sensitivity analysis, which is what makes it defensible before a committee
that questions them.&lt;/p>
&lt;h2 id="decision-checklist">Decision checklist&lt;/h2>
&lt;p>To take S2 from theory to decision, the questions that order the choice, in order:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Is the data subject to the GDPR, or is the system high-risk under the EU AI Act?&lt;/strong> If so, the US
hyperscaler is &lt;strong>ruled out on sovereignty&lt;/strong>; you choose between on-prem and European cloud. If not,
the hyperscaler enters the cost comparison.&lt;/li>
&lt;li>&lt;strong>Can I sustain utilisation above 65–70 % on the base load?&lt;/strong> If so, on-prem wins on cost for that
base. If not, the European cloud avoids paying capex for idle GPUs.&lt;/li>
&lt;li>&lt;strong>Does the traffic profile have marked peaks?&lt;/strong> If so, &lt;strong>hybrid&lt;/strong>: base on iron, peak on European
cloud. Size the iron for the base, never for the peak.&lt;/li>
&lt;li>&lt;strong>How much data do I take out of the provider each month?&lt;/strong> Model the egress; with a lot of
movement, the zero-egress of the European cloud, or on-prem, win clearly.&lt;/li>
&lt;li>&lt;strong>Which electricity grid and at what price?&lt;/strong> France or Spain with a PPA lower TCO and carbon;
include it in the model and in the CSRD report.&lt;/li>
&lt;li>&lt;strong>Have I fixed utilisation, energy, period and volume in writing?&lt;/strong> Without those four declared
assumptions, the number is not defensible.&lt;/li>
&lt;/ol>
&lt;p>Whoever answers these six questions with data, not with intuition, has the case built. The series'
recommendation for a sovereign European platform with a sustained base load is stable: &lt;strong>sovereign
on-prem for the high-utilisation base plus sovereign European cloud for the peak&lt;/strong>, with the
hyperscaler reserved only for workloads with no sovereignty requirement where frontier hardware is
needed without capex.&lt;/p>
&lt;hr>
&lt;h2 id="limits-and-traps-data-driven">Limits and traps (data-driven)&lt;/h2>
&lt;ol>
&lt;li>&lt;strong>Unrealistic assumed utilisation.&lt;/strong> The 80–90 % that makes on-prem win rarely happens in production
(40–65 % typical). Model your real utilisation, not the ideal one.&lt;/li>
&lt;li>&lt;strong>Comparing only the GPU-hour.&lt;/strong> TCO includes energy, operation, cooling, egress (cloud) and capex
(on-prem). Compare totals with the same assumptions.&lt;/li>
&lt;li>&lt;strong>Ignoring sovereignty.&lt;/strong> For GDPR data, the sovereignty axis rules out the hyperscaler before cost
does; it is not negotiable with price.&lt;/li>
&lt;li>&lt;strong>Forgetting the hybrid.&lt;/strong> It is not &amp;ldquo;all on-prem or all cloud&amp;rdquo;; the base-plus-peak pattern usually
dominates.&lt;/li>
&lt;li>&lt;strong>Data in USD.&lt;/strong> US comparisons are in dollars and with dirty regions; convert them to euros and to
your region&amp;rsquo;s grid (Spain/France) for your case.&lt;/li>
&lt;/ol>
&lt;p>The synthesis of S2, in one sentence: &lt;strong>for sovereign European data, the decision is not on-prem vs
cloud in the abstract, but sovereign on-prem (high utilisation) plus sovereign European cloud (peak)
against a hyperscaler that the sovereignty axis rules out&lt;/strong>, and utilisation is the variable that
splits the base between the first two. The rest of the series gives the numbers for each axis; this one
crosses them into the recommendation. The next synthesis article (S3) sizes the investment; this one
decides the architecture.&lt;/p>
&lt;h2 id="see-also">See also&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/cloud-gpu-on-demand-reserved-spot-neoclouds-2026-prices/">Cloud GPU: price comparison, commitment and sovereign neoclouds&lt;/a> — the on-demand, spot and reserved prices of the European cloud providers that appear as an alternative in this analysis, with updated 2026 data.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/full-tco-on-premise-gpu-cluster-capex-gpu-hour-break-even/">TCO of the on-premise GPU cluster: amortisation, energy and infrastructure&lt;/a> — the full breakdown of on-premise TCO: server CAPEX, amortisation, energy, network and staff, with the spreadsheet that gives the real €/GPU-hour.&lt;/li>
&lt;/ul>
&lt;h2 id="sources">Sources&lt;/h2>
&lt;ul>
&lt;li>Spheron · LLM Inference On-Premise vs GPU Cloud: 2026 Cost and Break-Even — &lt;a href="https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/">https://www.spheron.network/blog/llm-inference-on-premise-vs-cloud/&lt;/a>&lt;/li>
&lt;li>Lenovo Press · On-Premise vs Cloud: Generative AI TCO (2026) — &lt;a href="https://lenovopress.lenovo.com/lp2368-on-premise-vs-cloud-generative-ai-total-cost-of-ownership-2026-edition">https://lenovopress.lenovo.com/lp2368-on-premise-vs-cloud-generative-ai-total-cost-of-ownership-2026-edition&lt;/a>&lt;/li>
&lt;li>Lyceum · EU Sovereign Inference Platform Comparison (2026) — &lt;a href="https://lyceum.technology/magazine/eu-sovereign-inference-platform-comparison/">https://lyceum.technology/magazine/eu-sovereign-inference-platform-comparison/&lt;/a>&lt;/li>
&lt;li>Lyceum · Sovereign Cloud Providers 2026 — &lt;a href="https://lyceum.technology/magazine/sovereign-cloud-providers-2026/">https://lyceum.technology/magazine/sovereign-cloud-providers-2026/&lt;/a>&lt;/li>
&lt;li>Scaleway · H100 GPU instance (precio €, soberanía UE) — &lt;a href="https://www.scaleway.com/en/h100/">https://www.scaleway.com/en/h100/&lt;/a>&lt;/li>
&lt;li>Nerd Level Tech · GPU Cloud TCO 2026: hidden fees, egress costs — &lt;a href="https://nerdleveltech.com/gpu-cloud-comparison-2026-the-real-cost-of-ai-compute">https://nerdleveltech.com/gpu-cloud-comparison-2026-the-real-cost-of-ai-compute&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>The second cost vector of AI agents: durable execution with Temporal</title><link>https://blog.lo0.es/en/posts/second-cost-vector-ai-agents-durable-execution-temporal/</link><pubDate>Sun, 14 Jun 2026 04:00:00 +0200</pubDate><guid>https://blog.lo0.es/en/posts/second-cost-vector-ai-agents-durable-execution-temporal/</guid><description>&lt;blockquote>
&lt;p>Notation: amounts in &lt;strong>euros (N €)&lt;/strong>, decimals with a point. The dollar symbol is not used
(on this site it is a formula delimiter).&lt;/p>
&lt;/blockquote>
&lt;h2 id="the-thesis-agents-have-two-cost-vectors-not-one">The thesis: agents have two cost vectors, not one&lt;/h2>
&lt;p>Almost all the cost discourse around agentic AI focuses on a single vector: the &lt;strong>cost of
inference&lt;/strong> (the tokens). And that one is solvable: &lt;strong>FastMCP + vLLM on local hardware eliminates
the cost of inference&lt;/strong> — you serve the models on your own iron, the cost per token drops to your
amortised cost, with no API bill. But there is a &lt;strong>second vector&lt;/strong> that almost nobody measures and
that kills more projects: the &lt;strong>cost of faulty execution&lt;/strong>. An agent is not a call, it is a
&lt;strong>sequence&lt;/strong> of steps (reason, call a tool, reason, call another one, and so on), and when that
sequence fails halfway through with no persisted state, the cost is not linear: &lt;strong>it doubles, it
spirals with retries and it inflates on every recovery&lt;/strong>. This article is about that second vector,
how &lt;strong>durable execution&lt;/strong> attacks it (with Temporal as the reference), what the state of the art and
the papers say, and why it is the real cause of so many cancellations.&lt;/p>
&lt;p>The framing that backs the urgency comes from &lt;strong>Gartner&lt;/strong>: it predicts that &lt;strong>more than 40 % of
agentic AI projects will be cancelled before the end of 2027&lt;/strong>, because of &lt;strong>rising costs, unclear
business value or inadequate risk controls&lt;/strong> (&lt;a href="https://www.gartner.com/en/newsroom/press-releases/2025-06-25-gartner-predicts-over-40-percent-of-agentic-ai-projects-will-be-canceled-by-end-of-2027">Gartner&lt;/a>).
&amp;ldquo;Rising costs&amp;rdquo; is exactly this second vector: not the cost of serving the model, but the cost of
running it badly.&lt;/p>
&lt;hr>
&lt;h2 id="the-three-expenses-of-a-faulty-agent">The three expenses of a faulty agent&lt;/h2>
&lt;p>The cost of faulty execution breaks down into three concrete sources, and it is worth seeing them
with numbers:&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 220" role="img" aria-label="The three expenses of a faulty agent: failures charged twice, uncontrolled retries, and inflated context on recovery" xmlns="http://www.w3.org/2000/svg">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.3}.tl{font:600 12px sans-serif;fill:currentColor}.ts{font:11px sans-serif;fill:currentColor}&lt;/style>
&lt;rect class="bx" x="20" y="50" width="230" height="100" rx="6"/>
&lt;text x="32" y="72" class="tl">1 · Failures charged twice&lt;/text>
&lt;text x="32" y="92" class="ts">failure at step 15 of 20&lt;/text>
&lt;text x="32" y="108" class="ts">→ the previous 14 are thrown away&lt;/text>
&lt;text x="32" y="124" class="ts">+ full retry&lt;/text>
&lt;rect class="bx" x="275" y="50" width="230" height="100" rx="6"/>
&lt;text x="287" y="72" class="tl">2 · Uncontrolled retries&lt;/text>
&lt;text x="287" y="92" class="ts">retries with no policy&lt;/text>
&lt;text x="287" y="108" class="ts">→ API/compute bill&lt;/text>
&lt;text x="287" y="124" class="ts">with no ceiling&lt;/text>
&lt;rect class="bx" x="530" y="50" width="230" height="100" rx="6"/>
&lt;text x="542" y="72" class="tl">3 · Inflated context&lt;/text>
&lt;text x="542" y="92" class="ts">recovery rebuilds&lt;/text>
&lt;text x="542" y="108" class="ts">the full context&lt;/text>
&lt;text x="542" y="124" class="ts">→ extra tokens per step&lt;/text>
&lt;text x="20" y="185" class="ts">None is the "base" inference cost: they are the costs of running the sequence BADLY. The vector local vLLM does not touch.&lt;/text>
&lt;text x="20" y="205" class="ts">Durable execution attacks all three at once with per-step persisted state.&lt;/text>
&lt;/svg>
&lt;/div>
&lt;h3 id="vector-1-failures-charged-twice">Vector 1: failures charged twice&lt;/h3>
&lt;p>With no persisted state, a failure at &lt;strong>step 15 of 20&lt;/strong> costs you the tokens of the &lt;strong>14 previous
steps&lt;/strong> (which you already paid for) &lt;strong>plus the full retry&lt;/strong> from scratch. The wasted cost of an
agent with no checkpoint, per failure, is:&lt;/p>
$$\text{wasted cost} = \text{tokens of the completed steps} \times \text{cost per token} \times (\text{retries})$$
&lt;p>The longer the sequence and the later it fails, the more expensive: a failure at 75 % of the work
throws away 75 % of the cost. &lt;strong>Durable execution checkpoints every step&lt;/strong>: if the worker dies
halfway through, &lt;strong>another one picks the workflow up exactly where it stopped&lt;/strong>, not from scratch
(&lt;a href="https://temporal.io/">Temporal&lt;/a>). Temporal uses its &lt;strong>Event History&lt;/strong> as the record of past
decisions; on a failure it &lt;strong>replays&lt;/strong> the progress to date and resumes at the exact point
(&lt;a href="https://callsphere.ai/blog/temporal-ai-agent-workflows-durable-execution-workflow-as-code">CallSphere&lt;/a>).
Step 15 fails → step 15 is retried, not the previous 14. The wasted cost goes from &amp;ldquo;the 14 steps&amp;rdquo;
to &amp;ldquo;the step that failed&amp;rdquo;.&lt;/p>
&lt;h3 id="vector-2-uncontrolled-retries">Vector 2: uncontrolled retries&lt;/h3>
&lt;p>Agents that &lt;strong>retry with no policy&lt;/strong> blow up the API or compute bill: a retry loop against a &lt;em>rate
limit&lt;/em> can generate dozens of billable calls for a single task. Durable execution defines &lt;strong>retry
policies per activity&lt;/strong>: &lt;strong>hard limits&lt;/strong> on retries, &lt;strong>exponential backoff&lt;/strong> and &lt;strong>timeouts&lt;/strong> that
&lt;strong>bound the maximum possible cost&lt;/strong> of each call. Instead of &amp;ldquo;retry until it works&amp;rdquo; (unbounded
cost), you define &amp;ldquo;at most N retries with backoff and a timeout of T&amp;rdquo; (bounded cost).&lt;/p>
&lt;p>The state of the art backs this with data: the &lt;strong>ReliabilityBench&lt;/strong> benchmark evaluates agent
reliability under controlled failures and finds that &lt;strong>rate limiting is the most damaging failure&lt;/strong>
of all (&lt;a href="https://arxiv.org/abs/2601.06112">arXiv 2601.06112&lt;/a>). That is, exactly the failure that an
unpolicied retry turns into a cost spiral. Bounding retries is not hygiene, it is the defence
against the failure mode the literature singles out as the worst.&lt;/p>
&lt;h3 id="vector-3-inflated-context-on-recovery">Vector 3: inflated context on recovery&lt;/h3>
&lt;p>With no persisted state, the agent &lt;strong>rebuilds the full context on every recovery&lt;/strong>: it reloads the
whole conversation/task history to &amp;ldquo;remember&amp;rdquo; where it was, paying the tokens of that enormous
context &lt;strong>on every recovery&lt;/strong>. With durable execution the state lives in the workflow history, so
&lt;strong>the next step receives only the context needed for that step&lt;/strong>, not the whole conversation. The
saving is direct: fewer input tokens per step, and no penalty for rebuilding the context after
each failure.&lt;/p>
&lt;hr>
&lt;h2 id="a-worked-example-the-cost-of-one-failure">A worked example: the cost of one failure&lt;/h2>
&lt;p>Let us put numbers on the three vectors. A 20-step agent consuming ~3,000 tokens per step
(reasoning plus tool call), on in-house inference at ~1.09 €/1M tokens, with a failure at
&lt;strong>step 15&lt;/strong> and a failure rate of 10 % of executions:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Scenario&lt;/th>
&lt;th>Tokens recomputed per failure&lt;/th>
&lt;th>Cost per failure&lt;/th>
&lt;th>Over 10,000 executions (10 % fail)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>No state&lt;/strong> (full retry)&lt;/td>
&lt;td>14 steps × 3,000 = 42,000&lt;/td>
&lt;td>~0.046 €&lt;/td>
&lt;td>~46 € wasted&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>With checkpoint&lt;/strong> (resumes at 15)&lt;/td>
&lt;td>0 (only step 15 is retried)&lt;/td>
&lt;td>~0.003 €&lt;/td>
&lt;td>~3 € wasted&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The absolute figure looks small because inference is in-house and cheap; but the &lt;strong>ratio&lt;/strong> is
&lt;strong>~15×&lt;/strong> less waste per failure, and it scales with the length of the sequence and with the cost
per token (were it an external API at 10-30 €/1M, the waste without a checkpoint would be
multiplied by 10-30). And this is only vector 1: add uncontrolled retries (vector 2), which can
turn one failure into dozens of calls, and the context rebuilt on every recovery (vector 3). The
cost of faulty execution &lt;strong>is not the inference cost × something small&lt;/strong>; it is a multiplier that
grows with the agent&amp;rsquo;s complexity, and it is exactly what does not show up in the demo and does
show up in the production bill.&lt;/p>
&lt;hr>
&lt;h2 id="how-temporal-works-workflows-activities-and-determinism">How Temporal works: workflows, activities and determinism&lt;/h2>
&lt;p>To understand why Temporal achieves this, its two primitives:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Workflow&lt;/strong>: the agent&amp;rsquo;s orchestration logic (the sequence of steps, the decisions). It has to
be &lt;strong>deterministic&lt;/strong>: given the same Event History, it produces the same decisions. That is what
allows the execution to be &lt;strong>replayed&lt;/strong> after a failure and to arrive at the same point.&lt;/li>
&lt;li>&lt;strong>Activity&lt;/strong>: every step with side effects (a call to the LLM, to a tool, to an API). Activities
are &lt;strong>not&lt;/strong> deterministic (the LLM is probabilistic), so their result is &lt;strong>persisted&lt;/strong> in the
Event History: on replay they are not executed again, they are read from the history.&lt;/li>
&lt;/ul>
&lt;p>The trick is that separation: the deterministic logic (workflow) is replayed cheaply from the
history; the expensive, non-deterministic steps (activities, the model calls) are persisted and
&lt;strong>not repeated&lt;/strong>. That is why &amp;ldquo;completed work is never repeated&amp;rdquo;: completed activities are read,
not recomputed. And &lt;strong>retry policies are defined per activity&lt;/strong>, so each call to the model or to a
tool has its own retry limit, backoff and timeout, which is the vector 2 cost control at the right
level.&lt;/p>
&lt;hr>
&lt;h2 id="durable-execution-what-it-is-and-the-landscape">Durable execution: what it is and the landscape&lt;/h2>
&lt;p>&lt;strong>Durable execution&lt;/strong> is a programming model that &lt;strong>guarantees the code completes despite
failures&lt;/strong>: every step is checkpointed automatically, and if a worker dies, another one picks the
workflow up where it stopped (&lt;a href="https://temporal.io/">Temporal&lt;/a>). It crossed into the &amp;ldquo;early
majority&amp;rdquo; in 2025, with offerings from AWS, Cloudflare and Vercel, &lt;strong>driven precisely by AI agent
infrastructure&lt;/strong> (&lt;a href="https://www.inngest.com/blog/durable-execution-key-to-harnessing-ai-agents">Inngest&lt;/a>).&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 200" role="img" aria-label="Durable execution: a sequence of checkpointed steps; on a failure it resumes from the last checkpoint, not from scratch" xmlns="http://www.w3.org/2000/svg">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.3}.cp{fill:none;stroke:currentColor;stroke-width:1.3;stroke-dasharray:3 2}.tl{font:600 12px sans-serif;fill:currentColor}.ts{font:11px sans-serif;fill:currentColor}.ar{fill:none;stroke:currentColor;stroke-width:1.3;marker-end:url(#dm)}&lt;/style>
&lt;defs>&lt;marker id="dm" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="currentColor"/>&lt;/marker>&lt;/defs>
&lt;rect class="bx" x="30" y="60" width="80" height="40" rx="5"/>&lt;text x="70" y="84" text-anchor="middle" class="ts">step 1 ✓&lt;/text>
&lt;path class="ar" d="M110,80 L140,80"/>
&lt;rect class="bx" x="140" y="60" width="80" height="40" rx="5"/>&lt;text x="180" y="84" text-anchor="middle" class="ts">step 2 ✓&lt;/text>
&lt;path class="ar" d="M220,80 L250,80"/>
&lt;text x="255" y="55" class="ts">… checkpoint per step (Event History) …&lt;/text>
&lt;rect class="bx" x="430" y="60" width="80" height="40" rx="5"/>&lt;text x="470" y="84" text-anchor="middle" class="ts">step 15 ✗&lt;/text>
&lt;text x="430" y="125" class="ts">failure here&lt;/text>
&lt;path class="cp" d="M470,100 C470,140 400,140 400,108"/>
&lt;text x="300" y="155" class="ts">resumes at step 15, not at step 1&lt;/text>
&lt;rect class="bx" x="360" y="60" width="60" height="40" rx="5"/>&lt;text x="390" y="84" text-anchor="middle" class="ts">14 ✓&lt;/text>
&lt;text x="30" y="185" class="ts">Completed work is never repeated: the cost of a failure is that of the step that failed, not of the whole sequence.&lt;/text>
&lt;/svg>
&lt;/div>
&lt;p>The tooling landscape (the 2026 reference architecture for durable LLM agents): &lt;strong>Temporal&lt;/strong>,
&lt;strong>AWS Step Functions Express&lt;/strong>, &lt;strong>Restate&lt;/strong>, &lt;strong>DBOS&lt;/strong> and &lt;strong>Inngest&lt;/strong> as durable execution
primitives; and the &lt;em>checkpointer&lt;/em> model of &lt;strong>LangGraph&lt;/strong> (PostgresSaver, RedisSaver,
DynamoDBSaver) on the agent framework side (&lt;a href="https://render.com/articles/durable-workflow-platforms-ai-agents-llm-workloads">render.com&lt;/a>).
Temporal stands out for its &lt;em>workflow-as-code&lt;/em> model and its Event History as the source of truth.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Option&lt;/th>
&lt;th>Model&lt;/th>
&lt;th>Self-hosted&lt;/th>
&lt;th>Note for sovereignty&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Temporal&lt;/strong>&lt;/td>
&lt;td>workflow-as-code, Event History&lt;/td>
&lt;td>yes (open source)&lt;/td>
&lt;td>the most mature; full on-prem&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Restate&lt;/strong>&lt;/td>
&lt;td>durable RPC/handlers&lt;/td>
&lt;td>yes&lt;/td>
&lt;td>lightweight, modern&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>DBOS&lt;/strong>&lt;/td>
&lt;td>durable on top of Postgres&lt;/td>
&lt;td>yes&lt;/td>
&lt;td>state in your own Postgres&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Inngest&lt;/strong>&lt;/td>
&lt;td>event-driven, steps&lt;/td>
&lt;td>SaaS / self-host&lt;/td>
&lt;td>focus on DX&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>AWS Step Functions Express&lt;/strong>&lt;/td>
&lt;td>state machine&lt;/td>
&lt;td>no (AWS SaaS)&lt;/td>
&lt;td>tied to AWS, not sovereign&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>LangGraph checkpointer&lt;/strong>&lt;/td>
&lt;td>framework checkpointer&lt;/td>
&lt;td>yes (your backend)&lt;/td>
&lt;td>at framework level, not platform level&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>For a sovereign platform, the ones that are &lt;strong>self-hosted&lt;/strong> (Temporal, Restate, DBOS) keep state in
your cluster; the SaaS ones (Step Functions) move execution into someone else&amp;rsquo;s jurisdiction.
Temporal is the choice for maturity and for its complete on-prem deployment.&lt;/p>
&lt;hr>
&lt;h2 id="the-cost-of-durable-execution-itself">The cost of durable execution itself&lt;/h2>
&lt;p>Honesty about the data: durable execution &lt;strong>is not free&lt;/strong>. It has two costs to watch:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Execution overhead&lt;/strong>: persisting state and replaying history adds latency and compute. The
reference architectures put it at around &lt;strong>5-20 % overhead&lt;/strong> on a load of 100,000
executions/day (&lt;a href="https://render.com/articles/durable-workflow-platforms-ai-agents-llm-workloads">render.com&lt;/a>).&lt;/li>
&lt;li>&lt;strong>Price per step&lt;/strong>: in the offerings with &lt;strong>per-step pricing&lt;/strong>, orchestrating many model calls,
&lt;em>rate limit&lt;/em> retries and complex interactions generates &lt;strong>a large number of billable steps&lt;/strong>, and
the cost can run away (&lt;a href="https://render.com/articles/durable-workflow-platforms-ai-agents-llm-workloads">render.com&lt;/a>).&lt;/li>
&lt;/ol>
&lt;p>The conclusion for a sovereign platform: &lt;strong>self-host the durable execution engine&lt;/strong> (Temporal is
open source and deploys on-prem) and the SaaS per-step price disappears, leaving only the execution
overhead, a 5-20 % that is paid back many times over by what it saves on the three vectors above.
The arithmetic: if a failure halfway through a sequence used to cost you 50-75 % of the recomputed
cost, a 5-20 % overhead is a bargain.&lt;/p>
&lt;hr>
&lt;h2 id="observability-from-manual-post-mortem-to-real-time-traceability">Observability: from manual post-mortem to real-time traceability&lt;/h2>
&lt;p>The fourth element of the equation: the &lt;strong>observability layer&lt;/strong>. The integration with &lt;strong>Braintrust&lt;/strong>
and the &lt;strong>complete history of every execution&lt;/strong> (Temporal&amp;rsquo;s Event History) turn cost analysis from a
&lt;strong>manual post-mortem&lt;/strong>, reconstructing by hand what happened and what it cost after a failure, into
&lt;strong>real-time traceability&lt;/strong>: every execution is recorded step by step, with its tokens, its retries
and its cost, ready to analyse. This closes the FinOps loop of the cost-per-token article, but at
the &lt;strong>agent&lt;/strong> level: not just how much a token costs, but how much an &lt;strong>execution&lt;/strong> costs, and
where it is wasted.&lt;/p>
&lt;hr>
&lt;h2 id="the-complete-equation">The complete equation&lt;/h2>
&lt;p>Putting the pieces together, the cost architecture of a sovereign agent:&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 190" role="img" aria-label="The equation: FastMCP plus local vLLM eliminates the inference cost; Temporal eliminates the faulty execution cost; they are complementary" xmlns="http://www.w3.org/2000/svg">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.3}.tl{font:600 12px sans-serif;fill:currentColor}.ts{font:11px sans-serif;fill:currentColor}.op{font:600 18px sans-serif;fill:currentColor}&lt;/style>
&lt;rect class="bx" x="20" y="50" width="230" height="70" rx="6"/>
&lt;text x="32" y="74" class="tl">FastMCP + local vLLM&lt;/text>
&lt;text x="32" y="94" class="ts">eliminates the cost&lt;/text>
&lt;text x="32" y="110" class="ts">of INFERENCE (tokens)&lt;/text>
&lt;text x="288" y="92" text-anchor="middle" class="op">+&lt;/text>
&lt;rect class="bx" x="320" y="50" width="230" height="70" rx="6"/>
&lt;text x="332" y="74" class="tl">Temporal (durable exec.)&lt;/text>
&lt;text x="332" y="94" class="ts">eliminates the cost of&lt;/text>
&lt;text x="332" y="110" class="ts">FAULTY EXECUTION&lt;/text>
&lt;text x="578" y="92" text-anchor="middle" class="op">=&lt;/text>
&lt;rect class="bx" x="600" y="50" width="160" height="70" rx="6"/>
&lt;text x="612" y="74" class="tl">Sustainable agent&lt;/text>
&lt;text x="612" y="94" class="ts">bounded cost,&lt;/text>
&lt;text x="612" y="110" class="ts">sovereign, traceable&lt;/text>
&lt;text x="20" y="155" class="ts">They are COMPLEMENTARY: they attack different vectors. The 40% of projects Gartner writes off dies by ignoring the second.&lt;/text>
&lt;text x="20" y="175" class="ts">+ observability (Braintrust / Event History) = real-time cost traceability.&lt;/text>
&lt;/svg>
&lt;/div>
&lt;p>&lt;strong>FastMCP + vLLM running locally eliminates the cost of inference. Temporal eliminates the cost of
faulty execution. They are complementary&lt;/strong>, attacking different vectors of the same problem.
Whoever solves only the first (serves cheaply but executes badly) keeps bleeding through the
second; whoever solves only the second (executes robustly but pays for the API) keeps bleeding
through the first. A sustainable platform needs both.&lt;/p>
&lt;hr>
&lt;h2 id="state-of-the-art-and-papers">State of the art and papers&lt;/h2>
&lt;p>The reader asked for papers, and there are some: agent reliability and cost is an active research
area in 2026.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Paper&lt;/th>
&lt;th>Contribution&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>ReliabilityBench&lt;/strong> (&lt;a href="https://arxiv.org/abs/2601.06112">2601.06112&lt;/a>)&lt;/td>
&lt;td>an agent reliability benchmark: consistency, robustness and tolerance to tool/API failures; &lt;strong>rate limiting is the most damaging failure&lt;/strong>; ReAct more robust than Reflexion under stress&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Cost-Efficient LLM Agents&lt;/strong> (&lt;a href="https://arxiv.org/pdf/2603.01548">arXiv&lt;/a>)&lt;/td>
&lt;td>the &lt;strong>reliability-cost trade-off&lt;/strong>: routing every decision through the LLM improves accuracy but blows up cost; pre-coded graphs are cheaper but brittle against compound failures&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Multi-agent Byzantine Fault Tolerance&lt;/strong> (&lt;a href="https://arxiv.org/abs/2511.10400">2511.10400&lt;/a>)&lt;/td>
&lt;td>BFT consensus (CP-WBFT) to stabilise multi-agent systems under extreme failure rates (85.7 %)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>ACRFence&lt;/strong> (&lt;a href="https://arxiv.org/pdf/2603.20625">2603.20625&lt;/a>)&lt;/td>
&lt;td>security of agent &lt;strong>checkpoint-restore&lt;/strong>: preventing semantic &lt;em>rollback&lt;/em> attacks&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>The Six Sigma Agent&lt;/strong> (&lt;a href="https://arxiv.org/html/2601.22290">2601.22290&lt;/a>)&lt;/td>
&lt;td>enterprise reliability via consensus-driven decomposed execution&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Two readings for the proposal. First, &lt;strong>the reliability-cost trade-off is real and is
characterised&lt;/strong>: you cannot maximise accuracy ignoring cost, or the other way round; durable
execution shifts the frontier by &lt;strong>making reliability cheaper&lt;/strong> (checkpoint plus bounded retry
instead of recomputing). Second, &lt;strong>agent checkpoint-restore has its own security surface&lt;/strong>
(ACRFence): persisting state introduces &lt;em>rollback&lt;/em> risks that have to be controlled — durability is
not only a cost decision, it is a security one too.&lt;/p>
&lt;hr>
&lt;h2 id="the-sovereign-angle">The sovereign angle&lt;/h2>
&lt;p>For a European sovereign platform the combination fits perfectly and is &lt;strong>entirely on-prem&lt;/strong>:
&lt;strong>vLLM&lt;/strong> serves the models (no API cost and no sovereignty given away), &lt;strong>FastMCP&lt;/strong> exposes the
tools, and &lt;strong>self-hosted Temporal&lt;/strong> orchestrates with durability (no SaaS per-step price and no
execution data leaving for a foreign jurisdiction). The execution history, which describes in
detail what your agent does and with which data, &lt;strong>stays in your cluster&lt;/strong>, just like the
inferences. Against a stack of agents on external APIs plus a US SaaS orchestrator, the sovereign
stack removes both cost vectors &lt;strong>and&lt;/strong> keeps the data under EU jurisdiction. It is the same
argument as the rest of the series, applied to the agent layer: bounded, measured and sovereign
cost.&lt;/p>
&lt;hr>
&lt;h2 id="fastmcp-and-the-tool-layer">FastMCP and the tool layer&lt;/h2>
&lt;p>The other component of the equation is &lt;strong>FastMCP&lt;/strong>, the layer that exposes tools to the agent via
&lt;strong>MCP&lt;/strong> (Model Context Protocol). The agent does not call the APIs directly: it calls &lt;strong>MCP
servers&lt;/strong> that encapsulate each tool (a database, a search engine, an internal system) with a
uniform contract. The connection with cost and durability: every call to an MCP tool is a Temporal
&lt;strong>activity&lt;/strong>, so it inherits its retry policy, its timeout and its persistence. That is, FastMCP
defines &lt;strong>which&lt;/strong> tools exist and Temporal governs &lt;strong>how&lt;/strong> they are executed with durability and
bounded cost. And because the MCP servers are your own, the tools and the data they touch &lt;strong>stay
on-prem&lt;/strong>, never exposed to an external provider. The triad &lt;strong>vLLM (model) + FastMCP (tools) +
Temporal (orchestration)&lt;/strong> is a fully sovereign agent stack: the model, the tools and the
execution, all under your control and with the cost of both vectors bounded.&lt;/p>
&lt;hr>
&lt;h2 id="patterns-human-in-the-loop-and-long-sequences">Patterns: human-in-the-loop and long sequences&lt;/h2>
&lt;p>Where durable execution shines most, by agent pattern:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Human-in-the-loop (HITL)&lt;/strong>: human approval patterns, essential for an agent&amp;rsquo;s safety, map
directly to the &lt;strong>suspend/resume&lt;/strong> primitives of durable execution, allowing workflows that
&lt;strong>pause for hours or days waiting for an approval without losing state&lt;/strong>
(&lt;a href="https://www.inngest.com/blog/durable-execution-key-to-harnessing-ai-agents">Inngest&lt;/a>). Without
durability, keeping an agent &amp;ldquo;waiting&amp;rdquo; consumes resources or loses the context; with it, the
workflow sleeps at no cost and wakes up with all its state.&lt;/li>
&lt;li>&lt;strong>Long sequences&lt;/strong>: the longer the chain of steps, the greater the checkpoint&amp;rsquo;s saving (a late
failure throws away more work). Multi-step agents with tools are the canonical use case.&lt;/li>
&lt;li>&lt;strong>Multi-agent&lt;/strong>: when several agents collaborate, reliability gets complicated (the BFT papers
study this); durable execution provides the consistent state substrate on which to build that
coordination.&lt;/li>
&lt;/ul>
&lt;p>The pattern that does &lt;strong>not&lt;/strong> need it: a single-step, stateless call (a classifier, a simple
extraction). There the durability overhead buys nothing. The rule: durable execution for &lt;strong>stateful
sequences that fail halfway through&lt;/strong>; for everything else, a direct call.&lt;/p>
&lt;hr>
&lt;h2 id="how-to-measure-the-second-vector">How to measure the second vector&lt;/h2>
&lt;p>To manage the cost of faulty execution you have to measure it, and with the Event History plus
observability (Braintrust) the KPIs are within reach:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>KPI&lt;/th>
&lt;th>What it indicates&lt;/th>
&lt;th>Where it comes from&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Tokens wasted per failure&lt;/strong>&lt;/td>
&lt;td>the cost of vector 1&lt;/td>
&lt;td>tokens recomputed after a failure&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Retries per activity&lt;/strong>&lt;/td>
&lt;td>the cost of vector 2&lt;/td>
&lt;td>retry count from the Event History&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Context size on recovery&lt;/strong>&lt;/td>
&lt;td>the cost of vector 3&lt;/td>
&lt;td>input tokens after a recovery&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Failure rate per workflow&lt;/strong>&lt;/td>
&lt;td>how often the problem occurs&lt;/td>
&lt;td>failed executions / total&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Cost per complete execution&lt;/strong>&lt;/td>
&lt;td>the business figure&lt;/td>
&lt;td>sum of tokens × cost/token per run&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The last one closes the circle with cost per token: not just &amp;ldquo;how much a token costs&amp;rdquo;, but &amp;ldquo;how
much an &lt;strong>agent execution&lt;/strong> costs, and how much of that was useful work versus waste&amp;rdquo;. Without
these KPIs the second vector is invisible, and what is invisible does not get optimised, it gets
cancelled when the bill surprises you. With them, the cost of every execution is a real-time
figure, and the decision to make each workflow durable (or not) is taken with numbers.&lt;/p>
&lt;hr>
&lt;h2 id="limits-and-traps-data-driven">Limits and traps (data-driven)&lt;/h2>
&lt;ol>
&lt;li>&lt;strong>Durability is not free.&lt;/strong> 5-20 % overhead; on SaaS with per-step pricing it can run away.
Self-host Temporal to remove that price.&lt;/li>
&lt;li>&lt;strong>Not every agent needs it.&lt;/strong> A single-step, stateless task gains nothing from durable
execution; the value is in the &lt;strong>long sequences&lt;/strong> that fail halfway through.&lt;/li>
&lt;li>&lt;strong>Bounded retries can hide real failures.&lt;/strong> A retry limit avoids the cost spiral, but you have
to &lt;strong>alert&lt;/strong> when it is exhausted, not swallow the failure in silence.&lt;/li>
&lt;li>&lt;strong>Checkpoint = security surface.&lt;/strong> Persisting state opens the door to rollback attacks
(ACRFence); protect it.&lt;/li>
&lt;li>&lt;strong>The first vector still exists.&lt;/strong> Temporal does not lower the inference cost; you need local
vLLM for that. They are complementary, not substitutes.&lt;/li>
&lt;/ol>
&lt;p>The conclusion the data and the papers support: the cost of agentic AI has &lt;strong>two vectors&lt;/strong>, and the
industry has obsessed over the first (inference) while ignoring the second (faulty execution),
which is precisely where Gartner locates the cause of 40 % of cancellations. &lt;strong>FastMCP + vLLM&lt;/strong>
solves the first; &lt;strong>Temporal&lt;/strong> the second. A sovereign and sustainable agent platform needs to
attack both, with observability that turns the cost of each execution into a real-time figure
rather than an autopsy.&lt;/p>
&lt;h2 id="closing">Closing&lt;/h2>
&lt;p>The dominant narrative about agent cost is half-lame: it treats the inference cost as if it were
the only one, when it is only the one you see in the demo. The one that kills projects, the one
Gartner files under &amp;ldquo;rising costs&amp;rdquo; inside its 40 % of cancellations, is the &lt;strong>cost of faulty
execution&lt;/strong>: failures charged twice, retries with no ceiling and the context rebuilt on every
recovery. The engineering that attacks it is not a prompt trick or a better model, it is an
&lt;strong>execution model&lt;/strong>: durable execution, which checkpoints every step, bounds every retry and passes
only the necessary context. Temporal, open source and self-hosted, does this while keeping state in
your cluster, and combined with vLLM (model) and FastMCP (tools) it composes a &lt;strong>fully sovereign&lt;/strong>
agent stack where both cost vectors are bounded and the data never leaves the EU. The state of the
art and the papers confirm the problem is real and characterisable: the reliability-cost trade-off,
rate limiting as the worst failure, checkpoint-restore as a new security surface. For anyone who
wants to be in the 60 % of agentic projects that are &lt;strong>not&lt;/strong> cancelled, the recipe is the one in
this article: solve both vectors, not one, and measure the second as seriously as the first.&lt;/p>
&lt;h2 id="sources">Sources&lt;/h2>
&lt;ul>
&lt;li>Gartner · más del 40 % de proyectos de IA agéntica cancelados antes de 2027 — &lt;a href="https://www.gartner.com/en/newsroom/press-releases/2025-06-25-gartner-predicts-over-40-percent-of-agentic-ai-projects-will-be-canceled-by-end-of-2027">https://www.gartner.com/en/newsroom/press-releases/2025-06-25-gartner-predicts-over-40-percent-of-agentic-ai-projects-will-be-canceled-by-end-of-2027&lt;/a>&lt;/li>
&lt;li>Temporal · durable execution — &lt;a href="https://temporal.io/">https://temporal.io/&lt;/a>&lt;/li>
&lt;li>CallSphere · Temporal para workflows de agentes (Event History, replay) — &lt;a href="https://callsphere.ai/blog/temporal-ai-agent-workflows-durable-execution-workflow-as-code">https://callsphere.ai/blog/temporal-ai-agent-workflows-durable-execution-workflow-as-code&lt;/a>&lt;/li>
&lt;li>Inngest · durable execution, clave para agentes en producción — &lt;a href="https://www.inngest.com/blog/durable-execution-key-to-harnessing-ai-agents">https://www.inngest.com/blog/durable-execution-key-to-harnessing-ai-agents&lt;/a>&lt;/li>
&lt;li>render.com · plataformas de workflow durables para agentes/LLM (overhead, precio por paso) — &lt;a href="https://render.com/articles/durable-workflow-platforms-ai-agents-llm-workloads">https://render.com/articles/durable-workflow-platforms-ai-agents-llm-workloads&lt;/a>&lt;/li>
&lt;li>ReliabilityBench (arXiv 2601.06112) — &lt;a href="https://arxiv.org/abs/2601.06112">https://arxiv.org/abs/2601.06112&lt;/a>&lt;/li>
&lt;li>Cost-Efficient LLM Agents (arXiv 2603.01548) — &lt;a href="https://arxiv.org/pdf/2603.01548">https://arxiv.org/pdf/2603.01548&lt;/a>&lt;/li>
&lt;li>Byzantine Fault Tolerance multi-agente (arXiv 2511.10400) — &lt;a href="https://arxiv.org/abs/2511.10400">https://arxiv.org/abs/2511.10400&lt;/a>&lt;/li>
&lt;li>ACRFence · checkpoint-restore de agentes (arXiv 2603.20625) — &lt;a href="https://arxiv.org/pdf/2603.20625">https://arxiv.org/pdf/2603.20625&lt;/a>&lt;/li>
&lt;li>The Six Sigma Agent (arXiv 2601.22290) — &lt;a href="https://arxiv.org/html/2601.22290">https://arxiv.org/html/2601.22290&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>From GPU-hour to cost per token: the metric that compares on-prem and cloud</title><link>https://blog.lo0.es/en/posts/gpu-hour-to-cost-per-token-on-prem-vs-cloud/</link><pubDate>Sun, 14 Jun 2026 03:30:00 +0200</pubDate><guid>https://blog.lo0.es/en/posts/gpu-hour-to-cost-per-token-on-prem-vs-cloud/</guid><description>&lt;blockquote>
&lt;p>Notation: amounts in &lt;strong>euros (N €)&lt;/strong>, decimals with a point. The dollar sign is not used
(on this site it is a formula delimiter).&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-this-article-covers">What this article covers&lt;/h2>
&lt;p>Fourth article in the FinOps track (A4), and the one that closes out the metric that gives
the whole pillar its meaning: &lt;strong>cost per token&lt;/strong>. In A2 we saw how OpenCost assigns the cost of
the iron (the pod, in €/hour); in A3, which tool to use. But the cost of the pod is not the
metric that compares on-prem with cloud, nor the one the business understands: that one is
&lt;strong>cost per token&lt;/strong> (or per request). This article explains how it is really calculated (spoiler:
for your own inference it &lt;strong>is not a list price, it is €/GPU-hour divided by throughput&lt;/strong>), how
to instrument it with a gateway (LiteLLM), how to attribute it by team and product, and how
to compare it against European cloud. No recommendations; only the mechanics and the numbers.&lt;/p>
&lt;hr>
&lt;h2 id="the-identity-from-gpu-hour-to-token">The identity: from GPU-hour to token&lt;/h2>
&lt;p>Cost per million tokens (CPM) is the cost of the iron divided by what it produces:&lt;/p>
$$\text{cost per token} = \frac{\text{cost of the iron (€/h)}}{\text{throughput (tok/s)} \times 3600 / 10^6}$$
&lt;p>This is the central formula of the FinOps track, and it has a consequence many people never quite
internalise: for a self-hosted model, the cost of a token &lt;strong>does not exist as a fixed
price&lt;/strong>. It depends on two things that change: the cost of the iron (from OpenCost, in
€/hour) and the throughput (from your benchmark, in tok/s). The same model on the same node
costs half as much per token if you double the throughput with an optimisation, because the
denominator changes, not the numerator. The cost per token of your own inference is &lt;strong>a function
of your efficiency&lt;/strong>, not a tariff.&lt;/p>
&lt;p>That sets it radically apart from an external API, where the price per token really is a
fixed tariff the provider charges you. Comparing the two requires turning your variable cost
into a number, and that is where the iron (A2) meets the tokens (this article).&lt;/p>
&lt;hr>
&lt;h2 id="the-two-halves-of-cost-per-token">The two halves of cost per token&lt;/h2>
&lt;p>Cost per token comes from joining two measurements that live in different places:&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 220" role="img" aria-label="The two halves of cost per token: the cost of the iron from OpenCost and the token count from the gateway, which divided give the cost per token" xmlns="http://www.w3.org/2000/svg">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.3}.dsh{fill:none;stroke:currentColor;stroke-width:1.3;stroke-dasharray:5 3}.tl{font:600 12px sans-serif;fill:currentColor}.ts{font:11px sans-serif;fill:currentColor}.ar{fill:none;stroke:currentColor;stroke-width:1.3;marker-end:url(#tm)}&lt;/style>
&lt;defs>&lt;marker id="tm" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="currentColor"/>&lt;/marker>&lt;/defs>
&lt;rect class="bx" x="20" y="40" width="220" height="56" rx="6"/>
&lt;text x="32" y="62" class="tl">Cost of the iron (OpenCost)&lt;/text>
&lt;text x="32" y="80" class="ts">€/hour of the vLLM pod (by usage)&lt;/text>
&lt;rect class="bx" x="20" y="120" width="220" height="56" rx="6"/>
&lt;text x="32" y="142" class="tl">Tokens (LiteLLM gateway)&lt;/text>
&lt;text x="32" y="160" class="ts">tokens/s per model and team&lt;/text>
&lt;path class="ar" d="M240,68 L300,100"/>
&lt;path class="ar" d="M240,148 L300,116"/>
&lt;rect class="bx" x="300" y="84" width="200" height="48" rx="6"/>
&lt;text x="312" y="106" class="tl">÷ → cost per token&lt;/text>
&lt;text x="312" y="122" class="ts">€/1M tok per team&lt;/text>
&lt;path class="ar" d="M500,108 L560,108"/>
&lt;rect class="dsh" x="560" y="84" width="200" height="48" rx="6"/>
&lt;text x="572" y="106" class="tl">compare / chargeback&lt;/text>
&lt;text x="572" y="122" class="ts">on-prem vs cloud · per team&lt;/text>
&lt;text x="20" y="205" class="ts">Without the iron (OpenCost) you would know the tokens but not their cost; without the gateway, the cost of the pod but not the tokens. You need both.&lt;/text>
&lt;/svg>
&lt;/div>
&lt;p>The iron half comes from OpenCost (article A2): the by-usage cost of the vLLM pod, in
€/hour. The token half comes from the gateway, which intercepts every request and counts
tokens per model and per team. Divide, and out comes the cost per token and, crucially, &lt;strong>per
team&lt;/strong>, which is what enables chargeback.&lt;/p>
&lt;hr>
&lt;h2 id="the-gateway-litellm-in-depth">The gateway: LiteLLM in depth&lt;/h2>
&lt;p>The gateway sits between the application and the engine (vLLM) or the provider, and intercepts
every request. LiteLLM is the OSS reference: it proxies over 100 models and &lt;strong>tracks spend
by keys, users and teams&lt;/strong>, logging spend for all known models automatically
(&lt;a href="https://docs.litellm.ai/docs/proxy/cost_tracking">LiteLLM · Spend Tracking&lt;/a>).&lt;/p>
&lt;p>Its key mechanisms:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Mechanism&lt;/th>
&lt;th>What it does&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>response_cost&lt;/code>&lt;/td>
&lt;td>the cost of each response, available in logging (&lt;code>kwargs[&amp;quot;response_cost&amp;quot;]&lt;/code>)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Spend logs&lt;/strong>&lt;/td>
&lt;td>one row per request with tokens, model, user, key and cost (SQLite/PostgreSQL)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Tags&lt;/strong>&lt;/td>
&lt;td>tag each request by team, project, environment and model &lt;em>tier&lt;/em>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Budgets&lt;/strong>&lt;/td>
&lt;td>budgets and rate limits per team or user&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Custom callbacks&lt;/strong>&lt;/td>
&lt;td>bespoke loggers for key/user/model/tokens/cost&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>With the spend logs table (via the database adapter) you have the detail of every request to
build cost dashboards (&lt;a href="https://docs.litellm.ai/docs/proxy/logging">LiteLLM · Logging&lt;/a>), and you
can tag each request with team/project/environment for per-team analysis, with notifications
(Slack) and hard caps for keys that must not exceed a limit. In practice it is the platform&amp;rsquo;s
token meter and cost allocator.&lt;/p>
&lt;hr>
&lt;h2 id="the-self-hosted-trap-litellm-does-not-know-what-your-own-token-costs-you">The self-hosted trap: LiteLLM does not know what your own token costs you&lt;/h2>
&lt;p>Here is the point almost nobody wires up correctly. LiteLLM tracks cost using list prices
for known models (the API ones: OpenAI, Anthropic and so on). But for a model self-hosted on
your vLLM there &lt;strong>is no list price&lt;/strong>, because you pay nobody per token. If you tell it
nothing, LiteLLM does not know the real cost of your own token. The solution is custom
pricing: declaring to LiteLLM the cost per token of your self-hosted model
(&lt;a href="https://docs.litellm.ai/docs/proxy/custom_pricing">LiteLLM · Custom Pricing&lt;/a>).&lt;/p>
&lt;p>And where does that number come from? From the identity above: the cost of the iron
(OpenCost) divided by the throughput (your benchmark). That is, the correct flow is:&lt;/p>
&lt;ol>
&lt;li>OpenCost gives you the cost of the vLLM pod: ~11 €/hour (A2).&lt;/li>
&lt;li>Your benchmark gives you the throughput: ~2,800 tok/s (track B).&lt;/li>
&lt;li>The identity gives the cost per token: 11 ÷ (2,800 × 3,600 / 10⁶) ≈ &lt;strong>1.09 €/1M tok&lt;/strong>.&lt;/li>
&lt;li>That 1.09 €/1M goes into LiteLLM as custom pricing.&lt;/li>
&lt;/ol>
&lt;p>Only then does the &lt;code>response_cost&lt;/code> of each request reflect the real cost of your inference,
rather than a zero or somebody else&amp;rsquo;s list price. Without this step, your LiteLLM cost dashboards
lie about your own models. It is the explicit junction of the three tracks: iron (FinOps),
throughput (benchmarking) and measurement (gateway).&lt;/p>
&lt;hr>
&lt;h3 id="configuring-custom-pricing-an-example">Configuring custom pricing: an example&lt;/h3>
&lt;p>In LiteLLM, the real cost of a self-hosted model is declared in its configuration. An example
with the cost per token derived from the identity (1.09 €/1M ÷ 10⁶ = 0.00000109 €/token):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># LiteLLM config.yaml — real cost of a self-hosted model&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">model_list&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">model_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">llama-3.1-70b-onprem&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">litellm_params&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">model&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">openai/llama-3.1-70b &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># your vLLM endpoint&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">api_base&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">http://vllm:8000/v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">input_cost_per_token&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0.00000109&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># €/token (from OpenCost ÷ throughput)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">output_cost_per_token&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0.00000109&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># refine if you separate prefill/decode&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>With that, every &lt;code>response_cost&lt;/code> LiteLLM records reflects the real cost of your iron. The
operational note: this number must be updated when the cost of the node changes (new
depreciation, a different energy price) or the throughput does (an engine optimisation). It is
not a constant; it is the output of OpenCost and of the benchmark, and it is recalculated
whenever either of the two changes. Automating that recalculation, from node cost and measured
throughput to custom pricing, is what keeps cost dashboards honest.&lt;/p>
&lt;hr>
&lt;h2 id="cost-per-token-changes-with-throughput-read-it-off-the-sweep">Cost per token changes with throughput: read it off the sweep&lt;/h2>
&lt;p>Because throughput is the denominator, cost per token is not a point, it is a curve over the
concurrency sweep. On the ~11 €/hour node:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Concurrency&lt;/th>
&lt;th>Throughput (tok/s)&lt;/th>
&lt;th>Cost per 1M tokens&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>1&lt;/td>
&lt;td>350&lt;/td>
&lt;td>&lt;strong>~8.73 €&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>8&lt;/td>
&lt;td>2,100&lt;/td>
&lt;td>&lt;strong>~1.46 €&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>16&lt;/td>
&lt;td>3,330 (goodput)&lt;/td>
&lt;td>&lt;strong>~0.92 €&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>24&lt;/td>
&lt;td>3,900 raw / 2,420 goodput&lt;/td>
&lt;td>~0.78 € raw / &lt;strong>~1.26 € on goodput&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Important readings: at low concurrency the cost per token is sky-high (the fixed cost of the
node is shared among few tokens); cost falls as occupancy rises. But watch the last row: if you
compute cost on raw throughput (3,900) you get 0.78 €, whereas if you compute it on
goodput (2,420, the part that meets the SLO) you get 1.26 €, and the latter is the real
cost, because requests that violate the SLO do not count as useful production. An honest cost
per token is computed on &lt;strong>goodput&lt;/strong>, not on catalogue throughput. This is where the cost track
meets the benchmarking one: the correct denominator is goodput.&lt;/p>
&lt;hr>
&lt;h2 id="cost-per-request-not-only-per-token">Cost per request: not only per token&lt;/h2>
&lt;p>Cost per token is the unit; cost per request is what a complete request bills. A request
consumes &lt;strong>input tokens (prompt) plus output tokens (generation)&lt;/strong>, and both count:&lt;/p>
$$\text{cost per request} = (\text{input tokens} + \text{output tokens}) \times \text{cost per token}$$
&lt;p>With nuances: in many engines the prefill (input) and the decode (output) have different
costs per token (decode is memory-bound and more expensive per token), so a fine-grained cost
model distinguishes the two. For most cases a weighted average cost per token will do; to
optimise, it pays to separate prefill and decode. LiteLLM records input and output tokens
separately in its spend logs, so the data is there to refine with.&lt;/p>
&lt;p>Cost per request is what connects to the product: if a feature consumes 1,500 tokens on
average (1,200 of prompt plus 300 of output) and the cost is 1.09 €/1M, each use costs
~0.0016 €, the basis for setting margin, as seen in the FinOps introduction.&lt;/p>
&lt;hr>
&lt;h2 id="cost-per-model-and-per-tier">Cost per model and per tier&lt;/h2>
&lt;p>A platform does not serve a single model. Cost per token varies by model and by
configuration, and the gateway tells them apart. An 8B and a 70B on the same node have very
different costs per token: the 8B yields far more throughput (more tokens/s) for the same
GPU-hour, so its cost per token is a fraction of the 70B&amp;rsquo;s. The design consequence: routing
each request to the cheapest model that meets the quality bar is a direct cost lever, since
serving a 70B where an 8B suffices means overpaying per token.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model (example, same node)&lt;/th>
&lt;th>Relative throughput&lt;/th>
&lt;th>Relative cost per token&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>8B&lt;/td>
&lt;td>high&lt;/td>
&lt;td>low&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>70B FP8&lt;/td>
&lt;td>medium&lt;/td>
&lt;td>medium&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>70B FP16&lt;/td>
&lt;td>low&lt;/td>
&lt;td>high&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The gateway, with its per-model custom pricing, makes this difference visible and enables
&lt;strong>cost-based routing&lt;/strong>: classify the request and send it to the appropriate &lt;em>tier&lt;/em>. It connects
with the L7 router of the serving track: the gateway not only measures cost, it can decide
on the basis of it. And quantisation (FP16→FP8) shows up again as a lever: the same model in FP8
raises throughput and lowers cost per token, moving the figure without changing model.&lt;/p>
&lt;hr>
&lt;h2 id="attribution-from-cost-to-team-and-product">Attribution: from cost to team and product&lt;/h2>
&lt;p>The reason for putting a gateway in place is not only to measure total cost, it is to share it
out. By tagging each request with team, product and environment, LiteLLM builds the split by
business dimension:&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 200" role="img" aria-label="Cost per token attribution: the gateway tags each request by team and product, aggregates spend and produces chargeback with budgets and caps" xmlns="http://www.w3.org/2000/svg">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.3}.dsh{fill:none;stroke:currentColor;stroke-width:1.3;stroke-dasharray:5 3}.tl{font:600 12px sans-serif;fill:currentColor}.ts{font:11px sans-serif;fill:currentColor}.ar{fill:none;stroke:currentColor;stroke-width:1.3;marker-end:url(#am)}&lt;/style>
&lt;defs>&lt;marker id="am" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="currentColor"/>&lt;/marker>&lt;/defs>
&lt;rect class="bx" x="20" y="50" width="150" height="44" rx="6"/>&lt;text x="32" y="70" class="tl">request + tags&lt;/text>&lt;text x="32" y="86" class="ts">team · product&lt;/text>
&lt;path class="ar" d="M170,72 L215,72"/>
&lt;rect class="bx" x="215" y="50" width="160" height="44" rx="6"/>&lt;text x="227" y="70" class="tl">spend logs&lt;/text>&lt;text x="227" y="86" class="ts">tokens · cost · key&lt;/text>
&lt;path class="ar" d="M375,72 L420,72"/>
&lt;rect class="bx" x="420" y="50" width="160" height="44" rx="6"/>&lt;text x="432" y="70" class="tl">aggregation&lt;/text>&lt;text x="432" y="86" class="ts">€/team · €/product&lt;/text>
&lt;path class="ar" d="M580,72 L625,72"/>
&lt;rect class="bx" x="625" y="50" width="135" height="44" rx="6"/>&lt;text x="637" y="70" class="tl">chargeback&lt;/text>&lt;text x="637" y="86" class="ts">+ budgets&lt;/text>
&lt;text x="20" y="135" class="ts">Hard caps per key and budget alerts (Slack) close the loop: measure → attribute → govern.&lt;/text>
&lt;text x="20" y="160" class="ts">This is the token side of chargeback; the iron side comes from OpenCost. Together: cost per token per team.&lt;/text>
&lt;/svg>
&lt;/div>
&lt;p>With per-team budgets and &lt;strong>hard caps&lt;/strong> per key, the gateway does not only report: it
governs. A team that goes over budget gets an alert; a key with a hard cap cannot spend any
more. It is the FinOps &lt;em>Operate&lt;/em> phase applied to the token.&lt;/p>
&lt;h3 id="a-combined-report-example">A combined report example&lt;/h3>
&lt;p>Joining the iron (OpenCost) and the tokens (gateway), the monthly cost-per-token-per-team report
on the example node:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Team&lt;/th>
&lt;th>Tokens/month&lt;/th>
&lt;th>Applied cost per token&lt;/th>
&lt;th>Charged cost&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>A · chat-prod&lt;/td>
&lt;td>240M&lt;/td>
&lt;td>1.09 €/1M (70B FP8)&lt;/td>
&lt;td>~262 €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>B · batch&lt;/td>
&lt;td>90M&lt;/td>
&lt;td>0.40 €/1M (8B)&lt;/td>
&lt;td>~36 €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>C · experimentation&lt;/td>
&lt;td>15M&lt;/td>
&lt;td>1.09 €/1M (70B)&lt;/td>
&lt;td>~16 € + idle&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>What it reveals: A consumes a lot but at an efficient cost (FP8, high utilisation); B uses a
cheap model (8B) for its workload; C consumes little but, as we saw in A2, drags along the idle
cost of its underused GPU. The token report plus the OpenCost iron report give the full
picture: who spends, on which model, with what efficiency. It is the chargeback that neither
half would produce alone. And the per-team figure is what gets taken into the budget
conversation: not &amp;ldquo;the cluster costs X&amp;rdquo;, but &amp;ldquo;your team consumed Y million tokens at Z €/million&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="governing-spend-budgets-caps-and-alerts">Governing spend: budgets, caps and alerts&lt;/h2>
&lt;p>Measuring without governing is a dashboard; the value lies in closing the loop. LiteLLM allows
three levels of control over spend:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Control&lt;/th>
&lt;th>What it does&lt;/th>
&lt;th>When&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Soft budget&lt;/strong>&lt;/td>
&lt;td>alert (Slack) when a threshold is crossed&lt;/td>
&lt;td>tracking teams&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Rate limit&lt;/strong>&lt;/td>
&lt;td>limits requests/tokens per minute&lt;/td>
&lt;td>avoiding cost spikes&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Hard cap&lt;/strong>&lt;/td>
&lt;td>the key cannot spend any more&lt;/td>
&lt;td>keys that must never overrun&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The healthy pattern: soft budgets with alerts for the teams (so they adjust their consumption),
rate limits to smooth spikes, and hard caps only on critical keys (a service that must not run
away). With this, cost per token stops being an after-the-fact figure and becomes a real-time
control: the FinOps &lt;em>Operate&lt;/em> phase, applied to inference.&lt;/p>
&lt;hr>
&lt;h2 id="on-prem-vs-european-cloud-comparison-the-reason-for-all-of-it">On-prem vs European cloud comparison (the reason for all of it)&lt;/h2>
&lt;p>Cost per token exists to answer one question: is it cheaper to serve on my own iron or to
rent? With the identity and European figures:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Option&lt;/th>
&lt;th>Cost per 1M tokens&lt;/th>
&lt;th>Notes&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Depreciated on-prem (8×H100, 2,800 tok/s)&lt;/td>
&lt;td>&lt;strong>~1.09 €&lt;/strong>&lt;/td>
&lt;td>your iron, your sovereignty&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>European cloud Scaleway (8×H100, on-demand)&lt;/td>
&lt;td>&lt;strong>~2.17 €&lt;/strong>&lt;/td>
&lt;td>EU, no iron to operate&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>External API (proprietary model)&lt;/td>
&lt;td>provider tariff&lt;/td>
&lt;td>no data sovereignty&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Cost per token is what makes the incomparable comparable: your variable cost (iron ÷
throughput) against a provider&amp;rsquo;s fixed tariff. And it exposes the lever: if an optimisation
raises your throughput from 2,800 to 4,200 tok/s, your on-prem cost drops to ~0.73 €/1M,
widening the advantage over renting. That is why benchmarking (track B) is, indirectly, the tool
that moves cost per token the most: every goodput improvement translates into this number.&lt;/p>
&lt;hr>
&lt;h2 id="from-cost-per-token-to-product-price">From cost per token to product price&lt;/h2>
&lt;p>Cost per token closes the loop with the business when it becomes cost per unit of product.
The chain: cost per token (from the identity) × tokens per use of a feature = marginal cost of
that feature. With that, three business decisions become data-backed:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Setting a price.&lt;/strong> If serving a feature costs 0.0016 € per use, the price to the customer
has to cover it with margin; cost per token is the floor of &lt;em>pricing&lt;/em>.&lt;/li>
&lt;li>&lt;strong>Spotting loss-making features.&lt;/strong> A feature that consumes many tokens (long prompts, lots of
output) may cost more than it contributes; cost per request reveals it before you scale.&lt;/li>
&lt;li>&lt;strong>Comparing build vs buy at product level.&lt;/strong> On-prem cost per token against an API tariff,
for that specific workload, decides whether to serve in house or consume externally.&lt;/li>
&lt;/ol>
&lt;p>This is the ultimate reason for the whole FinOps track: it is not infrastructure accounting, it
is the quantitative basis for product and architecture decisions. A measured and attributed cost
per token turns &amp;ldquo;is this profitable?&amp;rdquo; from an intuition into a calculation.&lt;/p>
&lt;hr>
&lt;h2 id="detailed-comparison-on-prem-european-cloud-and-api">Detailed comparison: on-prem, European cloud and API&lt;/h2>
&lt;p>Extending the comparison with scenarios, for the same job (1M tokens):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Option&lt;/th>
&lt;th>Cost/1M tok&lt;/th>
&lt;th>Sovereignty&lt;/th>
&lt;th>Where it fits&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Depreciated on-prem, high utilisation&lt;/td>
&lt;td>&lt;strong>~0.7–1.1 €&lt;/strong>&lt;/td>
&lt;td>full (EU)&lt;/td>
&lt;td>high, sustained volume&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Depreciated on-prem, low utilisation&lt;/td>
&lt;td>&lt;strong>~2–4 €&lt;/strong>&lt;/td>
&lt;td>full (EU)&lt;/td>
&lt;td>bad: idle drives cost up&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>European cloud (Scaleway, on-demand)&lt;/td>
&lt;td>&lt;strong>~2.2 €&lt;/strong>&lt;/td>
&lt;td>EU&lt;/td>
&lt;td>variable volume, no iron to operate&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>European cloud (reserved/committed)&lt;/td>
&lt;td>&lt;strong>~1.5–1.8 €&lt;/strong>&lt;/td>
&lt;td>EU&lt;/td>
&lt;td>medium committed volume&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>External proprietary API&lt;/td>
&lt;td>provider tariff&lt;/td>
&lt;td>&lt;strong>non-EU&lt;/strong>&lt;/td>
&lt;td>prototype / no sovereignty requirement&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The table shows two things. First, &lt;strong>on-prem only wins if utilisation is high&lt;/strong>, since at low
occupancy idle makes it dearer than cloud, which connects cost per token with scheduling and
utilisation (the idle of A2). Second, the &lt;strong>external API&lt;/strong>, however competitive its tariff,
&lt;strong>loses data sovereignty&lt;/strong>, an axis that for GDPR data is not negotiable. Cost per token is the
metric that puts the first three on the same scale; sovereignty is the constraint that rules out
the last one for certain kinds of data.&lt;/p>
&lt;hr>
&lt;h2 id="prompt-caching-and-effective-cost">Prompt caching and effective cost&lt;/h2>
&lt;p>One nuance refines the real cost per token: &lt;strong>prefix caching&lt;/strong>. When many requests share the
same prefix (a long system prompt, a repeated context), the engine caches its prefill compute
and &lt;strong>does not recompute it&lt;/strong> on every request. That means cached input tokens &lt;strong>cost almost
zero&lt;/strong> the second time and thereafter, so the effective cost per token of a workload with a high
cache &lt;em>hit rate&lt;/em> is &lt;strong>lower&lt;/strong> than the nominal cost. External APIs already reflect this with a
reduced price for cached tokens; in self-hosted the effect is that throughput rises (less
prefill to recompute), which by the identity lowers cost per token.&lt;/p>
&lt;p>The implication for the cost model: if your workload has shared prefixes (RAG over the same
corpus, agents with the same system prompt), the real cost per token is &lt;strong>lower&lt;/strong> than the naive
calculation gives, and it pays to measure the prefix caching &lt;em>hit rate&lt;/em> so as not to overestimate
cost. And the reverse: a workload of unique, never-repeated prompts gains nothing from the cache,
and its cost per token is the full nominal figure. Cost per token, once again, is not a constant:
it depends on the model, the precision, the utilisation, the goodput &lt;strong>and&lt;/strong> the reuse pattern of
the workload. Measuring it on your real traffic, not on a synthetic benchmark without a cache, is
what gives the number you actually pay.&lt;/p>
&lt;hr>
&lt;h2 id="state-of-the-art-2026">State of the art 2026&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>The gateway as standard&lt;/strong>: LiteLLM (and alternatives) are consolidating as the layer that
measures tokens, attributes cost and governs budgets in the LLM stack.&lt;/li>
&lt;li>&lt;strong>FOCUS for tokens&lt;/strong>: layers on top of the gateway generate FOCUS-compatible logs, integrating
AI cost with the rest of cloud spend (FinOps introduction).&lt;/li>
&lt;li>&lt;strong>Custom pricing as a bridge&lt;/strong>: the OpenCost→gateway connection via custom pricing is the
standard way to give self-hosted models a real cost.&lt;/li>
&lt;li>&lt;strong>Cost per token as a business KPI&lt;/strong>: increasingly, cost per token (and per product) is the
metric the business demands, not the cost of the pod.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="limits-and-traps-data-driven">Limits and traps (data-driven)&lt;/h2>
&lt;ol>
&lt;li>&lt;strong>Forgotten custom pricing.&lt;/strong> Without declaring to LiteLLM the real cost of your self-hosted
token, the &lt;code>response_cost&lt;/code> of your own models is zero or someone else&amp;rsquo;s price. Close the
OpenCost→gateway bridge.&lt;/li>
&lt;li>&lt;strong>Static throughput.&lt;/strong> Cost per token changes with throughput; if you optimise the engine but
do not update the custom pricing, the reported cost drifts out of date.&lt;/li>
&lt;li>&lt;strong>Forgetting prefill/decode.&lt;/strong> An average cost per token is fine for reporting; to optimise,
separate input and output.&lt;/li>
&lt;li>&lt;strong>Measuring without governing.&lt;/strong> Spend logs without budgets or caps make a pretty dashboard;
the value lies in closing the loop with alerts and caps.&lt;/li>
&lt;li>&lt;strong>Comparing tariffs with costs without matching assumptions.&lt;/strong> Your 1.09 €/1M assumes a
utilisation and a throughput; the provider&amp;rsquo;s tariff does not. Fix the assumptions before
comparing.&lt;/li>
&lt;/ol>
&lt;p>With cost per token solved, the FinOps track has its central metric; the following articles (A5
multi-tenant chargeback, A8 TCO model) build on it. The full chain, iron (OpenCost) ÷ throughput
(benchmark) = cost per token, measured and attributed by the gateway, is what turns the cost of
AI into a defensible number, in euros and per team.&lt;/p>
&lt;h2 id="closing">Closing&lt;/h2>
&lt;p>Cost per token is the metric that reconciles the three tracks of the series into a single
number, and its underlying lesson is counter-intuitive: for your own inference, &lt;strong>the cost of a
token is not a price, it is the reflection of your efficiency&lt;/strong>. It comes from dividing the cost
of the iron (OpenCost) by useful throughput (the goodput of your benchmark), and it becomes
visible and governable with a gateway (LiteLLM) that counts the tokens, attributes them by team
and applies budgets. The mistake that invalidates the whole exercise is leaving custom pricing at
zero and believing your own inference is &amp;ldquo;free&amp;rdquo;; the right move is to close the OpenCost→gateway
bridge and keep the number up to date when the node cost or the throughput changes. Done that
way, cost per token answers with data the one question the business keeps repeating, is it worth
serving this on my own iron, and it does so in euros, per team and per product, comparable
against European cloud and against any API. For a sovereign architecture proposal, it is the
column of the dashboard that translates all the cost engineering into the sentence that closes a
budget meeting: &lt;strong>this is the cost per token, this is how it is measured, and here is the bench
to reproduce it&lt;/strong>.&lt;/p>
&lt;h2 id="see-also">See also&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/gpu-chargeback-showback-multi-tenancy/">GPU chargeback and showback&lt;/a> — sharing cost out per team (OpenCost + LiteLLM + Kueue) once you have the cost per token.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/gpu-idle-cost-not-on-any-invoice/">GPU utilisation as a FinOps lever&lt;/a> — why cost per token depends on occupancy: the cost of idle.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/cloud-gpu-on-demand-reserved-spot-neoclouds-2026-prices/">Cloud GPU: a comparison of prices, commitment and sovereign neoclouds&lt;/a> — the alternative €/GPU-hour: on-demand, spot and reserved prices from European cloud providers against on-premise.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/full-tco-on-premise-gpu-cluster-capex-gpu-hour-break-even/">TCO of an on-premise GPU cluster: depreciation, energy and infrastructure&lt;/a> — the reference €/GPU-hour in your own infrastructure: CAPEX, energy, networking and staff with the full spreadsheet.&lt;/li>
&lt;/ul>
&lt;h2 id="sources">Sources&lt;/h2>
&lt;ul>
&lt;li>LiteLLM · Spend Tracking — &lt;a href="https://docs.litellm.ai/docs/proxy/cost_tracking">https://docs.litellm.ai/docs/proxy/cost_tracking&lt;/a>&lt;/li>
&lt;li>LiteLLM · Custom Pricing (modelos self-hosted) — &lt;a href="https://docs.litellm.ai/docs/proxy/custom_pricing">https://docs.litellm.ai/docs/proxy/custom_pricing&lt;/a>&lt;/li>
&lt;li>LiteLLM · Logging — &lt;a href="https://docs.litellm.ai/docs/proxy/logging">https://docs.litellm.ai/docs/proxy/logging&lt;/a>&lt;/li>
&lt;li>LiteLLM · documentación general — &lt;a href="https://docs.litellm.ai/docs/">https://docs.litellm.ai/docs/&lt;/a>&lt;/li>
&lt;li>Statsig · LiteLLM cost tracking — &lt;a href="https://www.statsig.com/perspectives/litellm-cost-tracking">https://www.statsig.com/perspectives/litellm-cost-tracking&lt;/a>&lt;/li>
&lt;li>StackPulsar · LiteLLM production monitoring (2026) — &lt;a href="https://stackpulsar.com/blog/litellm-production-monitoring/">https://stackpulsar.com/blog/litellm-production-monitoring/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Kubecost vs OpenCost vs the alternatives: what the commercial layer adds and when it is worth paying for</title><link>https://blog.lo0.es/en/posts/kubecost-vs-opencost-vs-alternatives/</link><pubDate>Sun, 14 Jun 2026 02:30:00 +0200</pubDate><guid>https://blog.lo0.es/en/posts/kubecost-vs-opencost-vs-alternatives/</guid><description>&lt;blockquote>
&lt;p>Notation: amounts in &lt;strong>euros (N €)&lt;/strong>, decimals with a point; when a source quotes dollars it is
marked &amp;ldquo;USD&amp;rdquo;. The dollar sign is not used (on this site it is a formula delimiter).&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-this-article-covers">What this article covers&lt;/h2>
&lt;p>Third article in the &lt;strong>FinOps&lt;/strong> track (A3). A2 covered how OpenCost allocates cost, the free
base. This article answers the question that comes next: &lt;strong>is it worth paying for Kubecost or a
commercial alternative, or is OpenCost enough?&lt;/strong> It is a &lt;strong>build-vs-buy&lt;/strong> decision, operating the
open source tool yourself against buying a product, and the answer depends on your scale, your
audience and your appetite for operating infrastructure. No universal recommendations: here are the
facts (what each one adds, what it costs, when it fits) and, for a European platform, the nuance of
&lt;strong>cost data sovereignty&lt;/strong> that the American comparisons do not mention.&lt;/p>
&lt;hr>
&lt;h2 id="the-axis-of-the-decision-build-vs-buy">The axis of the decision: build vs buy&lt;/h2>
&lt;p>OpenCost is free (Apache 2.0), but &amp;ldquo;free&amp;rdquo; means operating it yourself: deploying it,
configuring the node price, keeping Prometheus with enough retention, building the Grafana panels
and the alerts. Kubecost (and the alternatives) charge for taking that work away and adding
capabilities OpenCost does not ship. The decision, therefore, is not &amp;ldquo;free vs expensive&amp;rdquo;: it is
your own operating cost vs a licence.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Axis&lt;/th>
&lt;th>Leans towards OpenCost&lt;/th>
&lt;th>Leans towards commercial&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Platform team&lt;/td>
&lt;td>you have someone to operate it&lt;/td>
&lt;td>you do not want to operate anything&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Scale (cloud/GPU spend)&lt;/td>
&lt;td>small to medium&lt;/td>
&lt;td>large (the saving pays the licence)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Report audience&lt;/td>
&lt;td>engineers&lt;/td>
&lt;td>finance / management&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Number of clusters&lt;/td>
&lt;td>one&lt;/td>
&lt;td>many (multi-cluster)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Need for automatic optimisation&lt;/td>
&lt;td>you do it by hand&lt;/td>
&lt;td>you want it out of the box&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>OpenCost is the open source core; Kubecost is the enterprise product built on that core,
with proprietary features added; Kubecost was the original developer of the engine before releasing
it, and IBM acquired the company (&lt;a href="https://www.cloudzero.com/blog/kubecost-vs-opencost/">CloudZero · Kubecost vs OpenCost&lt;/a>).
In other words: both share the same allocation engine; what you pay for is the layer on top.&lt;/p>
&lt;hr>
&lt;h2 id="what-kubecost-adds-on-top-of-opencost">What Kubecost adds on top of OpenCost&lt;/h2>
&lt;p>On top of allocation (which is common to both), Kubecost adds a layer of &lt;strong>optimisation, governance
and enterprise features&lt;/strong> (&lt;a href="https://www.cloudzero.com/blog/kubecost-vs-opencost/">CloudZero&lt;/a>, &lt;a href="https://www.finout.io/blog/kubecost-vs-opencost">Finout&lt;/a>):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Capability&lt;/th>
&lt;th>OpenCost&lt;/th>
&lt;th>Kubecost&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Cost allocation (CPU/GPU/mem/PV)&lt;/td>
&lt;td>✓&lt;/td>
&lt;td>✓ (same engine)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GPU via DCGM&lt;/td>
&lt;td>✓&lt;/td>
&lt;td>✓ (3.0)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Invoice reconciliation&lt;/strong> (discounts, RI, spot)&lt;/td>
&lt;td>—&lt;/td>
&lt;td>✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Rightsizing&lt;/strong> (recommendations)&lt;/td>
&lt;td>—&lt;/td>
&lt;td>✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Anomaly detection&lt;/strong>&lt;/td>
&lt;td>—&lt;/td>
&lt;td>✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Budget alerts&lt;/strong>&lt;/td>
&lt;td>manual&lt;/td>
&lt;td>✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>RBAC&lt;/strong>&lt;/td>
&lt;td>—&lt;/td>
&lt;td>✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Multi-cluster aggregation&lt;/strong>&lt;/td>
&lt;td>—&lt;/td>
&lt;td>✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Hosted&lt;/strong> option (SaaS)&lt;/td>
&lt;td>—&lt;/td>
&lt;td>✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Long historical retention&lt;/td>
&lt;td>your Prometheus&lt;/td>
&lt;td>included&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Support&lt;/td>
&lt;td>community&lt;/td>
&lt;td>commercial (IBM)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 250" role="img" aria-label="OpenCost as the allocation core and Kubecost as commercial layers on top: reconciliation, rightsizing, anomalies, governance and support" xmlns="http://www.w3.org/2000/svg">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.3}.dsh{fill:none;stroke:currentColor;stroke-width:1.3;stroke-dasharray:5 3}.tl{font:600 12px sans-serif;fill:currentColor}.ts{font:11px sans-serif;fill:currentColor}&lt;/style>
&lt;rect class="bx" x="180" y="180" width="420" height="48" rx="6"/>
&lt;text x="390" y="200" text-anchor="middle" class="tl">OpenCost — allocation engine (Apache 2.0)&lt;/text>
&lt;text x="390" y="217" text-anchor="middle" class="ts">node price · split by usage · Allocation API&lt;/text>
&lt;rect class="dsh" x="180" y="40" width="420" height="124" rx="6"/>
&lt;text x="390" y="60" text-anchor="middle" class="tl">Kubecost (IBM) — commercial layer&lt;/text>
&lt;rect class="bx" x="200" y="74" width="180" height="34" rx="5"/>&lt;text x="290" y="95" text-anchor="middle" class="ts">invoice reconciliation&lt;/text>
&lt;rect class="bx" x="400" y="74" width="180" height="34" rx="5"/>&lt;text x="490" y="95" text-anchor="middle" class="ts">rightsizing + anomalies&lt;/text>
&lt;rect class="bx" x="200" y="116" width="180" height="34" rx="5"/>&lt;text x="290" y="137" text-anchor="middle" class="ts">RBAC · budgets&lt;/text>
&lt;rect class="bx" x="400" y="116" width="180" height="34" rx="5"/>&lt;text x="490" y="137" text-anchor="middle" class="ts">multi-cluster · support&lt;/text>
&lt;text x="390" y="246" text-anchor="middle" class="ts">Same engine underneath; what you pay for is the optimisation, governance and enterprise layer on top.&lt;/text>
&lt;/svg>
&lt;/div>
&lt;h3 id="invoice-reconciliation">Invoice reconciliation&lt;/h3>
&lt;p>The capability that most justifies the price in cloud: OpenCost allocates on list rates;
Kubecost reconciles against the real invoice, which includes discounts, reserved instances and
spot prices (&lt;a href="https://www.finout.io/blog/kubecost-vs-opencost">Finout&lt;/a>). The difference can be
large: a cost allocated at list rates can be 30–50 % above what you actually pay after discounts.
On-prem this matters less (you declare the node&amp;rsquo;s real price), but in cloud or hybrid,
reconciliation is the difference between a &amp;ldquo;theoretical&amp;rdquo; cost and the cost that shows up on the
invoice.&lt;/p>
&lt;h3 id="rightsizing-and-anomaly-detection">Rightsizing and anomaly detection&lt;/h3>
&lt;p>Kubecost recommends resizing (rightsizing) underused resources, via the integration with IBM
Turbonomic, and detects spend anomalies automatically. OpenCost gives you the data to do it
by hand; Kubecost automates it. For a GPU fleet, rightsizing and anomalous idle detection are
exactly where the saving of the &lt;em>Optimize&lt;/em> phase lives.&lt;/p>
&lt;hr>
&lt;h2 id="the-price-of-kubecost-and-the-build-vs-buy-economics">The price of Kubecost (and the build-vs-buy economics)&lt;/h2>
&lt;p>Kubecost&amp;rsquo;s price starts at 449 USD/month for &lt;em>business&lt;/em> use, with enterprise options on request
(&lt;a href="https://www.cloudzero.com/blog/kubecost-vs-opencost/">source&lt;/a>). The right question is not &amp;ldquo;is 449
USD expensive?&amp;rdquo;, but &amp;ldquo;does operating OpenCost myself cost more or less than that?&amp;rdquo;. The
build-vs-buy calculation:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Cost&lt;/th>
&lt;th>OpenCost (build)&lt;/th>
&lt;th>Kubecost (buy)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Licence&lt;/td>
&lt;td>0&lt;/td>
&lt;td>from ~449 USD/month (business)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Engineering time&lt;/td>
&lt;td>deployment + maintenance + panels&lt;/td>
&lt;td>minimal&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Historical retention&lt;/td>
&lt;td>your Prometheus/Thanos cost&lt;/td>
&lt;td>included&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Support&lt;/td>
&lt;td>your team&lt;/td>
&lt;td>included&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>If one platform person spends a few hours a month operating OpenCost, that time can already exceed
449 USD; if your cluster is small and the operating cost is nearly nil (you set it up once and it
runs), OpenCost wins. The market rule of thumb: if &lt;strong>monthly Kubernetes spend is below ~10,000
USD&lt;/strong>, the cloud-native tools or open source (OpenCost) usually suffice; above that, the saving the
commercial tool captures pays for its licence ([search]).&lt;/p>
&lt;hr>
&lt;h3 id="worked-example-build-vs-buy">Worked example: build vs buy&lt;/h3>
&lt;p>An illustrative calculation for a platform with a medium-sized GPU cluster:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Item&lt;/th>
&lt;th>OpenCost (build)&lt;/th>
&lt;th>Kubecost (buy)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Annual licence&lt;/td>
&lt;td>0&lt;/td>
&lt;td>~449 USD/month × 12 = ~5,400 USD (~5,000 €)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Initial deployment&lt;/td>
&lt;td>~2–3 days of engineering&lt;/td>
&lt;td>~half a day&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Maintenance&lt;/td>
&lt;td>~4–8 h/month (panels, alerts, upgrades)&lt;/td>
&lt;td>minimal&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Operating cost (at ~60 €/h)&lt;/td>
&lt;td>~240–480 €/month&lt;/td>
&lt;td>~marginal&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Historical retention&lt;/td>
&lt;td>cost of your own Thanos/Mimir&lt;/td>
&lt;td>included&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>If OpenCost&amp;rsquo;s operating cost is around 240–480 €/month, it is in the same order as the Kubecost
licence (~415 €/month). The decision, then, is not set by the price but by the added value: if
invoice reconciliation, automatic rightsizing and the finance-facing UI save you or give you more
than that difference, Kubecost wins; if your team already operates Prometheus/Grafana and your
audience is engineers, OpenCost wins. For a large multi-cluster estate, the saving Kubecost
captures usually far exceeds the licence; for a small and stable one, operating OpenCost is almost
free. The number that decides is not 449 USD, it is &lt;strong>your operating cost against that added
value&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h3 id="migration-and-reversibility-an-argument-for-starting-with-opencost">Migration and reversibility: an argument for starting with OpenCost&lt;/h3>
&lt;p>A rarely mentioned advantage of Kubecost being built on OpenCost: migrating between the two is
low risk. Starting with OpenCost and, if scale justifies it, moving up to Kubecost later does
not force you to redo the allocation, it is the same engine, the same concepts, the same
Prometheus/DCGM instrumentation. What changes is the layer on top. This reduces &lt;em>lock-in&lt;/em>: it is
not an irreversible bet.&lt;/p>
&lt;p>For a platform just starting out, the low-risk strategy is clear: &lt;strong>stand up self-hosted OpenCost
first&lt;/strong>, get the allocation right with the node price in euros, live with it for a few months, and
only then decide whether invoice reconciliation, automatic rightsizing and the finance-facing UI
justify paying for Kubecost. Starting with the commercial product &amp;ldquo;just in case&amp;rdquo; is paying before
knowing whether you need it; starting with OpenCost and moving up if necessary is the option that
keeps reversibility and discovers the real value before the invoice. And since both are
self-hosted, neither route gives up data sovereignty.&lt;/p>
&lt;hr>
&lt;h2 id="the-alternatives-cloudzero-vantage-finout">The alternatives: CloudZero, Vantage, Finout&lt;/h2>
&lt;p>Kubecost is not the only commercial option; there is a category of cost platforms that operate
at a different level (closer to the business than to Kubernetes):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Tool&lt;/th>
&lt;th>Focus&lt;/th>
&lt;th>Differentiator&lt;/th>
&lt;th>Layer&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>CloudZero&lt;/strong>&lt;/td>
&lt;td>unit economics&lt;/td>
&lt;td>cost per feature/product/customer&lt;/td>
&lt;td>business&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Vantage&lt;/strong>&lt;/td>
&lt;td>multi-cloud (20+ integrations)&lt;/td>
&lt;td>breadth (incl. OpenAI invoice)&lt;/td>
&lt;td>business&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Finout&lt;/strong>&lt;/td>
&lt;td>multi-cloud&lt;/td>
&lt;td>virtual tagging (without touching resources)&lt;/td>
&lt;td>business&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Kubecost&lt;/strong>&lt;/td>
&lt;td>Kubernetes&lt;/td>
&lt;td>intra-cluster depth + optimisation&lt;/td>
&lt;td>resource+optim&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>OpenCost&lt;/strong>&lt;/td>
&lt;td>Kubernetes&lt;/td>
&lt;td>open source allocation&lt;/td>
&lt;td>resource&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The key distinction: Kubecost/OpenCost are Kubernetes-centred tools (intra-cluster depth);
CloudZero/Vantage/Finout are business cost platforms (multi-cloud breadth, cost per
product). They do not always compete: many organisations use OpenCost/Kubecost for the Kubernetes
detail and a business platform for the aggregate view.&lt;/p>
&lt;p>A short profile of each:&lt;/p>
&lt;ul>
&lt;li>CloudZero — a &lt;em>cost intelligence&lt;/em> platform with a dedicated Kubernetes visibility solution
for engineering teams. Its hallmark is unit economics: mapping cost to feature, product and
customer, answering &amp;ldquo;how much does it cost me to serve this customer?&amp;rdquo; more than &amp;ldquo;how much does
this pod cost?&amp;rdquo;. It fits when cost has to be taken to the product P&amp;amp;L.&lt;/li>
&lt;li>Vantage — breadth of integrations (more than 20 sources: AWS, Azure, GCP, Kubernetes,
Snowflake, Datadog and the invoice from LLM providers such as OpenAI). It is the option when
AI cost is spread across many providers and you want a single view, including the invoice from
the model APIs.&lt;/li>
&lt;li>Finout — multi-cloud with virtual tagging: it applies cost labels without modifying the
resources, which allows spend that was badly tagged at source to be allocated, and quick
deployment. It is among the top Kubernetes cost optimisation tools
(&lt;a href="https://www.cloudbolt.io/blog/top-kubecost-alternatives/">CloudBolt&lt;/a>, &lt;a href="https://www.finout.io/blog/kubecost-pros/cons-pricing-tutorial-alternatives-2026-guide">Finout&lt;/a>).&lt;/li>
&lt;/ul>
&lt;p>The usual pattern in a mature organisation: OpenCost/Kubecost for the Kubernetes and GPU detail,
and a business platform on top to cross that cost with the rest of the cloud spend and take it
to product. It is not &amp;ldquo;one or the other&amp;rdquo;: it is often &amp;ldquo;one and the other&amp;rdquo;, in layers.&lt;/p>
&lt;hr>
&lt;h2 id="decision-criteria">Decision criteria&lt;/h2>
&lt;p>Five questions that settle the choice without opinion:&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 250" role="img" aria-label="Decision tree: by scale, audience, multi-cluster and sovereignty, choose OpenCost, Kubecost or a business platform" xmlns="http://www.w3.org/2000/svg">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.3}.tl{font:600 12px sans-serif;fill:currentColor}.ts{font:11px sans-serif;fill:currentColor}.ar{fill:none;stroke:currentColor;stroke-width:1.3;marker-end:url(#km)}&lt;/style>
&lt;defs>&lt;marker id="km" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="currentColor"/>&lt;/marker>&lt;/defs>
&lt;rect class="bx" x="20" y="100" width="150" height="50" rx="6"/>
&lt;text x="32" y="122" class="tl">Spend &amp;gt; 10k/month?&lt;/text>
&lt;text x="32" y="139" class="ts">and multi-cluster?&lt;/text>
&lt;path class="ar" d="M170,115 L230,70"/>
&lt;text x="180" y="80" class="ts">no&lt;/text>
&lt;path class="ar" d="M170,135 L230,180"/>
&lt;text x="180" y="170" class="ts">yes&lt;/text>
&lt;rect class="bx" x="230" y="48" width="180" height="44" rx="6"/>
&lt;text x="242" y="68" class="tl">OpenCost (self-host)&lt;/text>
&lt;text x="242" y="84" class="ts">allocation, free, sovereign&lt;/text>
&lt;rect class="bx" x="230" y="158" width="180" height="44" rx="6"/>
&lt;text x="242" y="178" class="tl">Kubecost&lt;/text>
&lt;text x="242" y="194" class="ts">optimisation + governance&lt;/text>
&lt;path class="ar" d="M410,180 L470,150"/>
&lt;text x="420" y="150" class="ts">cost per product?&lt;/text>
&lt;rect class="bx" x="470" y="120" width="200" height="44" rx="6"/>
&lt;text x="482" y="140" class="tl">+ business platform&lt;/text>
&lt;text x="482" y="156" class="ts">CloudZero / Vantage / Finout&lt;/text>
&lt;text x="20" y="235" class="ts">And a cross-cutting question for Europe: where does the cost data live? (see sovereignty below)&lt;/text>
&lt;/svg>
&lt;/div>
&lt;ol>
&lt;li>&lt;strong>How much do you spend?&lt;/strong> Below ~10k USD/month, OpenCost is enough; above that, the saving pays
for the commercial tool.&lt;/li>
&lt;li>&lt;strong>Who reads the report?&lt;/strong> Engineers → OpenCost; finance/management → Kubecost&amp;rsquo;s polished UI or a
business platform.&lt;/li>
&lt;li>&lt;strong>One cluster or many?&lt;/strong> Multi-cluster leans towards Kubecost.&lt;/li>
&lt;li>&lt;strong>Do you want to operate infrastructure?&lt;/strong> If not, buy; if you have a platform team, build with
OpenCost.&lt;/li>
&lt;li>&lt;strong>Where can the cost data live?&lt;/strong> The European question, below.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="the-european-angle-cost-data-sovereignty">The European angle: cost data sovereignty&lt;/h2>
&lt;p>American comparisons leave out a criterion that for a sovereign platform is first-order: &lt;strong>where
the cost and usage data ends up&lt;/strong>. CloudZero, Vantage, Finout and Kubecost&amp;rsquo;s &lt;em>hosted&lt;/em> option are
SaaS, mostly American: you send them your telemetry of cost, usage, namespace names, teams and
products. That data, which describes your operation in detail, leaves for a jurisdiction subject to
the US CLOUD Act, with the same GDPR consideration as any other sensitive data sent to a
hyperscaler.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Option&lt;/th>
&lt;th>Where the cost data lives&lt;/th>
&lt;th>Sovereignty&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Self-hosted OpenCost&lt;/strong>&lt;/td>
&lt;td>in your cluster&lt;/td>
&lt;td>total&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Self-hosted Kubecost&lt;/strong>&lt;/td>
&lt;td>in your cluster&lt;/td>
&lt;td>total&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Hosted Kubecost (SaaS)&lt;/td>
&lt;td>the provider&amp;rsquo;s SaaS&lt;/td>
&lt;td>subject to the SaaS jurisdiction&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CloudZero / Vantage / Finout&lt;/td>
&lt;td>SaaS (US)&lt;/td>
&lt;td>subject to the US CLOUD Act&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>It is worth being fair: cost data is not the same as user data. Spend telemetry per namespace is
less sensitive than the content of the inferences. But it does reveal the operation: which
teams exist, which products consume GPU, the size of the platform, and, crossed with namespace
names, information that taken together can be sensitive. Most SaaS FinOps tooling is American;
there are emerging European options, but few with the maturity of the big players. When in doubt,
and for data subject to GDPR, the minimisation principle advises &lt;strong>not taking out of the EU what
does not need to leave&lt;/strong>, and cost allocation, with self-hosted OpenCost/Kubecost, does not need to
leave.&lt;/p>
&lt;p>The consequence for the proposal: &lt;strong>self-hosted OpenCost or Kubecost keep the cost data under your
control&lt;/strong>, just as the cluster keeps the inference data. If data sovereignty is a requirement (it
is for GDPR data, see &lt;a href="https://blog.lo0.es/en/posts/technical-controls-ens-iso-42001-eu-ai-act-cross-mapping/">ENS × 42001 × EU AI
Act controls&lt;/a>), the American SaaS platforms
run into the same conflict as a hyperscaler, however good their UI is. For Fibercli and any
sovereign platform, this tilts the balance towards &lt;strong>self-hosted&lt;/strong>, with OpenCost as the base and
self-hosted Kubecost if the enterprise layer is wanted without giving up the data.&lt;/p>
&lt;hr>
&lt;h2 id="ai-cost-what-none-of-them-ships-out-of-the-box">AI cost: what none of them ships out of the box&lt;/h2>
&lt;p>One point the generalist comparisons do not highlight and that is central for an LLM platform:
none of these tools gives cost per token out of the box. OpenCost and Kubecost reach the &lt;strong>pod
cost&lt;/strong> (per resource/usage); CloudZero, Vantage and Finout reach the &lt;strong>resource or invoice cost&lt;/strong>
(including the OpenAI one, in Vantage&amp;rsquo;s case). But the jump from &amp;ldquo;this vLLM pod cost X €/hour&amp;rdquo; to
&amp;ldquo;this request from this team cost Y&amp;rdquo; requires intercepting the inference traffic with a gateway
(LiteLLM) that counts tokens per request and per team, as developed in the FinOps introduction.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Layer&lt;/th>
&lt;th>What it gives&lt;/th>
&lt;th>Who covers it&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Resource/pod cost&lt;/td>
&lt;td>€/hour per pod, namespace, team&lt;/td>
&lt;td>OpenCost, Kubecost&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cloud/LLM invoice cost&lt;/td>
&lt;td>€/month per provider&lt;/td>
&lt;td>CloudZero, Vantage, Finout&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Cost per token&lt;/strong>&lt;/td>
&lt;td>€/1M tokens per team/model&lt;/td>
&lt;td>&lt;strong>gateway (LiteLLM) + any of the above&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The implication for tool selection: cost per token, the metric that compares on-prem against cloud,
is not solved by any tool on its own, but by the combination of allocation
(OpenCost/Kubecost) with token measurement (gateway). When evaluating a commercial tool, the AI
question is not &amp;ldquo;does it give the pod cost?&amp;rdquo; (they all do, one way or another), but &amp;ldquo;does it
integrate well with my gateway to reach the token?&amp;rdquo;. Vantage, by ingesting the OpenAI invoice,
gets close from the external API consumption side; for your own inference, the gateway remains
essential.&lt;/p>
&lt;hr>
&lt;h2 id="state-of-the-art-2026">State of the art 2026&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>Consolidation under IBM&lt;/strong>: Kubecost/OpenCost integrated into IBM&amp;rsquo;s FinOps Suite alongside
Cloudability and Turbonomic; OpenCost remains the CNCF allocation standard.&lt;/li>
&lt;li>&lt;strong>First-class GPU&lt;/strong>: Kubecost 3.0 and OpenCost allocate GPU via DCGM; GPU FinOps stops being a
special case.&lt;/li>
&lt;li>&lt;strong>Towards the token and FOCUS&lt;/strong>: the strong platforms track cost at token level, and the
FOCUS standard (v1.3, Dec 2025) is extending to AI workloads, which will push
interoperability between these tools.&lt;/li>
&lt;li>&lt;strong>A category of AI cost platforms&lt;/strong>: specific &lt;em>AI cost observability&lt;/em> tooling is emerging that
combines resource, invoice and token; it is worth demanding FOCUS compatibility so as not to
couple to one provider.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="selection-checklist">Selection checklist&lt;/h2>
&lt;p>To decide without opinion, in order:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Step&lt;/th>
&lt;th>Question&lt;/th>
&lt;th>If&amp;hellip;&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>1&lt;/td>
&lt;td>Do I have a team to operate OpenCost?&lt;/td>
&lt;td>no → commercial / hosted&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2&lt;/td>
&lt;td>Spend &amp;gt; ~10k USD/month and multi-cluster?&lt;/td>
&lt;td>yes → Kubecost&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3&lt;/td>
&lt;td>Is the report for finance/management?&lt;/td>
&lt;td>yes → Kubecost UI or business platform&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>4&lt;/td>
&lt;td>Do I need cost per product/customer?&lt;/td>
&lt;td>yes → CloudZero/Vantage/Finout on top&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>5&lt;/td>
&lt;td>Can the cost data go to a US SaaS?&lt;/td>
&lt;td>no → self-hosted (OpenCost / Kubecost)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>6&lt;/td>
&lt;td>Do I need cost per token?&lt;/td>
&lt;td>yes → any of them &lt;strong>+ gateway&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Step 5 is the filter for a sovereign platform: if the cost data cannot leave the EU jurisdiction,
the list shrinks to the &lt;strong>self-hosted&lt;/strong> options, with OpenCost as the base.&lt;/p>
&lt;hr>
&lt;h2 id="pricing-models-of-commercial-tooling">Pricing models of commercial tooling&lt;/h2>
&lt;p>For those that charge by saving or by percentage of spend:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th>How it charges&lt;/th>
&lt;th>Range&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Fixed licence (Kubecost)&lt;/td>
&lt;td>monthly rate&lt;/td>
&lt;td>from ~449 USD/month (business)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Savings-based&lt;/td>
&lt;td>% of the savings delivered&lt;/td>
&lt;td>15–35 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Fixed-fee&lt;/td>
&lt;td>% of annual cloud spend&lt;/td>
&lt;td>1–3 %&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The implication: at large spend, a 1–3 % &lt;em>fixed-fee&lt;/em> can exceed Kubecost&amp;rsquo;s fixed licence; with a
lot of waste, &lt;em>savings-based&lt;/em> aligns incentives but can come out expensive if the saving is large.
OpenCost, free, changes the equation if you have someone to operate it.&lt;/p>
&lt;hr>
&lt;h2 id="coexistence-the-reality-is-layered">Coexistence: the reality is layered&lt;/h2>
&lt;p>In mature organisations, these tools are not chosen against each other, they are stacked. The
usual pattern:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Layer&lt;/th>
&lt;th>Tool&lt;/th>
&lt;th>Answers&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Intra-Kubernetes allocation&lt;/td>
&lt;td>OpenCost / Kubecost&lt;/td>
&lt;td>how much does each pod/team/GPU cost?&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Per-token measurement&lt;/td>
&lt;td>gateway (LiteLLM)&lt;/td>
&lt;td>how much does each request/team cost?&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cost per product / multi-cloud&lt;/td>
&lt;td>CloudZero / Vantage / Finout&lt;/td>
&lt;td>how much does it cost to serve this product/customer?&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Each layer solves a different question and feeds off the one below: the pod cost (OpenCost) plus the
tokens (gateway) give the cost per token; that cost per token, aggregated by a business platform,
gives the cost per product. Trying to make a single tool cover all three layers usually ends in
compromises: the Kubernetes ones do not reach the business, the business ones do not reach the pod,
and none reaches the token without the gateway. The healthy architecture is &lt;strong>layers that
integrate&lt;/strong>, not one tool that does everything by halves. And the glue between layers, in 2026, is
FOCUS: demanding FOCUS compatibility from each piece is what allows the data to flow between
them without being rewritten.&lt;/p>
&lt;hr>
&lt;h2 id="summary-by-organisation-profile">Summary by organisation profile&lt;/h2>
&lt;p>To get your bearings quickly, which stack fits each profile (indicative, not prescriptive):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Profile&lt;/th>
&lt;th>Scale&lt;/th>
&lt;th>Stack that usually fits&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Small team with a platform&lt;/td>
&lt;td>1 cluster, low spend&lt;/td>
&lt;td>&lt;strong>OpenCost&lt;/strong> self-hosted + Grafana&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Scale-up with several teams&lt;/td>
&lt;td>multi-team, medium spend&lt;/td>
&lt;td>OpenCost + gateway; Kubecost if time is short&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Multi-cluster enterprise&lt;/td>
&lt;td>many clusters, high spend&lt;/td>
&lt;td>&lt;strong>Kubecost&lt;/strong> + business platform&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Sovereign platform (Fibercli)&lt;/strong>&lt;/td>
&lt;td>GDPR, data in the EU&lt;/td>
&lt;td>&lt;strong>self-hosted OpenCost/Kubecost&lt;/strong> + gateway; no US cost SaaS&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The sovereign profile is the one that changes the answer relative to the standard guides: however
much an American SaaS platform wins on features or on UI, the requirement to keep cost data under
EU jurisdiction rules it out for GDPR data. For Fibercli, the bottom row is the one that rules, and
that is why this series&amp;rsquo; FinOps track is built on &lt;strong>self-hosted&lt;/strong> tools.&lt;/p>
&lt;hr>
&lt;h2 id="limits-and-traps-data-driven">Limits and traps (data-driven)&lt;/h2>
&lt;ol>
&lt;li>&lt;strong>&amp;ldquo;Free&amp;rdquo; is not free.&lt;/strong> OpenCost has an operating cost; count it in the build-vs-buy or you are
fooling yourself comparing 0 with 449 USD.&lt;/li>
&lt;li>&lt;strong>Reconciliation only matters in cloud.&lt;/strong> In pure on-prem, you declare the node&amp;rsquo;s real price and
Kubecost&amp;rsquo;s reconciliation adds less.&lt;/li>
&lt;li>&lt;strong>Cost SaaS = cost data outside.&lt;/strong> For a sovereign platform, sending cost telemetry to an
American SaaS reintroduces the jurisdiction problem that on-prem avoided.&lt;/li>
&lt;li>&lt;strong>Same engine, different UI.&lt;/strong> A good part of what you pay for in Kubecost is the presentation
and governance layer; if your audience is engineers, it may not pay off.&lt;/li>
&lt;li>&lt;strong>Do not confuse layers.&lt;/strong> OpenCost/Kubecost (Kubernetes) and CloudZero/Vantage/Finout
(business) solve different problems; sometimes they complement each other, they do not compete.&lt;/li>
&lt;/ol>
&lt;p>With the allocation tooling decision settled, the FinOps track moves on to cost per token (A4) and
the full TCO model (A8). The foundation is the one from A2 and A3: correct allocation, with the
node price in euros, in a tool that respects data sovereignty.&lt;/p>
&lt;h2 id="closing">Closing&lt;/h2>
&lt;p>The OpenCost vs Kubecost vs commercial platform choice is sold as a feature comparison, and is in
reality a build-vs-buy decision with a sovereignty constraint on top. The allocation engine is
the same (OpenCost) in the first two; what you pay for in Kubecost is the optimisation, governance
and presentation layer, which pays off when scale is large, the audience is financial or you do not
want to operate infrastructure. The business platforms (CloudZero, Vantage, Finout) solve a
different problem, cost per product, multi-cloud, and often coexist in layers with the Kubernetes
ones rather than competing. But for a European sovereign platform there is a criterion no American
comparison puts first and that for Fibercli comes first: where the cost data lives. Sending
your spend, team and product telemetry to an American SaaS reintroduces, through the back door, the
jurisdiction problem that on-prem avoided. The conclusion the data supports: &lt;strong>self-hosted OpenCost
as the base, self-hosted Kubecost if the enterprise layer is wanted, and the gateway to reach the
token&lt;/strong>, a stack that keeps cost measured, in euros, and under EU jurisdiction. Expensive or cheap
is secondary; sovereign or not is not.&lt;/p>
&lt;h2 id="sources">Sources&lt;/h2>
&lt;ul>
&lt;li>CloudZero · Kubecost vs OpenCost (2026) — &lt;a href="https://www.cloudzero.com/blog/kubecost-vs-opencost/">https://www.cloudzero.com/blog/kubecost-vs-opencost/&lt;/a>&lt;/li>
&lt;li>Finout · Kubecost vs OpenCost (6 diferencias) — &lt;a href="https://www.finout.io/blog/kubecost-vs-opencost">https://www.finout.io/blog/kubecost-vs-opencost&lt;/a>&lt;/li>
&lt;li>Finout · Kubecost: pros/cons, pricing, alternativas (2026) — &lt;a href="https://www.finout.io/blog/kubecost-pros/cons-pricing-tutorial-alternatives-2026-guide">https://www.finout.io/blog/kubecost-pros/cons-pricing-tutorial-alternatives-2026-guide&lt;/a>&lt;/li>
&lt;li>CloudBolt · Top Kubecost Alternatives (2026) — &lt;a href="https://www.cloudbolt.io/blog/top-kubecost-alternatives/">https://www.cloudbolt.io/blog/top-kubecost-alternatives/&lt;/a>&lt;/li>
&lt;li>Amnic · OpenCost vs Kubecost — &lt;a href="https://amnic.com/blogs/opencost-vs-kubecost">https://amnic.com/blogs/opencost-vs-kubecost&lt;/a>&lt;/li>
&lt;li>Clanker Cloud · Best tools for managing GPU usage in Kubernetes (2026) — &lt;a href="https://clankercloud.ai/blog/best-tools-managing-gpu-usage-kubernetes-2025-2026-cost-roi">https://clankercloud.ai/blog/best-tools-managing-gpu-usage-kubernetes-2025-2026-cost-roi&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>OpenCost in depth: how GPU cost is allocated in Kubernetes</title><link>https://blog.lo0.es/en/posts/opencost-gpu-cost-allocation-kubernetes/</link><pubDate>Sun, 14 Jun 2026 02:00:00 +0200</pubDate><guid>https://blog.lo0.es/en/posts/opencost-gpu-cost-allocation-kubernetes/</guid><description>&lt;blockquote>
&lt;p>Notation: amounts in &lt;strong>euros (N €)&lt;/strong>, decimals with a point. European references (OVH is among
the supported providers). The dollar sign is not used (on this site it is a formula
delimiter).&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-this-article-covers">What this article covers&lt;/h2>
&lt;p>Second article in the &lt;strong>FinOps&lt;/strong> track (A2), and the first tool deep dive. The
&lt;a href="https://blog.lo0.es/en/posts/gpu-llm-finops-frameworks-metrics-state-of-the-art/">FinOps introduction&lt;/a> established that
OpenCost is the CNCF standard for cost allocation; here the box gets opened: how it allocates
cost internally, where it gets its data from, how on-prem prices are configured in euros, what its
API exposes, and where the traps are. Understanding the mechanics matters because &lt;strong>it defines what
any tool on top (Kubecost included) can and cannot do&lt;/strong>, and because a badly set base price
invalidates the whole split. No recommendations; only the mechanics and the methodology.&lt;/p>
&lt;hr>
&lt;h2 id="what-opencost-is">What OpenCost is&lt;/h2>
&lt;p>OpenCost is a vendor-neutral, Apache 2.0 project, originally built by Kubecost and donated to
the CNCF (incubating project). It is a cost allocation layer for Kubernetes: it reads
resource usage from the cluster, joins it with a price, and splits the cost across Kubernetes
dimensions down to the container (&lt;a href="https://github.com/opencost/opencost">OpenCost · GitHub&lt;/a>).
It supports dynamic pricing via the billing APIs of AWS, Azure and GCP, &lt;strong>and custom prices for
on-prem clusters&lt;/strong> (&lt;a href="https://opencost.io/docs/configuration/">OpenCost · configuration&lt;/a>).&lt;/p>
&lt;p>What it is &lt;strong>not&lt;/strong>: it is not an optimiser, nor a governance system, nor a product unit economics
tool. It is the piece that answers &amp;ldquo;how much does each namespace/pod/team cost?&amp;rdquo;, and leaves the
rest to the layers above. That is why it is the foundation of the FinOps track: without correct
allocation, there is no cost per token and no TCO model worth anything.&lt;/p>
&lt;hr>
&lt;h2 id="the-cost-model-node-level-allocation">The cost model: node-level allocation&lt;/h2>
&lt;p>The key to OpenCost is that the model works at node level. It starts from the node&amp;rsquo;s &lt;strong>resource
capacity&lt;/strong> (CPU, RAM, GPU, storage) and its &lt;strong>total price&lt;/strong>, and splits that price across the
resources. When the provider does not give explicit per-resource prices, OpenCost uses the &lt;strong>ratio
of a set of base prices&lt;/strong> (marginal rates, customisable) and &lt;strong>normalises them so that the sum of
the components equals the node&amp;rsquo;s total price&lt;/strong> (&lt;a href="https://opencost.io/docs/configuration/on-prem/">OpenCost · on-prem&lt;/a>).&lt;/p>
&lt;p>This is what makes it work on-prem: you declare the node cost (depreciated capex + opex), and
OpenCost splits it without needing a cloud invoice. The normalisation guarantees that no cost is
&amp;ldquo;lost&amp;rdquo; or &amp;ldquo;invented&amp;rdquo;: what you pay for the node is exactly what gets split across its resources.&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 250" role="img" aria-label="OpenCost cost model: total node price split and normalised across CPU, GPU, RAM and disk, and then allocated by usage to each pod" xmlns="http://www.w3.org/2000/svg">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.3}.dsh{fill:none;stroke:currentColor;stroke-width:1.3;stroke-dasharray:5 3}.tl{font:600 12px sans-serif;fill:currentColor}.ts{font:11px sans-serif;fill:currentColor}.ar{fill:none;stroke:currentColor;stroke-width:1.3;marker-end:url(#om)}&lt;/style>
&lt;defs>&lt;marker id="om" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="currentColor"/>&lt;/marker>&lt;/defs>
&lt;rect class="bx" x="20" y="40" width="170" height="60" rx="6"/>
&lt;text x="32" y="63" class="tl">Node price&lt;/text>
&lt;text x="32" y="81" class="ts">depreciated capex&lt;/text>
&lt;text x="32" y="95" class="ts">+ opex (€/hour)&lt;/text>
&lt;path class="ar" d="M190,70 L235,70"/>
&lt;rect class="bx" x="235" y="40" width="210" height="60" rx="6"/>
&lt;text x="247" y="63" class="tl">Split + normalisation&lt;/text>
&lt;text x="247" y="81" class="ts">CPU / GPU / RAM / disk&lt;/text>
&lt;text x="247" y="95" class="ts">sum = total price&lt;/text>
&lt;path class="ar" d="M445,70 L490,70"/>
&lt;rect class="bx" x="490" y="40" width="270" height="60" rx="6"/>
&lt;text x="502" y="63" class="tl">Allocation by USAGE&lt;/text>
&lt;text x="502" y="81" class="ts">GPU half used = half the cost&lt;/text>
&lt;text x="502" y="95" class="ts">per pod / namespace / team&lt;/text>
&lt;rect class="dsh" x="20" y="130" width="740" height="96" rx="6"/>
&lt;text x="34" y="152" class="tl">What OpenCost guarantees: no cost is lost and none is invented.&lt;/text>
&lt;text x="34" y="172" class="ts">· On-prem: you declare the node cost; OpenCost splits it (no cloud invoice needed).&lt;/text>
&lt;text x="34" y="190" class="ts">· GPU is allocated by USAGE, not presence: a GPU at 50 % charges half its cost.&lt;/text>
&lt;text x="34" y="208" class="ts">· Trap: if the default base price is wrong, the WHOLE split is wrong (typical on-prem under-pricing).&lt;/text>
&lt;/svg>
&lt;/div>
&lt;hr>
&lt;h2 id="where-the-data-comes-from-prometheus">Where the data comes from: Prometheus&lt;/h2>
&lt;p>OpenCost instruments nothing of its own: it reads from Prometheus, which is a prerequisite
of the installation (&lt;a href="https://github.com/opencost/opencost">OpenCost · GitHub&lt;/a>). The sources:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Source&lt;/th>
&lt;th>What it provides&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>kube-state-metrics&lt;/code>&lt;/td>
&lt;td>state of K8s objects (pods, requests/limits)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>node-exporter&lt;/code>&lt;/td>
&lt;td>node resources and capacity&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>cAdvisor&lt;/code>&lt;/td>
&lt;td>real CPU/memory usage per container&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>dcgm-exporter&lt;/code> (NVIDIA)&lt;/td>
&lt;td>&lt;strong>GPU&lt;/strong> usage, memory and power&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>With those series, OpenCost automates the join between Kubernetes resource usage and the
provider price, reducing the custom PromQL you would otherwise write by hand (&lt;a href="https://grafana.com/docs/grafana-cloud/monitor-infrastructure/kubernetes-monitoring/manage-costs/">Grafana ·
manage costs&lt;/a>).
The &lt;strong>GPU&lt;/strong> signal comes from DCGM via the &lt;code>dcgm-exporter&lt;/code> (part of the NVIDIA GPU Operator),
the same base as &lt;a href="https://blog.lo0.es/en/posts/gpu-observability-llm-inference-twelve-dcgm-vllm-metrics/">GPU observability&lt;/a>. Without
Prometheus and without DCGM exporting, OpenCost does not see the GPU.&lt;/p>
&lt;hr>
&lt;h2 id="architecture-the-pieces">Architecture: the pieces&lt;/h2>
&lt;p>OpenCost is not a monolith; it is three responsibilities worth telling apart:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Piece&lt;/th>
&lt;th>Function&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Cost model&lt;/strong>&lt;/td>
&lt;td>resolves the node price and splits/normalises it across resources&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Allocation API&lt;/strong>&lt;/td>
&lt;td>serves the allocated cost per dimension (&lt;code>/allocation&lt;/code> and endpoints)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Exporter&lt;/strong>&lt;/td>
&lt;td>publishes cost metrics on &lt;code>/metrics&lt;/code> for Prometheus/Grafana&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The data flow: Prometheus holds the usage series (from kube-state-metrics, cAdvisor,
dcgm-exporter); the cost model joins them with the resolved price; the Allocation API
serves them aggregated by whatever dimension you ask for; and the exporter returns them to
Prometheus so that Grafana can plot them. It is a cycle: metrics come in from Prometheus and cost
goes back to Prometheus enriched. That symmetry is what lets you reuse your observability stack
without standing up a new database: OpenCost lives on top of Prometheus, not beside it.&lt;/p>
&lt;p>An operational note: historical analysis depends on Prometheus retention. For cost reports
spanning months, a long-term backend behind it (Thanos, Mimir, VictoriaMetrics) is advisable, or
periodically exporting the allocations to a store of your own.&lt;/p>
&lt;hr>
&lt;h2 id="on-prem-prices-in-euros-the-piece-that-decides-everything">On-prem prices in euros: the piece that decides everything&lt;/h2>
&lt;p>On-prem, the most important figure, and the most neglected, is the &lt;strong>node price&lt;/strong>. It is configured
per provider; the options are &lt;code>alibaba&lt;/code>, &lt;code>aws&lt;/code>, &lt;code>azure&lt;/code>, &lt;code>gcp&lt;/code>, &lt;code>oracle&lt;/code>, &lt;strong>&lt;code>ovh&lt;/code>&lt;/strong> or &lt;strong>&lt;code>default&lt;/code>&lt;/strong>
(on-prem) (&lt;a href="https://opencost.io/docs/configuration/">OpenCost · configuration&lt;/a>).
For on-prem you use &lt;code>default&lt;/code>, supplying base prices in a &lt;code>default.json&lt;/code> (or by overriding the Helm
&lt;code>values.yaml&lt;/code>); anything you do not override uses the chart&amp;rsquo;s value (&lt;a href="https://opencost.io/docs/configuration/on-prem/">OpenCost · on-prem&lt;/a>).&lt;/p>
&lt;p>To set the node price in euros, the calculation is the TCO model one: depreciated capex + opex. An
example of an &lt;strong>8×H100&lt;/strong> node:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Component&lt;/th>
&lt;th>Calculation&lt;/th>
&lt;th>€/hour&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Capex (node ~240,000 €, 36 months)&lt;/td>
&lt;td>240,000 ÷ 26,280 h&lt;/td>
&lt;td>~9.13&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Energy (5.6 kW × PUE 1.4 × 0.058 €/kWh, France)&lt;/td>
&lt;td>~0.455 €/h&lt;/td>
&lt;td>~0.46&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Maintenance, networking, operations&lt;/td>
&lt;td>estimate&lt;/td>
&lt;td>~1.5&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Node total&lt;/strong>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;strong>~11.1 €/h&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>That ~11 €/h is the number you put into OpenCost, and everything is split from it. And here is
the documented trap: with the default pricing configuration, OpenCost &lt;strong>under-prices CPU and
GPU on-prem&lt;/strong> (&lt;a href="https://github.com/opencost/opencost/issues/3781">issue #3781&lt;/a>).
If you do not adjust the base prices to your real cost, the split will be systematically low and
the cost per token you report will be unreal. &lt;strong>Configuring the node price is not optional: it is
the figure everything else hangs from.&lt;/strong>&lt;/p>
&lt;hr>
&lt;h3 id="configuring-the-price-an-example">Configuring the price: an example&lt;/h3>
&lt;p>The base price is given in the Helm &lt;code>values.yaml&lt;/code> (or a &lt;code>default.json&lt;/code>), in euros per hour and per
unit of resource, derived from the node&amp;rsquo;s total cost:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># values.yaml — on-prem prices in €/hour (8xH100 node, France)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">opencost&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">customPricing&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">enabled&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">provider&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">custom&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">costModel&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">description&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;On-prem 8xH100 node&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">CPU&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;0.030&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># €/CPU-hour&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">RAM&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;0.004&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># €/GB-hour&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">GPU&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;1.30&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># €/GPU-hour ← the figure that moves cost/token the most&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">storage&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;0.0002&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># €/GB-hour&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>OpenCost normalises those values so that, multiplied by the node capacity, they add up to the
total price you declared (~11 €/h in the example). The number to be most careful with is the
&lt;strong>GPU&lt;/strong> one: at 1.30 €/GPU-hour, the 8 cards are ~10.4 €/h, the bulk of the node&amp;rsquo;s cost. A badly set
GPU base shifts the whole cost per token. Always review the resolved prices in
&lt;code>/allNodePricing&lt;/code> after configuring, because the default under-pricing bug bites exactly here.&lt;/p>
&lt;hr>
&lt;h2 id="allocation-by-usage-not-by-presence">Allocation: by usage, not by presence&lt;/h2>
&lt;p>The behaviour that has changed most and that matters most for GPU: in recent versions, &lt;strong>the cost
allocated to a GPU is determined by its usage, not its presence&lt;/strong>. If a GPU costing 100 €/month is
only half used, the allocated cost is ~50 € ([search]). This aligns allocation with reality,
you pay for what you use, but it has a consequence: the other half does not disappear, it is
&lt;strong>idle&lt;/strong> that somebody keeps paying for. OpenCost exposes it, and that is where the optimisation
lever comes from.&lt;/p>
&lt;p>OpenCost allocates to any Kubernetes dimension: &lt;strong>cluster, node, namespace, controller
(deployment, statefulset and so on), service, pod and container&lt;/strong>, and by &lt;strong>labels&lt;/strong> (team,
product). Access is through the Allocation API and several endpoints that expose the mechanics:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Endpoint&lt;/th>
&lt;th>What it returns&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>/allocation&lt;/code>&lt;/td>
&lt;td>cost allocated by the dimension you ask for&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>/costDataModel&lt;/code>&lt;/td>
&lt;td>the resolved node price&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>/allNodePricing&lt;/code>&lt;/td>
&lt;td>hourly prices per node&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>/pricingSourceSummary&lt;/code>&lt;/td>
&lt;td>summary of the pricing source&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>/metrics&lt;/code>&lt;/td>
&lt;td>cost metrics for Prometheus&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The &lt;code>/metrics&lt;/code> endpoint is what turns OpenCost into a Prometheus exporter: once there, you can
write PromQL to calculate the cost and efficiency of any Kubernetes concept and build panels in
Grafana (&lt;a href="https://opencost.io/docs/integrations/opencost-exporter/">OpenCost · exporter&lt;/a>).&lt;/p>
&lt;h3 id="allocation-api-parameters">Allocation API parameters&lt;/h3>
&lt;p>&lt;code>/allocation&lt;/code> is controlled by a handful of parameters worth knowing, because they define which
number you get:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Parameter&lt;/th>
&lt;th>What it controls&lt;/th>
&lt;th>Example&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>window&lt;/code>&lt;/td>
&lt;td>time range&lt;/td>
&lt;td>&lt;code>7d&lt;/code>, &lt;code>today&lt;/code>, specific dates&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>aggregate&lt;/code>&lt;/td>
&lt;td>aggregation dimension&lt;/td>
&lt;td>&lt;code>namespace&lt;/code>, &lt;code>label:team&lt;/code>, &lt;code>pod&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>accumulate&lt;/code>&lt;/td>
&lt;td>sum the range or break it per interval&lt;/td>
&lt;td>&lt;code>true&lt;/code> (total) / &lt;code>false&lt;/code> (series)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>idle&lt;/code>&lt;/td>
&lt;td>include idle cost or not&lt;/td>
&lt;td>&lt;code>true&lt;/code> / &lt;code>false&lt;/code> / &lt;code>separate&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>filter&lt;/code>&lt;/td>
&lt;td>filter by namespace, label and so on&lt;/td>
&lt;td>&lt;code>namespace:&amp;quot;llm-prod&amp;quot;&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The &lt;strong>&lt;code>idle&lt;/code>&lt;/strong> parameter is the most revealing: with &lt;code>idle=separate&lt;/code>, OpenCost returns the idle
cost as a separate row, which lets you see at a glance how much is being paid for unused
capacity. A query with &lt;code>aggregate=label:team&lt;/code> and &lt;code>idle=separate&lt;/code> over &lt;code>window=30d&lt;/code> is, literally,
the monthly chargeback report with the waste highlighted.&lt;/p>
&lt;hr>
&lt;h2 id="gpu-in-depth-dcgm-usage-mig-and-idle">GPU in depth: DCGM, usage, MIG and idle&lt;/h2>
&lt;p>The GPU is the expensive resource, so its allocation deserves detail:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Usage signal&lt;/strong>: &lt;code>dcgm-exporter&lt;/code> gives &lt;code>DCGM_FI_DEV_GPU_UTIL&lt;/code> (utilisation), &lt;code>DCGM_FI_DEV_FB_USED&lt;/code>
(memory), and power. OpenCost uses utilisation to allocate by usage.&lt;/li>
&lt;li>&lt;strong>MIG (Multi-Instance GPU)&lt;/strong>: an A100 is split into up to 7 isolated instances, which allows
cutting idle from 50 % to almost 0 % by sharing one card across small workloads
([search]; see &lt;a href="https://blog.lo0.es/en/posts/sharing-one-gpu-time-slicing-mps-mig/">sharing the GPU: time-slicing, MPS and MIG&lt;/a>).
The cost allocation of each MIG instance follows the same usage logic.&lt;/li>
&lt;li>&lt;strong>Idle&lt;/strong>: the reliable detection pattern combines three pieces, DCGM metrics in Prometheus, an
alert that fires when &lt;code>DCGM_FI_DEV_GPU_UTIL &amp;lt; 10&lt;/code> for more than 15 minutes, and routing of
that alert to the team that owns the namespace ([search]).&lt;/li>
&lt;/ul>
&lt;p>An attribution nuance worth knowing: MIG creates isolated instances that Kubernetes sees as
distinct resources, so OpenCost allocates them cleanly, each one to its pod. Time-slicing, by
contrast, shares the same physical GPU across several pods with no compute isolation: there,
per-pod utilisation is harder to separate, and cost attribution becomes approximate (the GPU is
split across the pods sharing it, according to their measured usage). The practical rule: if you
need exact per-team chargeback on a shared GPU, MIG gives cleaner attribution than
time-slicing, at the price of rigid fixed partitions. It is a trade-off between cost accuracy and
flexibility that is better decided before, not after, building the split.&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 220" role="img" aria-label="GPU allocation by usage in OpenCost: used cost charged to the pod, idle cost flagged to the namespace owner, and MIG splitting one card across workloads" xmlns="http://www.w3.org/2000/svg">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.3}.dsh{fill:none;stroke:currentColor;stroke-width:1.3;stroke-dasharray:5 3}.tl{font:600 12px sans-serif;fill:currentColor}.ts{font:11px sans-serif;fill:currentColor}.ar{fill:none;stroke:currentColor;stroke-width:1.3;marker-end:url(#om2)}&lt;/style>
&lt;defs>&lt;marker id="om2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="currentColor"/>&lt;/marker>&lt;/defs>
&lt;rect class="bx" x="20" y="40" width="170" height="70" rx="6"/>
&lt;text x="32" y="63" class="tl">GPU (DCGM)&lt;/text>
&lt;text x="32" y="81" class="ts">util, memory, power&lt;/text>
&lt;text x="32" y="98" class="ts">→ to Prometheus&lt;/text>
&lt;path class="ar" d="M190,75 L235,60"/>
&lt;path class="ar" d="M190,80 L235,150"/>
&lt;rect class="bx" x="235" y="34" width="240" height="44" rx="6"/>
&lt;text x="247" y="54" class="tl">USED cost → pod / team&lt;/text>
&lt;text x="247" y="70" class="ts">charged by utilisation&lt;/text>
&lt;rect class="dsh" x="235" y="128" width="240" height="44" rx="6"/>
&lt;text x="247" y="148" class="tl">IDLE cost → namespace owner&lt;/text>
&lt;text x="247" y="164" class="ts">alert util &amp;lt; 10 % &amp;gt; 15 min&lt;/text>
&lt;rect class="bx" x="520" y="80" width="240" height="50" rx="6"/>
&lt;text x="532" y="100" class="tl">MIG: 1 A100 → up to 7&lt;/text>
&lt;text x="532" y="118" class="ts">splits the card, idle 50%→~0&lt;/text>
&lt;text x="20" y="200" class="ts">Usage allocation reveals idle; MIG and scheduling recover it. OpenCost measures; other layers act.&lt;/text>
&lt;/svg>
&lt;/div>
&lt;hr>
&lt;h2 id="efficiency-the-number-that-triggers-action">Efficiency: the number that triggers action&lt;/h2>
&lt;p>With cost allocated by usage, OpenCost lets you calculate efficiency: how much of the allocated
cost corresponds to useful work against the total. A GPU at 30 % utilisation has an efficiency of
30 %: the remaining 70 % is idle cost that somebody pays without receiving work. That figure, per
team and per namespace, is what turns a dashboard into a chargeback conversation: &amp;ldquo;your namespace
has 5 GPUs allocated at 25 % efficiency; either you raise utilisation or you release three cards&amp;rdquo;.
It connects with &lt;a href="https://blog.lo0.es/en/posts/finops-multi-tenancy-gpu-cluster-who-pays/">FinOps and multi-tenancy&lt;/a>:
allocation locates the waste; scheduling and co-residency recover it.&lt;/p>
&lt;hr>
&lt;h2 id="worked-example-the-allocation-report">Worked example: the allocation report&lt;/h2>
&lt;p>On the ~11 €/h node (8×H100), three teams share the cluster for a month. What a
&lt;code>/allocation?aggregate=label:team&amp;amp;idle=separate&amp;amp;window=30d&lt;/code> query returns:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Team&lt;/th>
&lt;th>GPU-hours&lt;/th>
&lt;th>Avg. util.&lt;/th>
&lt;th>Allocated cost (€)&lt;/th>
&lt;th>Efficiency&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>A · chat-prod&lt;/td>
&lt;td>2,880 (4 GPU)&lt;/td>
&lt;td>78 %&lt;/td>
&lt;td>~3,744&lt;/td>
&lt;td>high&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>B · batch&lt;/td>
&lt;td>1,440 (2 GPU)&lt;/td>
&lt;td>55 %&lt;/td>
&lt;td>~1,872&lt;/td>
&lt;td>medium&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>C · experimentation&lt;/td>
&lt;td>1,440 (2 GPU)&lt;/td>
&lt;td>22 %&lt;/td>
&lt;td>~1,872&lt;/td>
&lt;td>&lt;strong>low&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>idle&lt;/strong> (separate)&lt;/td>
&lt;td>—&lt;/td>
&lt;td>—&lt;/td>
&lt;td>&lt;strong>~1,100&lt;/strong>&lt;/td>
&lt;td>—&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>What the report reveals: the cost allocated to A, B and C comes out of their GPU-hours and the node
price; but the &lt;strong>idle&lt;/strong> row (~1,100 €/month) is capacity nobody used and everyone pays for in the
pro rata. And the efficiency column points at C: 2 GPUs at 22 % is ~1,560 € a month of which
only ~340 € is useful work. That is the actionable figure: C does not have a model problem, it has
a utilisation problem. Without OpenCost, those ~1,100 € of idle and C&amp;rsquo;s inefficiency stay diluted
in an aggregate invoice nobody questions. With it, they are a named row and a number in euros.&lt;/p>
&lt;hr>
&lt;h2 id="useful-promql-queries">Useful PromQL queries&lt;/h2>
&lt;p>As a Prometheus exporter, OpenCost lets you build the panels by hand. Some reference queries (exact
metric names vary by version; check in your &lt;code>/metrics&lt;/code>):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-promql" data-lang="promql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Cost per node and hour (resolved price)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nv">node_total_hourly_cost&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1"># GPU cost allocated per namespace (€/hour)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">sum&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">by&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nv">namespace&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nv">container_gpu_allocation&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">on&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nv">node&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">group_left&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nv">node_gpu_hourly_cost&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1"># Average GPU utilisation per namespace (for efficiency)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">avg&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">by&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nv">namespace&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nv">DCGM_FI_DEV_GPU_UTIL&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1"># Idle GPUs: utilisation &amp;lt; 10 % sustained&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nv">DCGM_FI_DEV_GPU_UTIL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The last one, wrapped in an alert with &lt;code>for: 15m&lt;/code> and routed to the namespace owner, is the
standard idle detection pattern. With these series in Grafana you have cost per team, efficiency
and waste on one dashboard, the &amp;ldquo;Inform report&amp;rdquo; of the FinOps phase in real time.&lt;/p>
&lt;hr>
&lt;h2 id="dynamic-cloud-prices-vs-custom-on-prem-prices">Dynamic cloud prices vs custom on-prem prices&lt;/h2>
&lt;p>The difference is worth understanding, because it changes how reliable the figure is:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Cloud&lt;/strong> (aws/azure/gcp/oracle/ovh): OpenCost obtains the price dynamically from the
provider&amp;rsquo;s billing API, so the allocated cost reflects the real rate (including discounts, spot
and so on) without you declaring anything.&lt;/li>
&lt;li>&lt;strong>On-prem&lt;/strong> (&lt;code>default&lt;/code>/&lt;code>custom&lt;/code>): you declare the price, derived from your TCO model. The
advantage is total control; the risk is that a badly calculated price skews everything. That is
why the TCO model (depreciated capex + opex) is not an academic exercise: it is literally
OpenCost&amp;rsquo;s &lt;em>input&lt;/em>.&lt;/li>
&lt;/ul>
&lt;p>The &lt;strong>&lt;code>ovh&lt;/code>&lt;/strong> provider among those supported is relevant for a European platform: if part of the
workload goes to a European sovereign cloud, OpenCost can allocate its cost with dynamic pricing,
and mix it with on-prem in the same allocation view.&lt;/p>
&lt;hr>
&lt;h2 id="from-pod-cost-to-cost-per-token">From pod cost to cost per token&lt;/h2>
&lt;p>OpenCost gets as far as &amp;ldquo;this vLLM pod cost X €/hour&amp;rdquo;. The &lt;strong>cost per token&lt;/strong>, the metric that
compares on-prem against cloud, needs one more piece: the &lt;strong>gateway&lt;/strong> (LiteLLM) that counts tokens
per request and per team, as covered in the &lt;a href="https://blog.lo0.es/en/posts/gpu-llm-finops-frameworks-metrics-state-of-the-art/">FinOps introduction&lt;/a>.
The full chain is: OpenCost gives the pod cost by usage → the gateway gives the tokens per team →
dividing them gives the cost per token per team. OpenCost is the hardware half of that
equation; without it, you would know the tokens but not their real cost. That is why A2 (allocation)
is the foundation the whole cost per token of the proposal hangs from.&lt;/p>
&lt;hr>
&lt;h2 id="deployment-helm--prometheus">Deployment: Helm + Prometheus&lt;/h2>
&lt;p>The typical deployment is via Helm, with Prometheus as a prerequisite (for scraping and
series storage). Conceptual steps:&lt;/p>
&lt;ol>
&lt;li>Have Prometheus with &lt;code>kube-state-metrics&lt;/code>, &lt;code>node-exporter&lt;/code>, &lt;code>cAdvisor&lt;/code> and &lt;code>dcgm-exporter&lt;/code>.&lt;/li>
&lt;li>Install OpenCost with Helm, pointing it at your Prometheus.&lt;/li>
&lt;li>Configure the &lt;strong>node price&lt;/strong> in euros (&lt;code>default.json&lt;/code> / &lt;code>values.yaml&lt;/code>), the step most often
neglected.&lt;/li>
&lt;li>Expose &lt;code>/metrics&lt;/code> and build panels in Grafana with PromQL over the cost metrics.&lt;/li>
&lt;/ol>
&lt;p>As a Prometheus exporter, OpenCost lets you &lt;strong>write PromQL for the cost and efficiency of any
concept&lt;/strong> and create bespoke dashboards (&lt;a href="https://opencost.io/docs/integrations/opencost-exporter/">OpenCost · exporter&lt;/a>).
It reuses infrastructure a GPU cluster already has for observability (DCGM, Prometheus,
Grafana): OpenCost only adds the pricing and allocation layer.&lt;/p>
&lt;hr>
&lt;h2 id="opencost-vs-kubecost-just-the-essentials">OpenCost vs Kubecost (just the essentials)&lt;/h2>
&lt;p>OpenCost is the free base; Kubecost (IBM) is the commercial product built on it. Briefly,
what the commercial one adds:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Capability&lt;/th>
&lt;th>OpenCost&lt;/th>
&lt;th>Kubecost&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Cost allocation (CPU/GPU/mem/PV)&lt;/td>
&lt;td>✓&lt;/td>
&lt;td>✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GPU via DCGM&lt;/td>
&lt;td>✓&lt;/td>
&lt;td>✓ (3.0)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Long historical retention&lt;/td>
&lt;td>depends on your Prometheus&lt;/td>
&lt;td>included&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Automatic rightsizing&lt;/td>
&lt;td>—&lt;/td>
&lt;td>✓ (Turbonomic)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Governance, alerts, enterprise RBAC&lt;/td>
&lt;td>basic&lt;/td>
&lt;td>✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Commercial support&lt;/td>
&lt;td>community&lt;/td>
&lt;td>✓ (IBM)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>For most on-prem clusters with a platform team, OpenCost covers allocation; Kubecost adds value
when you want automated optimisation and enterprise features without operating it yourself. The
detail of the comparison, and when paying is worth it, is article &lt;strong>A3&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h2 id="implementation-checklist">Implementation checklist&lt;/h2>
&lt;p>For OpenCost to give a defensible per-team cost, order matters:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Step&lt;/th>
&lt;th>Action&lt;/th>
&lt;th>Verification&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>1&lt;/td>
&lt;td>Prometheus with kube-state-metrics, node-exporter, cAdvisor&lt;/td>
&lt;td>series present&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2&lt;/td>
&lt;td>NVIDIA GPU Operator + dcgm-exporter&lt;/td>
&lt;td>&lt;code>DCGM_FI_DEV_GPU_UTIL&lt;/code> in Prometheus&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3&lt;/td>
&lt;td>Install OpenCost (Helm) pointing at Prometheus&lt;/td>
&lt;td>&lt;code>/allocation&lt;/code> responds&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>4&lt;/td>
&lt;td>&lt;strong>Configure the node price in €&lt;/strong> (capex+opex)&lt;/td>
&lt;td>&lt;code>/allNodePricing&lt;/code> shows the real one&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>5&lt;/td>
&lt;td>Label pods/namespaces by team and product&lt;/td>
&lt;td>&lt;code>aggregate=label:team&lt;/code> splits correctly&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>6&lt;/td>
&lt;td>Grafana panels for cost, efficiency and idle&lt;/td>
&lt;td>the waste is visible&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>7&lt;/td>
&lt;td>Idle alert (&lt;code>util&amp;lt;10&lt;/code> &lt;code>for: 15m&lt;/code>) to the owner&lt;/td>
&lt;td>the alert reaches the team&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>8&lt;/td>
&lt;td>(optional) long-retention backend&lt;/td>
&lt;td>months of history available&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Step 4 is the one most often skipped and the one that most invalidates the result. If you only do
one thing right, make it that one: without the correct node price in euros, steps 5 to 7 split a
wrong cost with great precision.&lt;/p>
&lt;hr>
&lt;h2 id="limits-and-traps-data-driven">Limits and traps (data-driven)&lt;/h2>
&lt;ol>
&lt;li>&lt;strong>Default under-pricing.&lt;/strong> With the default pricing config, OpenCost underestimates on-prem
CPU and GPU (&lt;a href="https://github.com/opencost/opencost/issues/3781">#3781&lt;/a>). Adjust the base prices
to your real cost or the whole split will come out low.&lt;/li>
&lt;li>&lt;strong>It depends on Prometheus and DCGM.&lt;/strong> Without the right series (especially &lt;code>dcgm-exporter&lt;/code>),
there is no GPU allocation. Input data quality rules.&lt;/li>
&lt;li>&lt;strong>Allocation by usage ≠ total cost.&lt;/strong> A GPU at 50 % charges half; the other half is idle that
still costs. Do not confuse &amp;ldquo;allocated cost&amp;rdquo; with &amp;ldquo;cost paid&amp;rdquo;.&lt;/li>
&lt;li>&lt;strong>Prometheus retention.&lt;/strong> Historical cost analysis depends on how much your Prometheus retains;
for long series, plan a long-term backend.&lt;/li>
&lt;li>&lt;strong>Allocation, not per-token measurement.&lt;/strong> OpenCost reaches the pod; cost per token needs the
gateway (LiteLLM) above it, as covered in the FinOps introduction.&lt;/li>
&lt;/ol>
&lt;p>With OpenCost&amp;rsquo;s mechanics clear, the next article (A3) compares what Kubecost and the commercial
alternatives add, to decide which FinOps stack to adopt. But the foundation, correct allocation
with the node price properly set in euros, is this one.&lt;/p>
&lt;h2 id="closing">Closing&lt;/h2>
&lt;p>OpenCost looks like &amp;ldquo;installing a cost tool&amp;rdquo; and is, in reality, &lt;strong>declaring what your hardware is
worth and letting it split itself&lt;/strong>. That simplicity is its strength, it lives on top of the
Prometheus and DCGM you already have, with no new database, and also its trap: the split is only as
good as the node price you declare, and the default value under-prices on-prem GPU. Properly
configured in euros, OpenCost turns the cluster&amp;rsquo;s aggregate invoice into a split per team, per
product and per GPU, separates used cost from idle, and exposes the efficiency that triggers
chargeback conversations. It is the hardware half of the cost per token equation, the other half,
the tokens, is supplied by the gateway, and without this half there is no TCO model or on-prem vs
cloud comparison that holds up. For a sovereign architecture proposal, OpenCost is the piece that
makes cost stop being an intuition and become a table in euros, reproducible and auditable, with
the idle GPU named and identified. The &lt;em>Inform&lt;/em> phase of GPU FinOps starts here, and everything
else hangs on this number being right.&lt;/p>
&lt;h2 id="see-also">See also&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/gpu-chargeback-showback-multi-tenancy/">GPU chargeback and showback&lt;/a> — from OpenCost&amp;rsquo;s cost allocation to the per-team split with budgets (Kueue) and token attribution (LiteLLM).&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/gpu-idle-cost-not-on-any-invoice/">GPU utilisation as a FinOps lever&lt;/a> — the cost of idle and how occupancy moves the allocated cost.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/full-tco-on-premise-gpu-cluster-capex-gpu-hour-break-even/">TCO of an on-premise GPU cluster: depreciation, energy and infrastructure&lt;/a> — the CAPEX, the depreciation and the €/GPU-hour that OpenCost needs as a base price when the node is not cloud but your own on-premise hardware.&lt;/li>
&lt;/ul>
&lt;h2 id="sources">Sources&lt;/h2>
&lt;ul>
&lt;li>OpenCost · GitHub (CNCF, Apache 2.0) — &lt;a href="https://github.com/opencost/opencost">https://github.com/opencost/opencost&lt;/a>&lt;/li>
&lt;li>OpenCost · configuración (proveedores: aws/azure/gcp/oracle/ovh/default) — &lt;a href="https://opencost.io/docs/configuration/">https://opencost.io/docs/configuration/&lt;/a>&lt;/li>
&lt;li>OpenCost · configuración on-prem (precios personalizados) — &lt;a href="https://opencost.io/docs/configuration/on-prem/">https://opencost.io/docs/configuration/on-prem/&lt;/a>&lt;/li>
&lt;li>OpenCost · exportador de Prometheus — &lt;a href="https://opencost.io/docs/integrations/opencost-exporter/">https://opencost.io/docs/integrations/opencost-exporter/&lt;/a>&lt;/li>
&lt;li>OpenCost · issue #3781 (infra-precio CPU/GPU on-prem por defecto) — &lt;a href="https://github.com/opencost/opencost/issues/3781">https://github.com/opencost/opencost/issues/3781&lt;/a>&lt;/li>
&lt;li>NVIDIA · dcgm-exporter — &lt;a href="https://github.com/NVIDIA/dcgm-exporter">https://github.com/NVIDIA/dcgm-exporter&lt;/a>&lt;/li>
&lt;li>Grafana · manage costs (OpenCost + Kubernetes) — &lt;a href="https://grafana.com/docs/grafana-cloud/monitor-infrastructure/kubernetes-monitoring/manage-costs/">https://grafana.com/docs/grafana-cloud/monitor-infrastructure/kubernetes-monitoring/manage-costs/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Cost, performance and energy: the three axes of LLM inference and the identity that ties them together</title><link>https://blog.lo0.es/en/posts/cost-performance-energy-three-axes-llm-inference/</link><pubDate>Sat, 13 Jun 2026 03:30:00 +0200</pubDate><guid>https://blog.lo0.es/en/posts/cost-performance-energy-three-axes-llm-inference/</guid><description>&lt;blockquote>
&lt;p>Notation: amounts in &lt;strong>euros (N €)&lt;/strong>, decimals with a point. Cost and energy references are
European (France, Germany, Spain), because the aim is to defend a sovereign European
architecture; when a source quotes prices in dollars it is flagged as &amp;ldquo;USD&amp;rdquo;. The dollar
symbol is not used (on this site it is a formula delimiter).&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-this-series-is">What this series is&lt;/h2>
&lt;p>This is the first instalment of a series of &lt;strong>28 data articles&lt;/strong> (not opinion pieces)
about the three dimensions that decide whether a sovereign European LLM platform is
sustainable: &lt;strong>cost (FinOps)&lt;/strong>, &lt;strong>performance (benchmarking)&lt;/strong> and &lt;strong>energy&lt;/strong>. The aim
is to have a body of verifiable figures, with their methodology, to support architecture
decisions with numbers rather than intuition. This first article sets the
frame: what each axis measures, &lt;strong>how they link to one another&lt;/strong>, an end-to-end worked
example with European data and the 2026 references that will be used as a base.&lt;/p>
&lt;p>The quantitative thesis, in one sentence: the three axes &lt;strong>are not independent&lt;/strong>. Throughput
(tokens per second) is the common denominator of cost per token and energy per token, and
energy is, on top of that, between &lt;strong>30 % and 50 %&lt;/strong> of the cost itself. Optimising one moves
the other two. And in Europe, where the cluster sits (France, Germany, Spain) changes
both the electricity cost and the carbon per token at once.&lt;/p>
&lt;hr>
&lt;h2 id="the-three-axes-and-their-metrics">The three axes and their metrics&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Axis&lt;/th>
&lt;th>Question&lt;/th>
&lt;th>Key metrics&lt;/th>
&lt;th>Unit&lt;/th>
&lt;th>Tool family&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Cost (FinOps)&lt;/td>
&lt;td>How much does each unit of work cost?&lt;/td>
&lt;td>cost/1M tokens (CPM), cost/request, €/GPU-hour, utilisation&lt;/td>
&lt;td>€/1M tok, €/h&lt;/td>
&lt;td>OpenCost, Kubecost, CloudZero&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Performance&lt;/td>
&lt;td>How much work per second and at what latency?&lt;/td>
&lt;td>TTFT, TPOT/ITL, throughput, goodput, P50/P99&lt;/td>
&lt;td>tok/s, ms&lt;/td>
&lt;td>GuideLLM, AIPerf, LLMPerf, MLPerf Inference&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Energy&lt;/td>
&lt;td>How many watts per unit of work?&lt;/td>
&lt;td>energy/token, average power, PUE, carbon&lt;/td>
&lt;td>J/tok, Wh, kWh, gCO₂&lt;/td>
&lt;td>Zeus, CodeCarbon, MLPerf Power, Kepler&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Each axis has its own in-depth introductory article in this same batch; here only how they
fit together matters. It is worth breaking each one down a little more before crossing them.&lt;/p>
&lt;h3 id="cost-axis-in-detail">Cost axis in detail&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Metric&lt;/th>
&lt;th>Definition&lt;/th>
&lt;th>Why it matters&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>CPM (cost/1M tokens)&lt;/td>
&lt;td>cluster cost ÷ tokens produced&lt;/td>
&lt;td>the comparable unit on-prem vs cloud&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cost per request&lt;/td>
&lt;td>cost attributed to one complete request&lt;/td>
&lt;td>billing by real usage&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>€/GPU-hour&lt;/td>
&lt;td>hourly cost of a GPU (amortised or rented)&lt;/td>
&lt;td>the basis of any calculation&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Utilisation&lt;/td>
&lt;td>fraction of the GPU actually used&lt;/td>
&lt;td>the lever that moves everything else&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Fixed vs variable cost&lt;/td>
&lt;td>amortised capex vs energy/operations&lt;/td>
&lt;td>defines the break-even point&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="performance-axis-in-detail">Performance axis in detail&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Metric&lt;/th>
&lt;th>Definition&lt;/th>
&lt;th>Dominant phase&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>TTFT&lt;/td>
&lt;td>latency to the first token&lt;/td>
&lt;td>prefill&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>TPOT / ITL&lt;/td>
&lt;td>time between output tokens&lt;/td>
&lt;td>decode&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Throughput&lt;/td>
&lt;td>tokens or requests per second of the system&lt;/td>
&lt;td>both&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Goodput&lt;/td>
&lt;td>throughput that meets the SLO (not the raw figure)&lt;/td>
&lt;td>the honest number&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>P95 / P99&lt;/td>
&lt;td>latency percentiles (not the mean)&lt;/td>
&lt;td>the real experience&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="energy-axis-in-detail">Energy axis in detail&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Metric&lt;/th>
&lt;th>Definition&lt;/th>
&lt;th>Unit&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Average power&lt;/td>
&lt;td>average instantaneous consumption&lt;/td>
&lt;td>W&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Energy per token&lt;/td>
&lt;td>energy per generated token&lt;/td>
&lt;td>J/token&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Total energy&lt;/td>
&lt;td>integral of power over time&lt;/td>
&lt;td>Wh, kWh&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PUE&lt;/td>
&lt;td>datacenter overhead (cooling, losses)&lt;/td>
&lt;td>ratio ≥ 1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Carbon&lt;/td>
&lt;td>energy × grid intensity&lt;/td>
&lt;td>gCO₂eq&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="the-identity-that-ties-the-three-axes-together-throughput">The identity that ties the three axes together: throughput&lt;/h2>
&lt;p>The &lt;strong>cost per million tokens (CPM)&lt;/strong> is the cluster&amp;rsquo;s cost divided by what it
produces:&lt;/p>
$$\text{CPM} = \frac{\text{cluster cost per hour}}{\text{throughput (tok/s)} \times 3600 / 10^6}$$
&lt;p>&lt;strong>Energy per token&lt;/strong> has exactly the same form, with power in the
numerator:&lt;/p>
$$\text{energy per token (J)} = \frac{\text{average power (W)}}{\text{throughput (tok/s)}}$$
&lt;p>The two share the &lt;strong>same denominator&lt;/strong>: throughput. Hence the key operational consequence:
&lt;strong>every performance improvement that raises tokens/s lowers both cost per token and
energy per token at the same time&lt;/strong>. These are not three separate optimisations; throughput moves them together.
That is why performance benchmarking (axis 2) is, at bottom, also a FinOps tool and an
energy efficiency tool.&lt;/p>
&lt;p>The CPM↔throughput relationship is direct: the higher the throughput, the lower the CPM. It is the identity on
which the whole cost analysis of the series is built.&lt;/p>
&lt;hr>
&lt;h2 id="worked-example-unit-economics-of-an-8h100-node-in-europe">Worked example: unit economics of an 8×H100 node in Europe&lt;/h2>
&lt;p>To see the identity in action, an end-to-end calculation on an example node
(8×H100 SXM, Llama 3.1 70B FP16 served with vLLM), with European prices. Order-of-magnitude
figures; the methodology is what matters.&lt;/p>
&lt;p>&lt;strong>Starting data:&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Parameter&lt;/th>
&lt;th>Value&lt;/th>
&lt;th>Source&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>European cloud rental (Scaleway, per H100)&lt;/td>
&lt;td>2.73 €/h&lt;/td>
&lt;td>&lt;a href="https://www.scaleway.com/en/h100/">Scaleway&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>→ 8×H100 node on-demand&lt;/td>
&lt;td>~21.84 €/h&lt;/td>
&lt;td>8 × 2.73&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Amortised on-prem cost (estimate)&lt;/td>
&lt;td>~10–13 €/h&lt;/td>
&lt;td>capex 24–36 months + opex&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Throughput&lt;/td>
&lt;td>2,800 tok/s&lt;/td>
&lt;td>reference benchmark&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Power per H100 (TDP)&lt;/td>
&lt;td>up to 700 W&lt;/td>
&lt;td>&lt;a href="https://www.nvidia.com/en-us/data-center/h100/">NVIDIA&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Reference PUE&lt;/td>
&lt;td>1.4&lt;/td>
&lt;td>typical of a datacenter&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Cost per million tokens.&lt;/strong> At 2,800 tok/s, hourly throughput is
2,800 × 3,600 / 10⁶ = 10.08 million tokens/hour. Therefore:&lt;/p>
$$\text{CPM (cloud)} = \frac{21{.}84}{10{.}08} \approx 2{.}17 \text{ €/1M tok}$$
&lt;p>With the amortised on-prem cost (~11 €/h) it drops to &lt;strong>~1.09 €/1M tok&lt;/strong>, the difference between
renting from a provider and amortising your own iron.&lt;/p>
&lt;p>&lt;strong>Energy per token.&lt;/strong> The power of the 8 cards is 8 × 700 = 5,600 W (board only).
Over 2,800 tok/s:&lt;/p>
$$\text{energy per token} = \frac{5,600}{2,800} = 2 \text{ J/token (board)}$$
&lt;p>Over a million tokens that is 2 × 10⁶ J = 0.56 kWh (GPU only). Applying PUE 1.4: ~0.78
kWh per million tokens. At French wholesale prices (~0.058 €/kWh) that is ~0.045 € of
electricity per million tokens; in Germany (~0.091 €/kWh), ~0.071 €, some 57 % more, because of
the grid, not the model.&lt;/p>
&lt;p>&lt;strong>The link.&lt;/strong> If a serving optimisation raised throughput from 2,800 to 4,200 tok/s
(+50 %), the CPM would fall to ~1.45 €/1M tok and energy per token to ~1.33 J, both at
once, through the same denominator. That is the identity, with numbers.&lt;/p>
&lt;hr>
&lt;h2 id="energy-is-inside-the-cost">Energy is &lt;em>inside&lt;/em> the cost&lt;/h2>
&lt;p>The second link: energy is not an axis separate from cost, it is &lt;strong>a large component of
it&lt;/strong>. 2026 reference data:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Figure&lt;/th>
&lt;th>Value&lt;/th>
&lt;th>Source&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Electricity as a share of TCO (5 years)&lt;/td>
&lt;td>30–40 % (&amp;gt;50 % at high density 24/7)&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/ai-inference-power-electricity-cost-2026/">Spheron&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Day-ahead wholesale France (Mar-2026)&lt;/td>
&lt;td>~58 €/MWh (0.058 €/kWh)&lt;/td>
&lt;td>&lt;a href="https://tradingeconomics.com/france/electricity-price">TradingEconomics&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Day-ahead wholesale Spain&lt;/td>
&lt;td>~54 €/MWh (0.054 €/kWh)&lt;/td>
&lt;td>&lt;a href="https://tradingeconomics.com/spain/electricity-price">TradingEconomics&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Day-ahead wholesale Germany&lt;/td>
&lt;td>~91 €/MWh (0.091 €/kWh)&lt;/td>
&lt;td>&lt;a href="https://www.tradingeconomics.com/germany/electricity-price">TradingEconomics&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Electricity cost/token at 80 % util&lt;/td>
&lt;td>¼ of that at 20 % util&lt;/td>
&lt;td>&lt;a href="https://www.spheron.network/blog/ai-inference-power-electricity-cost-2026/">Spheron&lt;/a>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>A methodological warning: the &lt;strong>wholesale&lt;/strong> price (day-ahead) is not what a company pays; &lt;strong>industrial
retail&lt;/strong> adds grid charges and taxes (roughly ×1.5–2). But the relative
ordering holds: &lt;strong>France and Spain, cheap; Germany, expensive.&lt;/strong>&lt;/p>
&lt;p>The third link, utilisation, closes the triangle: power is consumed even when the
GPU is idle, so spreading the fixed cost (energy included) over more tokens (more
throughput, more occupancy) &lt;strong>lowers all three axes at once&lt;/strong>. The electricity cost per token at
80 % utilisation is a quarter of what it is at 20 %.&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 300" role="img" aria-label="The three axes (cost, performance, energy) linked by throughput at the centre; energy is also a component of cost" xmlns="http://www.w3.org/2000/svg">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.3}.tl{font:600 13px sans-serif;fill:currentColor}.ts{font:11px sans-serif;fill:currentColor}.ar{fill:none;stroke:currentColor;stroke-width:1.2;stroke-dasharray:4 3}&lt;/style>
&lt;rect class="bx" x="300" y="120" width="180" height="60" rx="8"/>
&lt;text x="390" y="146" text-anchor="middle" class="tl">THROUGHPUT&lt;/text>
&lt;text x="390" y="164" text-anchor="middle" class="ts">tokens/s · common denominator&lt;/text>
&lt;rect class="bx" x="40" y="40" width="200" height="64" rx="8"/>
&lt;text x="56" y="63" class="tl">Cost (FinOps)&lt;/text>
&lt;text x="56" y="81" class="ts">CPM = cost/h ÷ throughput&lt;/text>
&lt;text x="56" y="97" class="ts">€/1M tok · utilisation&lt;/text>
&lt;rect class="bx" x="540" y="40" width="200" height="64" rx="8"/>
&lt;text x="556" y="63" class="tl">Performance&lt;/text>
&lt;text x="556" y="81" class="ts">TTFT · TPOT · goodput&lt;/text>
&lt;text x="556" y="97" class="ts">raise tok/s -> lower the others&lt;/text>
&lt;rect class="bx" x="290" y="220" width="200" height="60" rx="8"/>
&lt;text x="306" y="243" class="tl">Energy&lt;/text>
&lt;text x="306" y="261" class="ts">J/tok = W ÷ throughput&lt;/text>
&lt;line class="ar" x1="240" y1="92" x2="300" y2="135"/>
&lt;line class="ar" x1="540" y1="92" x2="480" y2="135"/>
&lt;line class="ar" x1="390" y1="180" x2="390" y2="220"/>
&lt;line class="ar" x1="140" y1="104" x2="320" y2="220"/>
&lt;text x="150" y="170" class="ts">energy = 30-50 % of cost (TCO)&lt;/text>
&lt;text x="40" y="295" class="ts">Improving throughput lowers cost/token and energy/token at once; raising utilisation spreads fixed cost over more tokens.&lt;/text>
&lt;/svg>
&lt;/div>
&lt;hr>
&lt;h2 id="sensitivity-analysis-what-moves-the-cost-per-token">Sensitivity analysis: what moves the cost per token&lt;/h2>
&lt;p>Four levers dominate the CPM, and it is worth knowing their elasticity before defending
any figure:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Lever&lt;/th>
&lt;th>Change&lt;/th>
&lt;th>Effect on CPM&lt;/th>
&lt;th>Mechanism&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Throughput&lt;/td>
&lt;td>+50 % tok/s&lt;/td>
&lt;td>−33 % CPM&lt;/td>
&lt;td>direct denominator&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Utilisation&lt;/td>
&lt;td>20 % → 80 %&lt;/td>
&lt;td>up to −75 % on the electricity portion&lt;/td>
&lt;td>spreads fixed cost&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Location (energy)&lt;/td>
&lt;td>Germany → France&lt;/td>
&lt;td>lowers the electricity portion (~37 % less €/kWh)&lt;/td>
&lt;td>wholesale FR &amp;lt; DE&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Amortise vs rent&lt;/td>
&lt;td>21.84 → 11 €/h&lt;/td>
&lt;td>−50 % CPM&lt;/td>
&lt;td>removes the provider&amp;rsquo;s margin&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Quantisation (FP8/INT4)&lt;/td>
&lt;td>+throughput, −VRAM/KV&lt;/td>
&lt;td>lowers CPM and J/token&lt;/td>
&lt;td>same identity&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The reading: the CPM &lt;strong>is not a number, it is a function&lt;/strong> of utilisation, throughput, location
(energy price) and ownership model. Any on-prem vs cloud comparison that does not
fix those assumptions is not comparable, and that is exactly the job of the synthesis article.&lt;/p>
&lt;hr>
&lt;h2 id="simplified-tco-model-on-premise">Simplified TCO model (on-premise)&lt;/h2>
&lt;p>The real on-premise cost is not the GPU alone: it is &lt;strong>amortised capex + opex&lt;/strong>, over a hardware
cycle of 24–36 months. Reference structure of an inference node:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Component&lt;/th>
&lt;th>Type&lt;/th>
&lt;th>Typical weight&lt;/th>
&lt;th>Notes&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>GPUs&lt;/td>
&lt;td>capex&lt;/td>
&lt;td>the bulk&lt;/td>
&lt;td>amortise over 24–36 months&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Server, network, BMC&lt;/td>
&lt;td>capex&lt;/td>
&lt;td>medium&lt;/td>
&lt;td>NICs, switching, out-of-band management&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Electricity&lt;/td>
&lt;td>opex&lt;/td>
&lt;td>30–50 % of TCO&lt;/td>
&lt;td>depending on country (FR/ES cheap, DE expensive) and PUE&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cooling&lt;/td>
&lt;td>opex&lt;/td>
&lt;td>inside the PUE&lt;/td>
&lt;td>PUE 1.2–1.6 typical&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Maintenance and support&lt;/td>
&lt;td>opex&lt;/td>
&lt;td>medium&lt;/td>
&lt;td>spares, contracts, RMA&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Staff and operations&lt;/td>
&lt;td>opex&lt;/td>
&lt;td>variable&lt;/td>
&lt;td>the hidden cost that cloud rental includes&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The difference between cloud rental (21.84 €/h in the example) and the real amortised cost
(~10–13 €/h) is this breakdown: the provider charges a margin for absorbing capex,
cooling and operations. The break-even point appears when sustained volume
exceeds &lt;strong>~2M tokens/day&lt;/strong>, with typical amortisation in &lt;strong>6–12 months&lt;/strong>
(&lt;a href="https://www.spheron.network/blog/ai-inference-cost-economics-2026/">Spheron&lt;/a>). Below
that threshold, renting usually wins; above it, amortising. Article A8 develops the
full model and synthesis S3 connects it with sizing.&lt;/p>
&lt;hr>
&lt;h2 id="reference-the-price-of-a-gpu-hour-in-european-sovereign-cloud">Reference: the price of a GPU-hour in European sovereign cloud&lt;/h2>
&lt;p>The other side of the on-prem comparison is the price of renting the GPU. For a
sovereign European platform, what matters is not AWS/GCP/Azure (subject to the &lt;strong>US CLOUD
Act&lt;/strong>), but the European providers:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Provider&lt;/th>
&lt;th>H100 (€/hour)&lt;/th>
&lt;th>Location&lt;/th>
&lt;th>Sovereignty&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Scaleway&lt;/td>
&lt;td>from 2.73&lt;/td>
&lt;td>Paris, Warsaw&lt;/td>
&lt;td>EU jurisdiction, exempt from US CLOUD Act&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>OVHcloud&lt;/td>
&lt;td>+40–45 % vs Scaleway&lt;/td>
&lt;td>France, Germany&lt;/td>
&lt;td>EU jurisdiction, exempt from US CLOUD Act&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Hyperscalers (reference)&lt;/td>
&lt;td>3–7 USD&lt;/td>
&lt;td>global&lt;/td>
&lt;td>subject to US CLOUD Act&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The data: Scaleway offers the H100 from &lt;strong>2.73 €/h&lt;/strong> in European datacenters (Paris, Warsaw),
with models and data under EU jurisdiction, immune to extraterritorial regulation; OVHcloud
serves it from France and Germany, ~40–45 % more expensive than Scaleway as of March 2026. Both are
&lt;strong>exempt from the US CLOUD Act&lt;/strong>, the foundational sovereignty requirement for data subject to
GDPR (&lt;a href="https://www.scaleway.com/en/h100/">Scaleway&lt;/a>, &lt;a href="https://deploybase.ai/articles/ovhcloud-gpu-pricing">DeployBase&lt;/a>).&lt;/p>
&lt;p>The implication for the proposal: the comparison is not only cost, it is &lt;strong>cost + sovereignty&lt;/strong>. An
on-prem cluster of your own or a European cloud keep the data under EU jurisdiction; a
US hyperscaler does not, however cheap the GPU-hour comes out. That is the leg that the
cost axis on its own does not capture, and that synthesis S2 quantifies.&lt;/p>
&lt;hr>
&lt;h2 id="energy-and-carbon-reference-france-germany-spain">Energy and carbon reference: France, Germany, Spain&lt;/h2>
&lt;p>Here the choice of European country changes both the electricity cost and the carbon per
token. It is the most important table for a sovereign proposal:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Country&lt;/th>
&lt;th>Wholesale (€/MWh, Mar-2026)&lt;/th>
&lt;th>Grid carbon (gCO₂/kWh)&lt;/th>
&lt;th>Profile&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>France&lt;/td>
&lt;td>~58&lt;/td>
&lt;td>~20–60&lt;/td>
&lt;td>nuclear: cheap and very clean&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Spain&lt;/td>
&lt;td>~54&lt;/td>
&lt;td>~150–170&lt;/td>
&lt;td>renewables + gas: cheap, medium carbon&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Germany&lt;/td>
&lt;td>~91&lt;/td>
&lt;td>~363 (2024 average)&lt;/td>
&lt;td>coal/gas + renewables: expensive and dirty&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Sources: day-ahead wholesale prices (&lt;a href="https://tradingeconomics.com/france/electricity-price">TradingEconomics&lt;/a>),
German carbon ~363 gCO₂/kWh (2024 average, &lt;a href="https://en.wikipedia.org/wiki/Electricity_sector_in_Germany">Statista via Wikipedia&lt;/a>),
nuclear ~17–35 gCO₂e/kWh (&lt;a href="https://www.eea.europa.eu/en/analysis/indicators/greenhouse-gas-emission-intensity-of-1">EEA&lt;/a>),
Spain ~167 gCO₂/kWh (2020). The PUE multiplies all of that energy: at 1.4, for every kWh of
GPU the datacenter spends 1.4 kWh with cooling included.&lt;/p>
&lt;p>The calculation, on the example node (0.78 kWh per million tokens, with PUE): in
France (~40 gCO₂/kWh) it is &lt;strong>~31 gCO₂ per million tokens&lt;/strong>; in Germany (~363
gCO₂/kWh), &lt;strong>~283 gCO₂ per million&lt;/strong>, a factor of &lt;strong>~9×&lt;/strong>. That is, the same load
served in France emits of the order of &lt;strong>9× less carbon per token&lt;/strong> than in Germany, and
costs ~37 % less in electricity, &lt;strong>without touching a single line of the stack&lt;/strong>, purely because of the
country&amp;rsquo;s grid. &lt;strong>Siting the cluster in France
or Spain is at once a cost lever, a carbon lever and a compliance lever.&lt;/strong> Hourly grid
intensity is obtained from ElectricityMaps, the reference source for these
calculations.&lt;/p>
&lt;p>Scale context: global datacenters consumed &lt;strong>~460 TWh in 2025&lt;/strong> (≈1.8 % of the world&amp;rsquo;s
electricity), and the IEA projects passing &lt;strong>800 TWh in 2028&lt;/strong>, which is why energy
efficiency per token stops being a detail and becomes a design constraint,
and why carbon enters the European regulatory equation.&lt;/p>
&lt;hr>
&lt;h2 id="sovereignty-and-regulation-the-axis-that-does-not-appear-in-the-cpm">Sovereignty and regulation: the axis that does not appear in the CPM&lt;/h2>
&lt;p>For a European platform there is a fourth factor that the three numerical axes &lt;strong>do not
capture&lt;/strong>, and that can invalidate the lowest cost figure: jurisdiction. Three
fronts that turn &amp;ldquo;the cheapest GPU-hour&amp;rdquo; into &amp;ldquo;the cheapest GPU-hour &lt;em>that complies&lt;/em>&amp;rdquo;:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Front&lt;/th>
&lt;th>What it requires&lt;/th>
&lt;th>Architectural implication&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>GDPR + US CLOUD Act&lt;/td>
&lt;td>personal data under a jurisdiction accessible to US authorities is exposed&lt;/td>
&lt;td>European cloud (Scaleway/OVHcloud) or on-prem keep the data in the EU&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>EU AI Act&lt;/td>
&lt;td>obligations for high-risk systems (traceability, control, documentation)&lt;/td>
&lt;td>on-prem control makes auditability and compliance easier&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CSRD / emissions reporting&lt;/td>
&lt;td>reporting the carbon footprint (scope 2)&lt;/td>
&lt;td>a low-carbon grid (France) improves the reported figure&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The key point for the proposal: a US hyperscaler can offer a competitive
GPU-hour, but it &lt;strong>puts the data under the US CLOUD Act&lt;/strong>, which for data subject to GDPR
is a compliance risk, not a saving. That is why this series&amp;rsquo; comparison is not made
in USD against US providers: it is made in euros, against options that keep
&lt;strong>data sovereignty&lt;/strong>, on-prem of your own or European cloud. Cost, energy and
carbon are measurable axes; sovereignty is the constraint that decides &lt;strong>which options even
enter the table&lt;/strong>. This links with &lt;a href="https://blog.lo0.es/en/posts/eu-ai-act-technical-dossier-on-premise-llm-architecture/">the mapping of the EU AI Act onto the on-premise
architecture&lt;/a> and with &lt;a href="https://blog.lo0.es/en/posts/technical-controls-ens-iso-42001-eu-ai-act-cross-mapping/">the technical
controls ENS × ISO 42001 × EU AI Act&lt;/a>.&lt;/p>
&lt;p>The operational conclusion: &lt;strong>France and Spain not only come out cheaper and cleaner than
Germany; being EU jurisdiction, they meet the sovereignty constraint that a US
hyperscaler does not meet at any price.&lt;/strong> Three advantages that reinforce each other, and that the
proposal can defend with the same table.&lt;/p>
&lt;hr>
&lt;h2 id="methodology-how-to-read-and-not-fool-yourself-with-these-figures">Methodology: how to read (and not fool yourself with) these figures&lt;/h2>
&lt;p>Five warnings that apply to all the data in this series:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Board vs node.&lt;/strong> The GPU&amp;rsquo;s power (700 W per H100) is the floor; the complete
node (CPU, NICs, power supplies, fans) consumes more. Always compare the same boundary.&lt;/li>
&lt;li>&lt;strong>Wholesale vs retail.&lt;/strong> The day-ahead price is not what the company pays; industrial
retail adds grid charges and taxes. Use whichever matches your contract.&lt;/li>
&lt;li>&lt;strong>Measured vs estimated.&lt;/strong> A wattmeter figure and an &lt;code>nvidia-smi&lt;/code> estimate are not
the same thing; the difference is quantified in the energy articles.&lt;/li>
&lt;li>&lt;strong>Amortisation assumptions.&lt;/strong> The on-prem cost/hour depends on the hardware cycle (24 vs
36 months) and on the assumed utilisation; changing those assumptions moves the CPM more than
any serving optimisation.&lt;/li>
&lt;li>&lt;strong>Comparability.&lt;/strong> Two CPM or J/token figures are only comparable if model,
precision, context length, load &lt;strong>and country&lt;/strong> match. Most public
comparisons (and in USD) do not fix those assumptions, which is why the series reproduces the ones that matter
with its own harness and European data.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="the-combined-scorecard">The combined scorecard&lt;/h2>
&lt;p>The destination of the series is a single board where each model/configuration/location is
scored on all the axes at once, to decide on a &lt;strong>Pareto frontier&lt;/strong> rather than on a
single number. An &lt;strong>illustrative&lt;/strong> example (order-of-magnitude figures) on the same
8×H100 node:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Candidate&lt;/th>
&lt;th>CPM (€/1M)&lt;/th>
&lt;th>Goodput (tok/s)&lt;/th>
&lt;th>Energy (J/tok)&lt;/th>
&lt;th>Carbon (gCO₂/1M)&lt;/th>
&lt;th>Sovereignty&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>A · 70B FP16, on-prem France&lt;/td>
&lt;td>~1.09&lt;/td>
&lt;td>~2,800&lt;/td>
&lt;td>~2.0&lt;/td>
&lt;td>low (grid ~40 gCO₂/kWh)&lt;/td>
&lt;td>EU&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>B · 70B FP8, on-prem France&lt;/td>
&lt;td>~0.75&lt;/td>
&lt;td>~4,000&lt;/td>
&lt;td>~1.4&lt;/td>
&lt;td>lower&lt;/td>
&lt;td>EU&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>C · Scaleway on-demand&lt;/td>
&lt;td>~2.17&lt;/td>
&lt;td>~2,800&lt;/td>
&lt;td>n/a&lt;/td>
&lt;td>low (FR)&lt;/td>
&lt;td>EU&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>D · US hyperscaler&lt;/td>
&lt;td>~3.0&lt;/td>
&lt;td>~2,800&lt;/td>
&lt;td>n/a&lt;/td>
&lt;td>depends on region&lt;/td>
&lt;td>not EU&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The reading: &lt;strong>B dominates A&lt;/strong> on the three intrinsic axes thanks to FP8. C (European cloud) is
more expensive per token but shifts capex and operations, while keeping sovereignty. D
(hyperscaler) may be competitive on cost but &lt;strong>loses sovereignty&lt;/strong>, an axis that for
GDPR data is not negotiable. No isolated number decides: the choice comes from &lt;strong>seeing
the whole row&lt;/strong>.&lt;/p>
&lt;p>The trap the board avoids: &lt;strong>optimising a single axis&lt;/strong>. Each cell will be filled in by an
article in the series with measured data.&lt;/p>
&lt;hr>
&lt;h2 id="where-each-axis-is-measured-from-design-to-production">Where each axis is measured: from design to production&lt;/h2>
&lt;p>Each axis is measured at three different moments, with different tools and increasing
precision. Confusing them is a common source of figures that do not add up:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Axis&lt;/th>
&lt;th>Design (estimate)&lt;/th>
&lt;th>Benchmark (controlled)&lt;/th>
&lt;th>Production (continuous)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Cost&lt;/td>
&lt;td>datasheet, GPU-hour price&lt;/td>
&lt;td>CPM measured over synthetic load&lt;/td>
&lt;td>OpenCost/Kubecost per team and token&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Performance&lt;/td>
&lt;td>theoretical throughput (roofline)&lt;/td>
&lt;td>GuideLLM/AIPerf with SLO&lt;/td>
&lt;td>OTel/Langfuse over real traffic&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Energy&lt;/td>
&lt;td>TDP × hours&lt;/td>
&lt;td>Zeus/MLPerf Power on the bench&lt;/td>
&lt;td>Kepler/DCGM per pod in the cluster&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The rule: a design figure serves for sizing, but not for defending a
proposal; for that you need a reproducible benchmark figure, and to operate, a
continuous production one. When someone disputes a number in the proposal, the answer is not
&amp;ldquo;a blog says so&amp;rdquo;, but &amp;ldquo;here is the bench, here is the methodology, reproduce it&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="glossary-of-units-and-conversions">Glossary of units and conversions&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Quantity&lt;/th>
&lt;th>Unit&lt;/th>
&lt;th>Conversion / reference&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Energy&lt;/td>
&lt;td>joule (J), watt-hour (Wh)&lt;/td>
&lt;td>1 Wh = 3,600 J; 1 kWh = 3.6 MJ = 3,600,000 J&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Power&lt;/td>
&lt;td>watt (W)&lt;/td>
&lt;td>1 W = 1 J/s; H100 ≈ 700 W TDP&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Energy per token&lt;/td>
&lt;td>J/token&lt;/td>
&lt;td>= average power (W) ÷ throughput (tok/s)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Throughput&lt;/td>
&lt;td>tok/s&lt;/td>
&lt;td>tokens generated per second by the system&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cost per token&lt;/td>
&lt;td>€/1M tok (CPM)&lt;/td>
&lt;td>= cost/hour ÷ (tok/s × 3,600 / 10⁶)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Latency&lt;/td>
&lt;td>ms&lt;/td>
&lt;td>TTFT (first token), TPOT/ITL (between tokens)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PUE&lt;/td>
&lt;td>ratio (≥1)&lt;/td>
&lt;td>total DC energy ÷ compute energy&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Carbon&lt;/td>
&lt;td>gCO₂eq&lt;/td>
&lt;td>= energy (kWh) × PUE × grid intensity (gCO₂/kWh)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>A useful conversion to keep in your head: at a constant 700 W, an H100 consumes ~0.7 kWh per hour, ~16.8
kWh a day and ~6,130 kWh a year (board only, no PUE). Multiplied by the PUE and your country&amp;rsquo;s
energy price (FR ~0.058, ES ~0.054, DE ~0.091 €/kWh wholesale), that number is the basis
of the electricity portion of the TCO.&lt;/p>
&lt;hr>
&lt;h2 id="how-the-rest-of-the-series-fits-together">How the rest of the series fits together&lt;/h2>
&lt;p>The 28 articles group into four tracks; this is the map:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Track&lt;/th>
&lt;th>What it contributes&lt;/th>
&lt;th>Closes with&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>FinOps (8)&lt;/td>
&lt;td>measuring and allocating cost, TCO model&lt;/td>
&lt;td>cost/token comparable on-prem vs European cloud&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LLM benchmarking (8)&lt;/td>
&lt;td>measuring performance reproducibly&lt;/td>
&lt;td>Pareto frontier of engines&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Energy (8)&lt;/td>
&lt;td>measuring watts and carbon per token&lt;/td>
&lt;td>energy in the TCO and in European regulation&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Synthesis (4)&lt;/td>
&lt;td>crossing the three axes&lt;/td>
&lt;td>scorecard, on-prem vs cloud, sizing, harness&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The three articles that follow this one in the batch introduce each axis in depth:
their frameworks and the state of the art. From there, each article adds a layer of data
to the final scorecard.&lt;/p>
&lt;p>The thread running through all 28 instalments is the one in this article: &lt;strong>cost, performance and energy
are a single problem with three faces, tied together by throughput and by the energy that lives
inside the cost, and in Europe, by the country where the cluster sits&lt;/strong>. Anyone who optimises one
face while ignoring the others will end up with a pretty number and a platform that does not hold up.
The work of the series is to produce the figures, measured, dated, European and reproducible,
that allow a sovereign architecture to be defended before any committee with the only answer
that closes a technical debate: the data and the bench to reproduce it.&lt;/p>
&lt;h2 id="see-also">See also&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/llm-serving-engines-pareto-frontier-comparison/">Comparison of serving engines (vLLM/SGLang/TRT-LLM/Dynamo)&lt;/a> — the synthesis of the performance axis: which engine wins at each point of the goodput-latency frontier with measured data.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/llm-quality-benchmarks-contamination-trap-oss-tools/">LLM quality benchmarks: MMLU, MT-Bench, HELM and the leaderboards that matter&lt;/a> — the quality dimension that complements performance: how public leaderboards let you calibrate the right model for each SLO.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/llm-energy-leaderboards-wh-per-token/">LLM energy efficiency leaderboards&lt;/a> — the synthesis of the energy axis: the J/token rankings and how to read them without being fooled by the benchmark&amp;rsquo;s setup.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/cloud-gpu-on-demand-reserved-spot-neoclouds-2026-prices/">Cloud GPU: comparison of prices, commitment and sovereign neoclouds&lt;/a> — the cost axis piece for cloud or hybrid architectures: on-demand, spot and reserved prices from European providers in 2026.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/full-tco-on-premise-gpu-cluster-capex-gpu-hour-break-even/">TCO of the on-premise GPU cluster: amortisation, energy and infrastructure&lt;/a> — the cost axis piece for your own architectures: the real €/GPU-hour with CAPEX, energy and operations broken down.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/measurement-bias-reproducibility-llm-benchmarks/">Measurement bias and reproducibility in LLM benchmarks&lt;/a> — the methodological traps that make two benchmarks of the same engine give incompatible results: the basis for trusting this series&amp;rsquo; data.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/watt-to-carbon-pue-grid-intensity-gco2-per-token/">From the watt to carbon: PUE, grid intensity and the real cost of a token&lt;/a> — how to convert the GPU&amp;rsquo;s watts into gCO₂eq and into electricity euros, closing the loop between the energy axis and the cost axis.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/levers-fewer-watts-per-token-llm-inference/">Energy efficiency levers in LLM inference&lt;/a> — the optimisations that move all three axes at once: quantisation, batching and engine as levers that reduce J/token, €/token and improve goodput simultaneously.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/reproducible-harness-cost-performance-energy-experiment/">The reproducible harness for measuring cost, performance and energy&lt;/a> — the test bench that makes this article&amp;rsquo;s methodology concrete: how to produce the data for the three axes with a single reproducible experiment.&lt;/li>
&lt;/ul>
&lt;h2 id="sources">Sources&lt;/h2>
&lt;ul>
&lt;li>Scaleway · H100 GPU instance (€/h price, EU sovereignty) — &lt;a href="https://www.scaleway.com/en/h100/">https://www.scaleway.com/en/h100/&lt;/a>&lt;/li>
&lt;li>DeployBase · OVHcloud GPU Pricing (European sovereignty) — &lt;a href="https://deploybase.ai/articles/ovhcloud-gpu-pricing">https://deploybase.ai/articles/ovhcloud-gpu-pricing&lt;/a>&lt;/li>
&lt;li>TradingEconomics · electricity price France / Germany / Spain — &lt;a href="https://tradingeconomics.com/france/electricity-price">https://tradingeconomics.com/france/electricity-price&lt;/a>&lt;/li>
&lt;li>EEA · emission intensity of electricity in Europe — &lt;a href="https://www.eea.europa.eu/en/analysis/indicators/greenhouse-gas-emission-intensity-of-1">https://www.eea.europa.eu/en/analysis/indicators/greenhouse-gas-emission-intensity-of-1&lt;/a>&lt;/li>
&lt;li>Electricity sector in Germany (carbon ~363 gCO₂/kWh) — &lt;a href="https://en.wikipedia.org/wiki/Electricity_sector_in_Germany">https://en.wikipedia.org/wiki/Electricity_sector_in_Germany&lt;/a>&lt;/li>
&lt;li>Spheron · AI Inference Power &amp;amp; Cost Economics (2026) — &lt;a href="https://www.spheron.network/blog/ai-inference-cost-economics-2026/">https://www.spheron.network/blog/ai-inference-cost-economics-2026/&lt;/a>&lt;/li>
&lt;li>NVIDIA · H100 (TDP/specs) — &lt;a href="https://www.nvidia.com/en-us/data-center/h100/">https://www.nvidia.com/en-us/data-center/h100/&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>GPU/LLM FinOps: frameworks, metrics and state of the art (tool by tool)</title><link>https://blog.lo0.es/en/posts/gpu-llm-finops-frameworks-metrics-state-of-the-art/</link><pubDate>Sat, 13 Jun 2026 03:00:00 +0200</pubDate><guid>https://blog.lo0.es/en/posts/gpu-llm-finops-frameworks-metrics-state-of-the-art/</guid><description>&lt;blockquote>
&lt;p>Notation: amounts in &lt;strong>euros (N €)&lt;/strong>, decimals with a point. The references are &lt;strong>European&lt;/strong>
(providers and prices from FR/DE/ES), as this is a sovereign proposal; when a source quotes
dollars it is marked &amp;ldquo;USD&amp;rdquo;. The dollar sign is not used (on this site it is a formula
delimiter).&lt;/p>
&lt;/blockquote>
&lt;h2 id="what-this-introduction-covers">What this introduction covers&lt;/h2>
&lt;p>Second article in the data series, and the first pillar &lt;em>deep dive&lt;/em>. Here the &lt;strong>FinOps&lt;/strong> tooling
for GPU/LLM infrastructure is inventoried with the detail an architecture decision needs: what
metrics each tool handles, how it allocates cost internally, what GPU support it has, under
what licence and pricing model it operates, and where its limits are. It is the natural
continuation of the &lt;a href="https://blog.lo0.es/en/posts/cost-performance-energy-three-axes-llm-inference/">opening article&lt;/a>,
which established that cost per token is the unit that allows on-prem and cloud to be compared.
No recommendations: the final choice is settled in the synthesis article with the Pareto table;
here there are only facts and methodology.&lt;/p>
&lt;hr>
&lt;h2 id="why-gpu-finops-is-a-different-problem">Why GPU FinOps is a different problem&lt;/h2>
&lt;p>Classic cloud FinOps was born to split CPU, memory and storage, cheap and elastic resources. The
GPU breaks three assumptions at once, and that is why it needs its own treatment:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>It is expensive and discrete.&lt;/strong> An H100 costs on the order of 2.7 to 7 €/hour in European
cloud (Scaleway from 2.73 €/h; OVHcloud somewhat more), or its depreciated capex on-prem; it is
not a resource that gets split in trivial fractions. A 10 % allocation error across a GPU fleet
is real money.&lt;/li>
&lt;li>&lt;strong>It is easily underused.&lt;/strong> Unlike the CPU, a GPU draws its power even when idle, and
average occupancy in ungoverned clusters is notoriously low. The cost of &lt;em>idle&lt;/em>, the GPU
powered on without working, is waste number one, and it is invisible if it is not measured.&lt;/li>
&lt;li>&lt;strong>It is hard to attribute.&lt;/strong> Who owns the cost of a GPU shared by &lt;em>time-slicing&lt;/em> or MIG across
several pods from several teams? Without an allocation layer, GPU spend is a black hole nobody
claims and nobody optimises.&lt;/li>
&lt;/ol>
&lt;p>The goal of GPU FinOps is to turn that black hole into an attributed, measured and actionable
invoice: knowing what each team, each model and, in the end, each token costs.&lt;/p>
&lt;hr>
&lt;h2 id="the-finops-metrics">The FinOps metrics&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Metric&lt;/th>
&lt;th>Definition&lt;/th>
&lt;th>Unit&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>CPM&lt;/strong> (cost/1M tokens)&lt;/td>
&lt;td>cluster cost ÷ tokens produced&lt;/td>
&lt;td>€ / 1M tok&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cost per request&lt;/td>
&lt;td>cost charged to a complete request&lt;/td>
&lt;td>€ / req&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>€/GPU-hour&lt;/td>
&lt;td>hourly cost of a GPU (depreciated or rented)&lt;/td>
&lt;td>€/h&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Utilisation&lt;/strong>&lt;/td>
&lt;td>fraction of the GPU actually used&lt;/td>
&lt;td>% (MFU, GPU-hour util)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cost of idle&lt;/td>
&lt;td>GPU powered on without useful work&lt;/td>
&lt;td>€/h wasted&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Showback&lt;/td>
&lt;td>reporting the cost to each team (without charging)&lt;/td>
&lt;td>€ / team&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Chargeback&lt;/td>
&lt;td>charging the cost to each team&lt;/td>
&lt;td>€ / team&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cost efficiency&lt;/td>
&lt;td>real cost ÷ cost if it were at 100 %&lt;/td>
&lt;td>%&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The metric that closes the loop with the business is cost per token (or per request): it is the
only one that can be compared across providers, across models and against the price of an external
API. All GPU FinOps tooling exists to reach, one way or another, that number.&lt;/p>
&lt;hr>
&lt;h2 id="the-three-finops-phases-finops-foundation">The three FinOps phases (FinOps Foundation)&lt;/h2>
&lt;p>The FinOps Foundation framework organises the work in a three-phase cycle. Applied to GPU:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Phase&lt;/th>
&lt;th>Goal&lt;/th>
&lt;th>Typical GPU action&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Inform&lt;/strong>&lt;/td>
&lt;td>visibility and allocation&lt;/td>
&lt;td>measure cost per namespace/team/model/token&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Optimize&lt;/strong>&lt;/td>
&lt;td>reduce spend&lt;/td>
&lt;td>rightsizing, spot, quotas, switch off the idle, quantise&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Operate&lt;/strong>&lt;/td>
&lt;td>continuous governance&lt;/td>
&lt;td>budgets, idle alerts, automated chargeback&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The common mistake is jumping to &lt;em>Optimize&lt;/em> without having done &lt;em>Inform&lt;/em>: GPUs get bought or
replicas get adjusted without knowing where the money really goes. Allocation (the Inform phase) is
the prerequisite for everything else, and it is where the tooling comes in.&lt;/p>
&lt;hr>
&lt;h2 id="how-cost-is-allocated-in-kubernetes-opencosts-mechanics">How cost is allocated in Kubernetes: OpenCost&amp;rsquo;s mechanics&lt;/h2>
&lt;p>OpenCost is the de facto standard, so it is worth understanding how it allocates cost,
because that defines what any tool on top can and cannot do. It is a vendor-neutral, Apache 2.0
project, originally built by Kubecost and donated to the CNCF (incubating project)
(&lt;a href="https://github.com/opencost/opencost">OpenCost · GitHub&lt;/a>).&lt;/p>
&lt;p>The model works at node level: it starts from the node&amp;rsquo;s resource capacity (CPU, RAM, GPU,
storage) and its total price, and splits that price across the resources. When the provider does
not give explicit CPU/GPU/RAM prices, OpenCost uses the ratio of a set of base prices (the
provider&amp;rsquo;s marginal rates, customisable) and &lt;strong>normalises them so that the sum of the components
equals the node&amp;rsquo;s total price&lt;/strong>
(&lt;a href="https://opencost.io/docs/configuration/on-prem/">OpenCost · on-prem&lt;/a>). This is key on-premise:
you define the node cost (depreciated capex + opex) and OpenCost splits it.&lt;/p>
&lt;p>It obtains utilisation by scraping Prometheus: &lt;code>kube-state-metrics&lt;/code>, &lt;code>node-exporter&lt;/code>
and &lt;code>cAdvisor&lt;/code> give it the real consumption per pod, and with that it allocates cost per
cluster, node, namespace, controller, service or pod (&lt;a href="https://opencost.io/docs/integrations/opencost-exporter/">OpenCost · exporter&lt;/a>).
For the GPU, the signal comes from DCGM (via the NVIDIA GPU Operator) exported to
Prometheus, the same base as &lt;a href="https://blog.lo0.es/en/posts/gpu-observability-llm-inference-twelve-dcgm-vllm-metrics/">GPU observability&lt;/a>.
A common idle detection pattern: an alert when &lt;code>DCGM_FI_DEV_GPU_UTIL &amp;lt; 10&lt;/code>
for more than 15 minutes, routed to the team that owns the namespace.&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 250" role="img" aria-label="OpenCost cost allocation mechanics: utilisation metrics from Prometheus and DCGM, node-level pricing model, and allocation per namespace, team and pod" xmlns="http://www.w3.org/2000/svg">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.3}.dsh{fill:none;stroke:currentColor;stroke-width:1.3;stroke-dasharray:5 3}.tl{font:600 12px sans-serif;fill:currentColor}.ts{font:11px sans-serif;fill:currentColor}.ar{fill:none;stroke:currentColor;stroke-width:1.3;marker-end:url(#fm)}&lt;/style>
&lt;defs>&lt;marker id="fm" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="currentColor"/>&lt;/marker>&lt;/defs>
&lt;text x="20" y="26" class="tl">Utilisation metrics (Prometheus)&lt;/text>
&lt;rect class="bx" x="20" y="36" width="200" height="84" rx="6"/>
&lt;text x="32" y="58" class="ts">kube-state-metrics&lt;/text>
&lt;text x="32" y="76" class="ts">node-exporter · cAdvisor&lt;/text>
&lt;text x="32" y="94" class="ts">DCGM (GPU, via GPU Operator)&lt;/text>
&lt;text x="32" y="112" class="ts">usage per pod&lt;/text>
&lt;path class="ar" d="M220,78 L265,78"/>
&lt;rect class="bx" x="265" y="36" width="210" height="84" rx="6"/>
&lt;text x="277" y="58" class="tl">Pricing model (per node)&lt;/text>
&lt;text x="277" y="78" class="ts">total node price split&lt;/text>
&lt;text x="277" y="96" class="ts">across CPU/GPU/RAM/disk&lt;/text>
&lt;text x="277" y="114" class="ts">normalised so sum = total&lt;/text>
&lt;path class="ar" d="M475,78 L520,78"/>
&lt;rect class="bx" x="520" y="36" width="240" height="84" rx="6"/>
&lt;text x="532" y="58" class="tl">Allocation&lt;/text>
&lt;text x="532" y="78" class="ts">per cluster / node / namespace&lt;/text>
&lt;text x="532" y="96" class="ts">controller / service / pod&lt;/text>
&lt;text x="532" y="114" class="ts">→ cost per team&lt;/text>
&lt;rect class="dsh" x="20" y="150" width="740" height="74" rx="6"/>
&lt;text x="34" y="172" class="tl">On-premise you define the node cost (depreciated capex + opex); OpenCost only splits it.&lt;/text>
&lt;text x="34" y="192" class="ts">GPU idle: alert if DCGM_FI_DEV_GPU_UTIL &amp;lt; 10 for &amp;gt;15 min → route to the namespace owner.&lt;/text>
&lt;text x="34" y="210" class="ts">What OpenCost allocates per resource, the layers above enrich into cost per token.&lt;/text>
&lt;/svg>
&lt;/div>
&lt;hr>
&lt;h2 id="frameworks-tool-by-tool">Frameworks, tool by tool&lt;/h2>
&lt;h3 id="opencost--the-allocation-standard-cncf-apache-20">OpenCost — the allocation standard (CNCF, Apache 2.0)&lt;/h3>
&lt;p>What it measures: allocated cost of in-cluster resources (CPU, GPU, memory, volumes) by any
Kubernetes dimension. Method: node-level pricing model + Prometheus scraping (above). GPU: yes,
via DCGM. Licence: Apache 2.0, free. It can run as a metrics exporter to Prometheus with no
further dependencies. Limit: it is the allocation layer, it brings no optimisation, governance
or product unit economics; for that you put something on top of it.&lt;/p>
&lt;h3 id="kubecost--the-commercial-product-on-opencost-ibm">Kubecost — the commercial product on OpenCost (IBM)&lt;/h3>
&lt;p>What it measures: what OpenCost does (it is built on it) plus enterprise capabilities. &lt;strong>Kubecost
3.0 (2025)&lt;/strong> added GPU monitoring via &lt;strong>NVIDIA DCGM&lt;/strong> and integration with &lt;strong>IBM
Turbonomic&lt;/strong> for automatic &lt;em>rightsizing&lt;/em>, and extended its scope from Kubernetes to cloud service
cost. IBM acquired Kubecost and integrated Kubecost/OpenCost into its FinOps Suite alongside
Cloudability and Turbonomic (&lt;a href="https://www.cloudzero.com/blog/kubecost-vs-opencost/">CloudZero · Kubecost vs OpenCost&lt;/a>).
Differentiator: rightsizing, governance, support. Limit: it is a commercial product; the bulk of
the value over free OpenCost is the optimisation and enterprise layer.&lt;/p>
&lt;h3 id="cloudzero--unit-economics-and-cost-per-product">CloudZero — unit economics and cost per product&lt;/h3>
&lt;p>What it measures: it maps cloud cost to features, products, teams and customers, not just to
resources. Method: it ingests multi-cloud billing and models it in business dimensions.
Differentiator: the deepest allocation and the unit economics approach (cost per business
unit). Limit: less focused on intra-Kubernetes mechanics than OpenCost/Kubecost; it is the
&amp;ldquo;cost to business&amp;rdquo; layer.&lt;/p>
&lt;h3 id="vantage--multi-cloud-with-many-integrations">Vantage — multi-cloud with many integrations&lt;/h3>
&lt;p>What it measures: multi-cloud cost with more than 20 native integrations (AWS, Azure, GCP,
Kubernetes, Snowflake, Datadog, OpenAI and so on). Differentiator: breadth of sources, including
the invoice from LLM providers (OpenAI), which brings it closer to end-to-end AI cost. Limit:
the depth of intra-cluster allocation is lower than that of K8s-native tools.&lt;/p>
&lt;h3 id="finout--virtual-tagging-fast-deployment">Finout — virtual tagging, fast deployment&lt;/h3>
&lt;p>What it measures: multi-cloud cost with virtual tagging: it applies cost labels &lt;strong>without
modifying the real resources&lt;/strong>, which allows spend that was not properly tagged at source to be
allocated. Differentiator: fast deployment and flexible reallocation without touching the infra.
Limit: like the others in its category, it depends on the quality of the billing data it ingests.&lt;/p>
&lt;h3 id="comparison-table">Comparison table&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Tool&lt;/th>
&lt;th>Scope&lt;/th>
&lt;th>GPU&lt;/th>
&lt;th>Licence / model&lt;/th>
&lt;th>Differentiator&lt;/th>
&lt;th>Layer&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>OpenCost&lt;/strong>&lt;/td>
&lt;td>Kubernetes&lt;/td>
&lt;td>Yes (DCGM)&lt;/td>
&lt;td>Apache 2.0 (CNCF), free&lt;/td>
&lt;td>allocation standard&lt;/td>
&lt;td>resource&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Kubecost&lt;/strong>&lt;/td>
&lt;td>K8s + cloud&lt;/td>
&lt;td>Yes (DCGM, 3.0)&lt;/td>
&lt;td>commercial (IBM)&lt;/td>
&lt;td>rightsizing, enterprise&lt;/td>
&lt;td>resource+optim&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>CloudZero&lt;/strong>&lt;/td>
&lt;td>multi-cloud&lt;/td>
&lt;td>indirect&lt;/td>
&lt;td>commercial&lt;/td>
&lt;td>unit economics per product&lt;/td>
&lt;td>business&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Vantage&lt;/strong>&lt;/td>
&lt;td>multi-cloud (20+)&lt;/td>
&lt;td>via K8s/provider&lt;/td>
&lt;td>commercial&lt;/td>
&lt;td>breadth (incl. OpenAI)&lt;/td>
&lt;td>business&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Finout&lt;/strong>&lt;/td>
&lt;td>multi-cloud&lt;/td>
&lt;td>via K8s/provider&lt;/td>
&lt;td>commercial&lt;/td>
&lt;td>virtual tagging&lt;/td>
&lt;td>business&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="pricing-models-of-commercial-tooling">Pricing models of commercial tooling&lt;/h2>
&lt;p>They are worth knowing because the cost of the FinOps tool is also FinOps:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th>How it charges&lt;/th>
&lt;th>Range&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Savings-based&lt;/td>
&lt;td>% of the savings delivered&lt;/td>
&lt;td>&lt;strong>15–35 %&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Fixed-fee&lt;/td>
&lt;td>% of annual cloud spend&lt;/td>
&lt;td>&lt;strong>1–3 %&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>(&lt;a href="https://www.cloudzero.com/blog/finops-tools/">CloudZero · FinOps Tools&lt;/a>). The implication: at
large cloud spend, a 1–3 % &lt;em>fixed-fee&lt;/em> can exceed the &lt;em>savings-based&lt;/em> model; at small spend with a
lot of waste, &lt;em>savings-based&lt;/em> aligns incentives. OpenCost, being free, changes the equation for
whoever has a team to operate it.&lt;/p>
&lt;hr>
&lt;h2 id="focus-the-cost-data-standard">FOCUS: the cost data standard&lt;/h2>
&lt;p>The cross-cutting problem of multi-source FinOps is that each provider bills in its own format.
&lt;strong>FOCUS&lt;/strong> (FinOps Open Cost and Usage Specification) is the open technical specification that
defines requirements so that providers produce uniform billing datasets
(&lt;a href="https://focus.finops.org/focus-specification/">FOCUS · FinOps Foundation&lt;/a>).
The committee ratified FOCUS v1.3 on 4 December 2025.&lt;/p>
&lt;p>What is relevant for this series: at &lt;strong>FinOps X 2026&lt;/strong> the focus has been put on &lt;strong>extending FOCUS
to AI workloads&lt;/strong>, with token economics pushing the specification, and the expansion requests
include AI, datacenter and SaaS/PaaS workloads (&lt;a href="https://siliconangle.com/2026/06/08/focus-specification-ai-cost-accountability-finopsx/">SiliconANGLE&lt;/a>).
In other words, the standard that normalises cloud cost is being stretched to cover AI cost per
token. For an architecture proposal, betting on tools that emit and consume FOCUS is betting on
future interoperability.&lt;/p>
&lt;hr>
&lt;h2 id="from-resource-to-token-cost-per-token-with-a-gateway">From resource to token: cost per token with a gateway&lt;/h2>
&lt;p>Resource allocation (OpenCost) gets as far as &amp;ldquo;this vLLM pod cost X €/hour&amp;rdquo;. To get to &amp;ldquo;this
request from this team cost Y&amp;rdquo; you have to intercept the inference traffic. That is where the
gateway comes in.&lt;/p>
&lt;p>Tools such as LiteLLM sit between the application and the LLM provider/engine, and &lt;strong>intercept
every request to record tokens, latency and cost in real time&lt;/strong>. Layers above (for instance
OpenLM&amp;rsquo;s AI Gateway) generate usage logs compatible with FOCUS (v1.0 to 1.3) and map spend to
team, product, customer or feature, enabling showback or chargeback
(&lt;a href="https://www.openlm.com/enable-ai-finops-with-real-time-token-attribution/">OpenLM · token attribution&lt;/a>).
This links up with &lt;a href="https://blog.lo0.es/en/posts/finops-multi-tenancy-gpu-cluster-who-pays/">FinOps and multi-tenancy of the GPU cluster&lt;/a>,
where the gateway is the piece that splits cost between tenants.&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 230" role="img" aria-label="From GPU-hour to cost per token: OpenCost allocates the pod cost, the LiteLLM gateway intercepts requests and counts tokens, and cost is split per team and product" xmlns="http://www.w3.org/2000/svg">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.3}.dsh{fill:none;stroke:currentColor;stroke-width:1.3;stroke-dasharray:5 3}.tl{font:600 12px sans-serif;fill:currentColor}.ts{font:11px sans-serif;fill:currentColor}.ar{fill:none;stroke:currentColor;stroke-width:1.3;marker-end:url(#fm2)}&lt;/style>
&lt;defs>&lt;marker id="fm2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="currentColor"/>&lt;/marker>&lt;/defs>
&lt;rect class="bx" x="20" y="40" width="160" height="58" rx="6"/>
&lt;text x="32" y="63" class="tl">OpenCost&lt;/text>
&lt;text x="32" y="82" class="ts">vLLM pod cost (€/h)&lt;/text>
&lt;path class="ar" d="M180,69 L225,69"/>
&lt;rect class="bx" x="225" y="40" width="180" height="58" rx="6"/>
&lt;text x="237" y="63" class="tl">Gateway (LiteLLM)&lt;/text>
&lt;text x="237" y="82" class="ts">intercepts · counts tokens&lt;/text>
&lt;path class="ar" d="M405,69 L450,69"/>
&lt;rect class="bx" x="450" y="40" width="150" height="58" rx="6"/>
&lt;text x="462" y="63" class="tl">FOCUS logs&lt;/text>
&lt;text x="462" y="82" class="ts">tokens · latency · cost&lt;/text>
&lt;path class="ar" d="M600,69 L645,69"/>
&lt;rect class="bx" x="645" y="40" width="115" height="58" rx="6"/>
&lt;text x="657" y="63" class="tl">Showback&lt;/text>
&lt;text x="657" y="82" class="ts">per team&lt;/text>
&lt;rect class="dsh" x="20" y="130" width="740" height="72" rx="6"/>
&lt;text x="34" y="152" class="tl">Two halves that have to be joined: allocation per resource (OpenCost) + measurement per token (gateway).&lt;/text>
&lt;text x="34" y="172" class="ts">Without the gateway you know the pod's cost, not the request's. Without OpenCost you know the tokens, not the real hardware cost.&lt;/text>
&lt;text x="34" y="190" class="ts">FOCUS is the common format that lets them be crossed with the rest of the cloud spend.&lt;/text>
&lt;/svg>
&lt;/div>
&lt;hr>
&lt;h2 id="worked-example-chargeback-of-a-multi-tenant-cluster">Worked example: chargeback of a multi-tenant cluster&lt;/h2>
&lt;p>To see the two halves joined, a split over an example node (&lt;strong>4×H100&lt;/strong>, depreciated cost
12 €/hour) shared by three teams via namespaces and MIG:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Team&lt;/th>
&lt;th>GPU-hours allocated (OpenCost)&lt;/th>
&lt;th>Hardware cost (€/h)&lt;/th>
&lt;th>Tokens/day (gateway)&lt;/th>
&lt;th>Cost/1M tok&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>A · chat product&lt;/td>
&lt;td>50 %&lt;/td>
&lt;td>6.00&lt;/td>
&lt;td>8M&lt;/td>
&lt;td>~0.75 €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>B · nightly batch&lt;/td>
&lt;td>30 %&lt;/td>
&lt;td>3.60&lt;/td>
&lt;td>3M&lt;/td>
&lt;td>~1.20 €&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>C · experimentation&lt;/td>
&lt;td>20 %&lt;/td>
&lt;td>2.40&lt;/td>
&lt;td>0.5M&lt;/td>
&lt;td>~4.80 €&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>How it comes out: OpenCost splits the node&amp;rsquo;s 12 €/h according to the GPU-hours each namespace
consumes (half for A → 6 €/h). The gateway supplies the tokens per team. Each one&amp;rsquo;s cost per
million tokens is its hardware cost divided by its output, and it reveals something neither half
would see alone: team &lt;strong>C pays 6× more per token than A&lt;/strong>, not because its model is worse, but
because its GPU is underused (a lot of allocated GPU-hours for few tokens). That 4.80 €/1M is
the chargeback signal that triggers an optimisation conversation: either C raises its utilisation,
or it releases the GPU. Without crossing allocation and tokens, that waste stays hidden in a
cluster average.&lt;/p>
&lt;hr>
&lt;h2 id="the-hidden-cost-utilisation">The hidden cost: utilisation&lt;/h2>
&lt;p>Optimisation lever number one is not changing GPU, it is stopping paying for an idle GPU. As
seen in the opening article, the electricity cost per token at 80 % utilisation is a quarter of
what it is at 20 %, and utilisation spreads all the fixed cost (capex + energy) over more tokens.
Measuring idle is, therefore, the highest-return action of the &lt;em>Optimize&lt;/em> phase:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Signal&lt;/th>
&lt;th>Source&lt;/th>
&lt;th>Typical threshold&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Idle GPU&lt;/td>
&lt;td>&lt;code>DCGM_FI_DEV_GPU_UTIL&lt;/code>&lt;/td>
&lt;td>&amp;lt; 10 % for &amp;gt; 15 min&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Unused GPU memory&lt;/td>
&lt;td>&lt;code>DCGM_FI_DEV_FB_USED&lt;/code>&lt;/td>
&lt;td>reserved but not used&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Pods with no traffic&lt;/td>
&lt;td>gateway metrics&lt;/td>
&lt;td>0 requests, GPU allocated&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Allocation (OpenCost) locates whose the idle GPU is; scheduling and co-residency
(&lt;a href="https://blog.lo0.es/en/posts/sharing-one-gpu-time-slicing-mps-mig/">sharing the GPU: time-slicing, MPS and MIG&lt;/a>)
recover it. FinOps closes the loop: measure → attribute → optimise → govern.&lt;/p>
&lt;hr>
&lt;h2 id="optimize-the-saving-levers-with-data">Optimize: the saving levers (with data)&lt;/h2>
&lt;p>Once cost is allocated (Inform), the &lt;em>Optimize&lt;/em> phase has a bounded repertoire of levers. Ordered
by typical return:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Lever&lt;/th>
&lt;th>Typical saving&lt;/th>
&lt;th>Mechanism&lt;/th>
&lt;th>Cost/risk&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Recover idle&lt;/strong>&lt;/td>
&lt;td>the largest&lt;/td>
&lt;td>switch off/share the idle GPU&lt;/td>
&lt;td>requires measuring utilisation&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Quantisation (FP8/INT4)&lt;/strong>&lt;/td>
&lt;td>raises throughput, lowers VRAM&lt;/td>
&lt;td>more tokens per GPU-hour&lt;/td>
&lt;td>possible quality cost&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Reserved commitment&lt;/strong>&lt;/td>
&lt;td>&lt;strong>20–40 %&lt;/strong> over on-demand&lt;/td>
&lt;td>reserve capacity&lt;/td>
&lt;td>less elasticity&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Spot/preemptible&lt;/strong>&lt;/td>
&lt;td>the deepest discount&lt;/td>
&lt;td>interruptible capacity&lt;/td>
&lt;td>you have to tolerate cuts&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Rightsizing&lt;/strong>&lt;/td>
&lt;td>variable&lt;/td>
&lt;td>match GPU type/count to the SLO&lt;/td>
&lt;td>requires benchmarks&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Autoscaling (HPA/KEDA)&lt;/strong>&lt;/td>
&lt;td>variable&lt;/td>
&lt;td>scale replicas with demand&lt;/td>
&lt;td>tuning metrics&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>On commitment, the 2026 cloud data is conclusive: reserved plans give 20–40 % saving
against on-demand, and spot the deepest discount in exchange for interruptibility; the price of
the H100 fell 64–75 % between Q4 2024 and the start of 2026
(&lt;a href="https://www.spheron.network/blog/gpu-cloud-pricing-comparison-2026/">Spheron · GPU Cloud Pricing&lt;/a>).
Rightsizing and autoscaling connect with cluster scheduling: fitting the workload onto the
right GPU and scaling replicas with demand are, at the same time, performance and cost levers.
Quantisation appears here because it raises throughput, and, by the identity from the opening
article, lowers cost and energy per token at the same time.&lt;/p>
&lt;hr>
&lt;h2 id="gpu-finops-maturity">GPU FinOps maturity&lt;/h2>
&lt;p>A simple model to place where an organisation is, and what it is missing:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Level&lt;/th>
&lt;th>State&lt;/th>
&lt;th>What is missing to move up&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>0 · blind&lt;/td>
&lt;td>aggregate invoice, no attribution&lt;/td>
&lt;td>instrument OpenCost + DCGM&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1 · visibility&lt;/td>
&lt;td>cost per namespace/team&lt;/td>
&lt;td>per-token measurement (gateway)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2 · unit economics&lt;/td>
&lt;td>cost per token and per product&lt;/td>
&lt;td>idle alerts and budgets&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3 · optimisation&lt;/td>
&lt;td>idle recovered, commitment, rightsizing&lt;/td>
&lt;td>automated chargeback&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>4 · governance&lt;/td>
&lt;td>chargeback + budgets + FOCUS&lt;/td>
&lt;td>continuous improvement&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Most organisations with GPUs are at level 0 or 1: they see a big invoice but do not know whose
each euro is. The value jump is in reaching level 2, cost per token and per product, which is
exactly where the tooling in this article stops being optional. The series&amp;rsquo; FinOps track walks that
ladder up to the full TCO model and the comparable cost/token that underpins the architecture
proposal.&lt;/p>
&lt;hr>
&lt;h2 id="the-minimum-stack-to-reach-level-2">The minimum stack to reach level 2&lt;/h2>
&lt;p>Bringing the previous pieces together into a concrete toolchain, all open source, that takes you
from blindness (level 0) to cost per token and per product (level 2):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Piece&lt;/th>
&lt;th>Function&lt;/th>
&lt;th>Alternative&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>NVIDIA GPU Operator + DCGM&lt;/strong>&lt;/td>
&lt;td>exports GPU metrics (usage, memory, power)&lt;/td>
&lt;td>—&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Prometheus&lt;/strong>&lt;/td>
&lt;td>stores the usage and cost series&lt;/td>
&lt;td>VictoriaMetrics&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>OpenCost&lt;/strong>&lt;/td>
&lt;td>allocates cost per resource and dimension&lt;/td>
&lt;td>Kubecost (commercial)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Gateway (LiteLLM)&lt;/strong>&lt;/td>
&lt;td>counts tokens per team/model&lt;/td>
&lt;td>OpenLM AI Gateway&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Grafana&lt;/strong>&lt;/td>
&lt;td>cost, utilisation and idle panels&lt;/td>
&lt;td>—&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>It is, deliberately, infrastructure many clusters already have for observability (DCGM,
Prometheus, Grafana); GPU FinOps reuses that base and adds OpenCost and the gateway to it. It is
not a new product, it is a layer on top of what exists.&lt;/p>
&lt;h3 id="kpis-to-watch">KPIs to watch&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>KPI&lt;/th>
&lt;th>What it indicates&lt;/th>
&lt;th>Typical target&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Average GPU utilisation&lt;/td>
&lt;td>waste&lt;/td>
&lt;td>&amp;gt;70–80 % sustained&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cost per 1M tokens (per model)&lt;/td>
&lt;td>economic efficiency&lt;/td>
&lt;td>compare against cloud rental&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>% of GPU-hours idle&lt;/td>
&lt;td>money thrown away&lt;/td>
&lt;td>minimise (&amp;lt;10–15 %)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cost per team/product&lt;/td>
&lt;td>attribution&lt;/td>
&lt;td>fair split, no surprises&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Deviation against budget&lt;/td>
&lt;td>governance&lt;/td>
&lt;td>alert before it is exceeded&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>These five KPIs are what turn GPU FinOps from a pretty dashboard into a decision tool: if you watch
them, you know at all times what each thing costs and where the waste is.&lt;/p>
&lt;hr>
&lt;h2 id="state-of-the-art-2026">State of the art 2026&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>Consolidation in Kubernetes&lt;/strong>: OpenCost is the CNCF allocation standard; Kubecost
(IBM) the reference commercial product, already with GPU via DCGM and rightsizing through
Turbonomic.&lt;/li>
&lt;li>&lt;strong>From resource to token&lt;/strong>: the strong platforms of 2026 track cost at token and GPU level,
combining resource allocation (OpenCost) with gateway measurement (LiteLLM); it is the only route
to a comparable cost/token for on-prem against cloud.&lt;/li>
&lt;li>&lt;strong>FOCUS v1.3&lt;/strong> (Dec 2025) as the interoperability layer, extending to AI at
FinOps X 2026: the cloud cost standard absorbs token economics.&lt;/li>
&lt;li>&lt;strong>Mature GPU FinOps&lt;/strong>: instrumentation leans on DCGM (the same base as observability), and idle
detection by a utilisation threshold is common practice.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="cost-per-token-the-bridge-to-the-business-decision">Cost per token: the bridge to the business decision&lt;/h2>
&lt;p>All the previous tooling exists to produce a number the business understands: cost per token
(or per request). It is the one that lets you answer the three questions that underpin an
architecture proposal:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Build or buy?&lt;/strong> On-prem cost/token (depreciated capex + opex, at real utilisation) against the
price of an external API or of cloud rental. Below the volume threshold (~2M tokens/day) buying
usually wins; above it, building.&lt;/li>
&lt;li>&lt;strong>How do you price a product?&lt;/strong> If a feature consumes N tokens per use and each million costs C,
the marginal cost per use is N·C/10⁶, the basis of any margin.&lt;/li>
&lt;li>&lt;strong>Where is the waste?&lt;/strong> Cost/token per team (the chargeback example) points at whoever underuses
the GPU before the aggregate invoice hides it.&lt;/li>
&lt;/ol>
&lt;p>The trap: comparing cost/token across scenarios without fixing the assumptions (utilisation,
precision, ownership model). An on-prem cost/token calculated at 80 % utilisation is not comparable
with one at 20 %, nor an FP16 with an FP8. That is why allocation (Inform) is not an end in itself:
it is the raw material of a cost model with explicit assumptions, which is what the synthesis
article turns into the &amp;ldquo;build vs buy&amp;rdquo; argument with defensible numbers. GPU FinOps is not
accounting; it is the quantitative basis of the architecture decision.&lt;/p>
&lt;hr>
&lt;h2 id="limits-and-traps-data-driven">Limits and traps (data-driven)&lt;/h2>
&lt;ol>
&lt;li>&lt;strong>Allocation ≠ per-token measurement.&lt;/strong> OpenCost splits the resource cost; without a gateway you
do not reach the request. They are two halves that have to be joined explicitly.&lt;/li>
&lt;li>&lt;strong>Badly set base prices = badly set allocation.&lt;/strong> On-prem, OpenCost splits the cost &lt;strong>you
declare&lt;/strong> for the node; if the depreciated capex/opex is wrong, the whole split is wrong. Input
data quality rules.&lt;/li>
&lt;li>&lt;strong>Invisible idle.&lt;/strong> Without DCGM exported to Prometheus and without utilisation alerts, waste
number one does not appear on any dashboard.&lt;/li>
&lt;li>&lt;strong>Cost of the tool.&lt;/strong> A 1–3 % &lt;em>fixed-fee&lt;/em> on large spend is real money; compare the tooling cost
with the saving it delivers (it is FinOps on top of FinOps).&lt;/li>
&lt;li>&lt;strong>Proprietary formats.&lt;/strong> Tools that do not emit/consume FOCUS tie you to their data model; in
2026 the robust bet is FOCUS interoperability.&lt;/li>
&lt;/ol>
&lt;p>The next article in the series (A2) goes into OpenCost in depth; this one fixes the map. With
allocation settled, the rest of the FinOps track builds the TCO model and the cost/token the
proposal needs.&lt;/p>
&lt;h2 id="sources">Sources&lt;/h2>
&lt;ul>
&lt;li>OpenCost · GitHub (CNCF, Apache 2.0) — &lt;a href="https://github.com/opencost/opencost">https://github.com/opencost/opencost&lt;/a>&lt;/li>
&lt;li>OpenCost · documentación on-prem (modelo de precio por nodo) — &lt;a href="https://opencost.io/docs/configuration/on-prem/">https://opencost.io/docs/configuration/on-prem/&lt;/a>&lt;/li>
&lt;li>OpenCost · exporter de Prometheus — &lt;a href="https://opencost.io/docs/integrations/opencost-exporter/">https://opencost.io/docs/integrations/opencost-exporter/&lt;/a>&lt;/li>
&lt;li>CloudZero · Kubecost vs OpenCost (2026) — &lt;a href="https://www.cloudzero.com/blog/kubecost-vs-opencost/">https://www.cloudzero.com/blog/kubecost-vs-opencost/&lt;/a>&lt;/li>
&lt;li>CloudZero · FinOps Tools: Definitive Guide (2026) — &lt;a href="https://www.cloudzero.com/blog/finops-tools/">https://www.cloudzero.com/blog/finops-tools/&lt;/a>&lt;/li>
&lt;li>FOCUS · especificación (FinOps Foundation) — &lt;a href="https://focus.finops.org/focus-specification/">https://focus.finops.org/focus-specification/&lt;/a>&lt;/li>
&lt;li>SiliconANGLE · FOCUS y la economía de tokens de IA (FinOps X 2026) — &lt;a href="https://siliconangle.com/2026/06/08/focus-specification-ai-cost-accountability-finopsx/">https://siliconangle.com/2026/06/08/focus-specification-ai-cost-accountability-finopsx/&lt;/a>&lt;/li>
&lt;li>OpenLM · atribución de tokens en tiempo real (LiteLLM + FOCUS) — &lt;a href="https://www.openlm.com/enable-ai-finops-with-real-time-token-attribution/">https://www.openlm.com/enable-ai-finops-with-real-time-token-attribution/&lt;/a>&lt;/li>
&lt;li>Finout · Best AI Cost Observability Tools (2026) — &lt;a href="https://www.finout.io/blog/best-ai-cost-observability-tools-in-2026">https://www.finout.io/blog/best-ai-cost-observability-tools-in-2026&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>FinOps and multi-tenancy on the GPU cluster: who pays for what</title><link>https://blog.lo0.es/en/posts/finops-multi-tenancy-gpu-cluster-who-pays/</link><pubDate>Thu, 11 Jun 2026 12:20:00 +0000</pubDate><guid>https://blog.lo0.es/en/posts/finops-multi-tenancy-gpu-cluster-who-pays/</guid><description>&lt;blockquote>
&lt;p>Third instalment in an operational series on how to squeeze a generic on-premise LLM inference cluster of &lt;strong>4×H100 SXM 80 GB&lt;/strong>. The sibling pieces are &lt;a href="https://blog.lo0.es/en/posts/multimodal-on-premise-serving-vlm-vllm/">Multimodal VLM on-premise with vLLM&lt;/a>, serving vision-language models on the same cluster, and &lt;a href="https://blog.lo0.es/en/posts/speeding-up-model-cold-start-minutes-to-seconds/">Speeding up cold start with Tensorizer&lt;/a>, cutting the model load time from disk to HBM. This post closes the economic question the other two leave open: once the cluster serves several models for several teams, &lt;strong>who pays for what?&lt;/strong>&lt;/p>
&lt;/blockquote>
&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>An on-premise GPU cluster does not have the property that makes FinOps easy in the cloud: no monthly invoice arrives telling you what each compute hour cost. The cost has to be manufactured from capex and energy. The right unit is the token, and it is derived in two steps. First the €/GPU-hour: capex depreciation spread over the hours of useful life, plus energy (power × PUE × price_kWh). For an H100 SXM with an assumed capex of 30,000 € and 4 years of life at 90 % availability, that gives ≈ 1.07 €/GPU-hour (0.95 of depreciation + 0.12 of energy at 0.12 €/kWh and PUE 1.4). Second, the €/1M tokens: divide that €/GPU-hour by the sustained throughput. At 2,000 useful tok/s, ≈ 0.148 €/1M tokens; but that number only holds if the GPU is at 100 % useful utilisation. At 20 % utilisation the same token costs 4× more (0.74 €/1M), because capex and energy keep being paid even if the GPU is idle. That is the central thesis: &lt;strong>the FinOps goal is not to negotiate the price of the token down, there is no provider to negotiate with, but to raise useful utilisation&lt;/strong>, which is the only term that moves cost by an order of magnitude. Attribution (chargeback / showback) is made real with LiteLLM virtual keys: per-team and per-user budget, RPM/TPM rate limit, automatic spend tracking and &lt;code>tags&lt;/code> to assign spend to cost centres. Isolation between tenants is achieved with MIG (hard GPU partitioning), namespaces, &lt;code>ResourceQuota&lt;/code> and &lt;code>PriorityClass&lt;/code>. The FinOps Foundation rule worth carving in stone: &lt;strong>showback always, chargeback only if the organisation&amp;rsquo;s accounting policy supports it&lt;/strong>; neither of the two is &amp;ldquo;more mature&amp;rdquo;. And the governance metric of the shared cluster is not the €/token but &lt;code>DCGM_FI_DEV_GPU_UTIL&lt;/code>.&lt;/p>
&lt;h2 id="the-problem-the-cloud-hides-from-you-and-on-premise-forces-you-to-solve">The problem the cloud hides from you and on-premise forces you to solve&lt;/h2>
&lt;p>In the cloud, the cost of a GPU is an observable fact: AWS, GCP or Azure charge you per GPU-hour and the invoice arrives itemised. FinOps in that world consists of &lt;strong>reading&lt;/strong> an invoice that already exists and attributing it. On-premise there is no such invoice. The GPU was bought once, eighteen months ago, on a purchase order nobody remembers; the electricity bill arrives for the whole building with no breakdown of what fraction was the cluster&amp;rsquo;s; and the datacenter PUE is a number the facilities team knows but the AI team never asked about.&lt;/p>
&lt;p>The first job of on-premise FinOps, then, is not to attribute a cost but to manufacture one. You have to build, from explicit assumptions, the equivalent of the &amp;ldquo;price per GPU-hour&amp;rdquo; that in the cloud is given to you. And once manufactured, you have to understand that this number is not a property of the hardware, like VRAM or TDP, but a function of how the hardware is used. An idle H100 costs exactly the same as an H100 at 100 %: the capex is already paid and idle energy is not zero. The only thing that changes is &lt;strong>how many useful tokens&lt;/strong> that fixed cost produced. That is where the whole thesis of this post comes from.&lt;/p>
&lt;h2 id="the-analogy-the-coworking-space-with-metered-services">The analogy: the coworking space with metered services&lt;/h2>
&lt;p>A coworking space manages a finite physical space and rents it to teams. The cost model has exactly the structure of the GPU cluster, and the analogy holds down to the last detail.&lt;/p>
&lt;p>&lt;strong>The desk-hour is the spread capex.&lt;/strong> The manager paid the rent on the premises, the furniture and the refurbishment, a one-off outlay or a fixed monthly cost, and spreads it over the available desk-hours in the month. Each desk has a base cost that &lt;strong>does not depend on whether anybody uses it&lt;/strong>: the walls, the table and the chair cost the same empty as occupied. This is the &lt;strong>depreciation of the GPU&lt;/strong>: capex divided by the hours of useful life.&lt;/p>
&lt;p>&lt;strong>The metered kWh is the energy.&lt;/strong> On top of the desk-hour, the coworking space measures the actual electricity consumption, the plugged-in laptop, the screen, the air conditioning in that room, and bills it separately. This is the &lt;strong>energy of the GPU&lt;/strong>: power × hours × price_kWh, multiplied by the datacenter&amp;rsquo;s &lt;strong>PUE&lt;/strong>, which is the coworking space also charging you for the fraction of the building&amp;rsquo;s air conditioning that cools your room.&lt;/p>
&lt;p>&lt;strong>The empty desk is paid for by whoever booked it.&lt;/strong> Here is the heart of the matter. If a team books ten desks for the whole month and only uses two, &lt;strong>it pays for all ten anyway&lt;/strong>. The empty desk still takes up space the manager cannot resell, still depreciates furniture, still counts as committed capacity. In the cluster: a GPU assigned to a tenant that uses it at 20 % costs the same as if it used it at 100 %, and the idle 80 % is &lt;strong>wasted capex&lt;/strong> that somebody is paying for.&lt;/p>
&lt;p>&lt;strong>The manager optimises occupancy, not tariff.&lt;/strong> A competent coworking manager does not obsess over lowering the price of the desk-hour, it is a sunk cost, the premises are already paid for. They obsess over the &lt;strong>occupancy rate&lt;/strong>: every empty desk is margin lost forever, because an unsold desk-hour cannot be stored. The FinOps equivalent: &lt;strong>the goal is not to lower the €/token, it is to raise utilisation&lt;/strong>, because the €/token falls only as a consequence of utilisation rising. There is no provider to squeeze; the only margin lies in not leaving GPUs idle.&lt;/p>
&lt;p>The analogy has an honest limit worth naming: in a coworking space, two people cannot physically share the same chair. On a GPU you &lt;strong>can&lt;/strong>, with time-slicing, MPS or MIG (see &lt;a href="https://blog.lo0.es/en/posts/sharing-one-gpu-time-slicing-mps-mig/">Sharing a GPU&lt;/a>), and that is precisely the technical lever that lets you raise occupancy beyond what the physical coworking space would allow. But the economics of fixed cost + metered cost is identical.&lt;/p>
&lt;h2 id="the-token-as-the-unit-of-cost">The token as the unit of cost&lt;/h2>
&lt;p>Why the token and not the GPU-hour, or the request, or the model? Because the token is the only unit comparable across heterogeneous workloads. A GPU-hour of Llama 8B and one of Llama 70B produce radically different amounts of &amp;ldquo;useful work&amp;rdquo;; a RAG request with a 50-token answer and one generating a 3,000-token report are not comparable. The token, specifically the pair (input_tokens, output_tokens), normalises all of that. It is the unit LiteLLM accounts for natively, the one that appears in the OTel attributes &lt;code>gen_ai.usage.input_tokens&lt;/code> and &lt;code>gen_ai.usage.output_tokens&lt;/code> (see &lt;a href="https://blog.lo0.es/en/posts/llm-tracing-opentelemetry-genai/">LLM tracing with OpenTelemetry GenAI&lt;/a>), and the one commercial providers already use to charge. Adopting the token as the internal currency makes on-premise chargeback directly comparable with the cloud alternative, which is exactly the comparison management wants to see.&lt;/p>
&lt;p>The two derived metrics that matter:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Tokens/€&lt;/strong> — how many useful tokens each euro of total cluster cost buys. It rises when utilisation rises. It is the metric of economic efficiency.&lt;/li>
&lt;li>&lt;strong>Tokens/W&lt;/strong> — how many tokens each watt consumed produces. It is the metric of energy efficiency and, in a datacenter with limited power (the usual case on-premise), it is often the real &lt;strong>hard constraint&lt;/strong>: you cannot add more GPUs because there are no more kW in the rack, so every watt has to perform.&lt;/li>
&lt;/ul>
&lt;h2 id="cost-model-of-a-gpu-from-capex-to-token">Cost model of a GPU: from capex to €/token&lt;/h2>
&lt;p>The hourly cost of a GPU has two addends. Let us put all the assumptions in writing, because, as in &lt;a href="https://blog.lo0.es/en/posts/capacity-planning-on-premise-llm-inference/">capacity planning&lt;/a>, a cost calculation without written assumptions is a disposable calculation.&lt;/p>
&lt;h3 id="depreciation-spread-capex">Depreciation (spread capex)&lt;/h3>
$$\text{depreciation cost/h} = \frac{\text{capex}_{\text{GPU}}}{\text{hours of useful life}}$$
&lt;p>where the hours of useful life are the depreciation years × 8,760 h/year × the effective availability (the GPU is not available 100 % of the time: there is maintenance, restarts, update windows). Explicit, &lt;strong>generic&lt;/strong> assumptions (they are not figures from any provider or any real purchase):&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Capex per GPU&lt;/strong>: 30,000 € — a reasonable assumption that includes not just the GPU but its &lt;strong>pro-rata share&lt;/strong> of the server, power supply, cooling, NVLink/InfiniBand network and rack. A &amp;ldquo;bare&amp;rdquo; H100 SXM costs less, but honest FinOps spreads the cost of the whole node across its GPUs.&lt;/li>
&lt;li>&lt;strong>Accounting useful life&lt;/strong>: 4 years. That is aggressive-realistic for a datacenter GPU; some depreciate over 3, others over 5.&lt;/li>
&lt;li>&lt;strong>Effective availability&lt;/strong>: 90 %.&lt;/li>
&lt;/ul>
$$\text{hours of life} = 4 \times 8{,}760 \times 0.90 = 31{,}536 \text{ h}$$
$$\text{depreciation cost/h} = \frac{30{,}000}{31{,}536} \approx 0.95 \text{ €/GPU-hour}$$
&lt;h3 id="energy-power--pue--price_kwh">Energy (power × PUE × price_kWh)&lt;/h3>
$$\text{energy cost/h} = P_{\text{GPU}} \cdot \text{PUE} \cdot \text{price kWh}$$
&lt;p>Assumptions:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>GPU power&lt;/strong>: the TDP of the H100 SXM is 700 W according to the NVIDIA datasheet (&lt;a href="https://resources.nvidia.com/en-us-gpu-resources/h100-datasheet-24306">H100 datasheet&lt;/a>). Under sustained inference load it hovers around that value; we use 0.7 kW as the average power under load. (At idle it drops a lot, 60–100 W, but the FinOps of reserved capacity reasons about power under load, which is what limits the rack.)&lt;/li>
&lt;li>&lt;strong>PUE&lt;/strong> (Power Usage Effectiveness): how many watts enter the datacenter for every watt that reaches the chip. A modest, well-managed on-premise datacenter sits at 1.4; a bad one at 2.0; the hyperscalers boast of 1.1. We use 1.4.&lt;/li>
&lt;li>&lt;strong>Price per kWh&lt;/strong>: 0.12 €/kWh — a generic assumption for an industrial tariff; it varies enormously by country and contract.&lt;/li>
&lt;/ul>
$$\text{energy cost/h} = 0.7 \cdot 1.4 \cdot 0.12 \approx 0.1176 \approx 0.12 \text{ €/GPU-hour}$$
&lt;h3 id="the-total-gpu-hour">The total €/GPU-hour&lt;/h3>
$$\boxed{\text{€/GPU-hour} = 0.95 + 0.12 \approx 1.07 \text{ €/GPU-hour}}$$
&lt;p>Two important readings of this number. First: depreciation dominates (89 % of the cost); energy is 11 %. This inverts many people&amp;rsquo;s intuition, who believe &amp;ldquo;the expensive part is the electricity&amp;rdquo;. With these assumptions, the expensive part is having bought the GPU, and that is why leaving it idle hurts so much. Second: the number is per-GPU; for the 4×H100 node it is ≈ 4.28 €/hour, and for the complete generic cluster of 4 nodes (16 GPUs), ≈ 17.1 €/hour ≈ 150,000 €/year of fixed cost that is paid whether it is used or not.&lt;/p>
&lt;h3 id="from-gpu-hour-to-token">From €/GPU-hour to €/token&lt;/h3>
&lt;p>Now we divide the hourly cost by the tokens the GPU produces in an hour. Here sustained throughput comes in. Let us suppose, an order-of-magnitude figure, checkable with &lt;code>vllm bench serve&lt;/code>, that a Llama 70B replica on 4×H100 (TP=4) sustains &lt;strong>2,000 useful tokens/s&lt;/strong> aggregated under good concurrency.&lt;/p>
$$\text{tokens/hour} = 2{,}000 \times 3{,}600 = 7.2 \times 10^6 \text{ tok/h}$$
&lt;p>The cost of that replica (4 GPUs) is $4 \times 1.07 = 4.28$ €/hour. Therefore:&lt;/p>
$$\text{€/1M tokens} = \frac{4.28}{7.2} \approx 0.59 \text{ €/1M tokens}$$
&lt;p>Per individual GPU, if we take a smaller replica (e.g. Llama 8B FP8 on 1 GPU at ~2,000 tok/s):&lt;/p>
$$\text{€/1M tokens} = \frac{1.07 \text{ €/h}}{7.2 \times 10^6 \text{ tok/h}} \times 10^6 \approx 0.149 \text{ €/1M tokens}$$
&lt;p>These numbers are &lt;strong>the theoretical floor at 100 % utilisation&lt;/strong>. Nobody operates at 100 %. And that is the whole story.&lt;/p>
&lt;h2 id="utilisation-is-the-lever-with-numbers">Utilisation is the lever (with numbers)&lt;/h2>
&lt;p>The €/token in the previous section assumes the GPU produces 7.2 M tokens every hour, hour after hour. In practice it produces that only during peak hours. The rest of the time it is partly idle: at night, at weekends, between traffic peaks. The &lt;strong>useful utilisation&lt;/strong> $u$ is the fraction of the theoretical capacity that really turns into billable tokens.&lt;/p>
&lt;p>The hourly cost is &lt;strong>fixed&lt;/strong> (1.07 €/GPU-hour is paid come rain or shine), but the tokens produced scale with $u$:&lt;/p>
$$\text{€/1M tokens}(u) = \frac{\text{€/GPU-hour}}{\text{max tok/hour} \cdot u} = \frac{1.07}{7.2 \times 10^6 \cdot u} \times 10^6 = \frac{0.149}{u}$$
&lt;p>The requested contrast, worked out:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Useful utilisation $u$&lt;/th>
&lt;th>Real tokens/h&lt;/th>
&lt;th>€/1M tokens&lt;/th>
&lt;th>Multiplier vs 80 %&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>100 %&lt;/td>
&lt;td>7.2 M&lt;/td>
&lt;td>0.149 €&lt;/td>
&lt;td>0.80×&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>80 %&lt;/td>
&lt;td>5.76 M&lt;/td>
&lt;td>0.186 €&lt;/td>
&lt;td>1.00× (baseline)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>50 %&lt;/td>
&lt;td>3.6 M&lt;/td>
&lt;td>0.298 €&lt;/td>
&lt;td>1.60×&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>20 %&lt;/td>
&lt;td>1.44 M&lt;/td>
&lt;td>0.744 €&lt;/td>
&lt;td>4.00×&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>10 %&lt;/td>
&lt;td>0.72 M&lt;/td>
&lt;td>1.488 €&lt;/td>
&lt;td>8.00×&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Direct reading: going from 20 % to 80 % utilisation divides the cost per token by four ($0.744 \to 0.186$). No tariff negotiation, no quantization, no hardware change gives you that factor of 4 so cheaply. Quantizing from BF16 to FP8 can double throughput, and therefore halves the €/token, but it degrades quality and demands evals (see &lt;a href="https://blog.lo0.es/en/posts/quantization-llm-inference-fp8-int4-gguf/">Quantization for inference&lt;/a>); raising utilisation from 20 to 80 % does not touch the quality of a single token.&lt;/p>
&lt;p>This reformulates GPU cluster FinOps in a single sentence: &lt;strong>the €/token is not something you negotiate, it is something you earn by filling the GPUs&lt;/strong>. And raising utilisation is an engineering problem that already has its pieces:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Autoscaling&lt;/strong> that switches replicas off when traffic drops, so as not to pay for idle GPU (see &lt;a href="https://blog.lo0.es/en/posts/llm-inference-autoscaling-kubernetes-hpa-keda/">LLM autoscaling on Kubernetes with KEDA&lt;/a>). Switching off a 4-GPU replica for eight hours overnight saves ≈ 34 € a day of real cost; multiplied by replicas and by days, that is the difference between a profitable cluster and one that bleeds.&lt;/li>
&lt;li>&lt;strong>Sharing a GPU&lt;/strong> between small workloads with MIG or time-slicing, so that two tenants that individually would use 30 % together fill a GPU to 60 % (see &lt;a href="https://blog.lo0.es/en/posts/sharing-one-gpu-time-slicing-mps-mig/">Sharing a GPU&lt;/a>).&lt;/li>
&lt;li>&lt;strong>Overnight batch&lt;/strong> that fills the off-peak hours with non-urgent work (re-embeddings, evals, light fine-tuning) instead of leaving the GPUs switched off or ticking over.&lt;/li>
&lt;li>&lt;strong>Reducing cold start&lt;/strong> so that scaling to zero and starting again is cheap and therefore viable as a utilisation strategy (see the sibling piece &lt;a href="https://blog.lo0.es/en/posts/speeding-up-model-cold-start-minutes-to-seconds/">Speeding up cold start with Tensorizer&lt;/a>).&lt;/li>
&lt;/ul>
&lt;h2 id="the-diagram-the-token-cascade-by-utilisation">The diagram: the €/token cascade by utilisation&lt;/h2>
&lt;p>The cost per token is not a number, it is a cascade that multiplies as utilisation falls:&lt;/p>
&lt;div class="diagram" style="max-width:760px;margin:1.5rem auto;">
&lt;svg viewBox="0 0 760 360" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="cost per token cascade by utilisation">
&lt;text x="380" y="24" text-anchor="middle" font-size="15" font-weight="700" fill="currentColor">The €/token is manufactured, then inflated by low utilisation&lt;/text>
&lt;text x="380" y="48" text-anchor="middle" font-size="11" font-style="italic" fill="currentColor">fixed cost (capex + energy) → ÷ tokens produced → €/token, which grows as utilisation falls&lt;/text>
&lt;rect x="40" y="70" width="160" height="50" rx="6" fill="#8b5cf6" stroke="currentColor" stroke-width="1.2"/>
&lt;text x="120" y="92" text-anchor="middle" font-size="11" font-weight="700" fill="#ffffff">Depreciation&lt;/text>
&lt;text x="120" y="108" text-anchor="middle" font-size="11" fill="#ffffff">0.95 €/GPU-h&lt;/text>
&lt;rect x="40" y="130" width="160" height="50" rx="6" fill="#f59e0b" stroke="currentColor" stroke-width="1.2"/>
&lt;text x="120" y="152" text-anchor="middle" font-size="11" font-weight="700" fill="#ffffff">Energy × PUE&lt;/text>
&lt;text x="120" y="168" text-anchor="middle" font-size="11" fill="#ffffff">0.12 €/GPU-h&lt;/text>
&lt;path d="M200,95 L230,95 L230,150 L200,150" fill="none" stroke="currentColor" stroke-width="1.2"/>
&lt;line x1="230" y1="125" x2="265" y2="125" stroke="currentColor" stroke-width="1.4" marker-end="url(#fa)"/>
&lt;rect x="270" y="100" width="150" height="50" rx="6" fill="none" stroke="currentColor" stroke-width="1.6"/>
&lt;text x="345" y="122" text-anchor="middle" font-size="11" font-weight="700" fill="currentColor">€/GPU-hour&lt;/text>
&lt;text x="345" y="138" text-anchor="middle" font-size="11" fill="currentColor">1.07 €/h (fixed)&lt;/text>
&lt;text x="345" y="172" text-anchor="middle" font-size="10" font-style="italic" fill="currentColor">÷ tokens produced/hour&lt;/text>
&lt;line x1="420" y1="125" x2="470" y2="125" stroke="currentColor" stroke-width="1.4" marker-end="url(#fa)"/>
&lt;rect x="475" y="100" width="245" height="50" rx="6" fill="none" stroke="currentColor" stroke-width="1.6"/>
&lt;text x="597" y="122" text-anchor="middle" font-size="11" font-weight="700" fill="currentColor">€/1M tokens = 0.149 / u&lt;/text>
&lt;text x="597" y="138" text-anchor="middle" font-size="10" fill="currentColor">depends on useful utilisation u&lt;/text>
&lt;text x="380" y="200" text-anchor="middle" font-size="12" font-weight="700" fill="currentColor">Same GPU, different cost per token by u:&lt;/text>
&lt;rect x="90" y="220" width="130" height="110" rx="6" fill="#22c55e" stroke="currentColor" stroke-width="1.2"/>
&lt;text x="155" y="245" text-anchor="middle" font-size="12" font-weight="700" fill="#ffffff">u = 80%&lt;/text>
&lt;text x="155" y="280" text-anchor="middle" font-size="18" font-weight="700" fill="#ffffff">0.186 €&lt;/text>
&lt;text x="155" y="300" text-anchor="middle" font-size="10" fill="#ffffff">/1M tokens&lt;/text>
&lt;text x="155" y="318" text-anchor="middle" font-size="10" fill="#ffffff">GPU well filled&lt;/text>
&lt;rect x="315" y="220" width="130" height="110" rx="6" fill="#f59e0b" stroke="currentColor" stroke-width="1.2"/>
&lt;text x="380" y="245" text-anchor="middle" font-size="12" font-weight="700" fill="#ffffff">u = 50%&lt;/text>
&lt;text x="380" y="280" text-anchor="middle" font-size="18" font-weight="700" fill="#ffffff">0.298 €&lt;/text>
&lt;text x="380" y="300" text-anchor="middle" font-size="10" fill="#ffffff">/1M tokens&lt;/text>
&lt;text x="380" y="318" text-anchor="middle" font-size="10" fill="#ffffff">1.6× the baseline&lt;/text>
&lt;rect x="540" y="220" width="130" height="110" rx="6" fill="#ef4444" stroke="currentColor" stroke-width="1.2"/>
&lt;text x="605" y="245" text-anchor="middle" font-size="12" font-weight="700" fill="#ffffff">u = 20%&lt;/text>
&lt;text x="605" y="280" text-anchor="middle" font-size="18" font-weight="700" fill="#ffffff">0.744 €&lt;/text>
&lt;text x="605" y="300" text-anchor="middle" font-size="10" fill="#ffffff">/1M tokens&lt;/text>
&lt;text x="605" y="318" text-anchor="middle" font-size="10" fill="#ffffff">4× the baseline&lt;/text>
&lt;defs>
&lt;marker id="fa" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="currentColor"/>&lt;/marker>
&lt;/defs>
&lt;/svg>
&lt;/div>
&lt;h2 id="attributing-the-cost-litellm-virtual-keys">Attributing the cost: LiteLLM virtual keys&lt;/h2>
&lt;p>With the €/token manufactured, it remains to attribute it to whoever consumed it. The piece that already sits in front of the engines, the &lt;a href="https://blog.lo0.es/en/posts/llm-inference-router-l7-switchboard/">L7 inference router&lt;/a>, is also the natural place to count. &lt;strong>LiteLLM Proxy&lt;/strong> makes chargeback real with four mechanisms, all documented:&lt;/p>
&lt;p>&lt;strong>Virtual keys with a budget.&lt;/strong> Each team or user receives a &lt;em>virtual key&lt;/em> with a &lt;code>max_budget&lt;/code> and a &lt;code>budget_duration&lt;/code>. You can define &lt;strong>several independent budget windows&lt;/strong> (for example one of 24 h and another of 30 d) that reset on their own cycle (&lt;a href="https://docs.litellm.ai/docs/proxy/virtual_keys">Virtual Keys&lt;/a>). When a team exhausts its monthly token budget, the proxy rejects with a budget exceeded error instead of carrying on spending GPU-hours nobody will be able to charge anywhere.&lt;/p>
&lt;p>&lt;strong>RPM/TPM rate limit.&lt;/strong> Each key carries requests per minute (RPM) and tokens per minute (TPM) limits (&lt;a href="https://docs.litellm.ai/docs/proxy/users">Budgets, Rate Limits&lt;/a>). This is not just anti-abuse protection: it is the tool that stops a noisy tenant monopolising the cluster&amp;rsquo;s utilisation at everybody else&amp;rsquo;s expense. The rate limit is the capacity quota; the budget is the spend quota. They are orthogonal and you use both.&lt;/p>
&lt;p>&lt;strong>Automatic spend tracking.&lt;/strong> LiteLLM accounts for the spend of all known models by key, user and team, recording the API key, the user, the &lt;code>team_id&lt;/code>, the request&amp;rsquo;s &lt;code>tags&lt;/code>, the end user, the model group and the token counts (&lt;a href="https://docs.litellm.ai/docs/proxy/cost_tracking">Spend Tracking&lt;/a>). For the cluster&amp;rsquo;s self-hosted models, the cost per token is configured &lt;strong>with the €/token we manufactured above&lt;/strong>, and that is the hook between the cost model and the real accounting.&lt;/p>
&lt;p>&lt;strong>Tags for cost centres.&lt;/strong> The &lt;code>tags&lt;/code> let you track spend and set budgets per label, categorising costs by project, department or cost centre (&lt;a href="https://docs.litellm.ai/docs/proxy/tag_budgets">Setting Tag Budgets&lt;/a>). A tag is attached when the key is created and &lt;strong>every request made with that key inherits the tag automatically&lt;/strong>; the proxy applies the label&amp;rsquo;s budget. That way chargeback maps cleanly onto the organisation&amp;rsquo;s accounting hierarchy without every team having to remember to tag by hand.&lt;/p>
&lt;p>A declarative catalogue fragment, which fits into the &lt;code>litellm-config&lt;/code> of the &lt;a href="https://blog.lo0.es/en/posts/llm-inference-router-l7-switchboard/">router post&lt;/a>, materialising the manufactured cost and two tenants:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">litellm_settings&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># €/token manufactured in this post for the self-hosted model.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># input/output in €/token (not per 1M); 0.186 €/1M = 1.86e-7 €/token at u=80%.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">model_cost_map&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">llama-70b-onprem&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">input_cost_per_token&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0.000000186&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">output_cost_per_token&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0.000000186&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># Virtual keys per team (via the /key/generate API or config):&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># equipo-datos: max_budget=500 €/month, tpm_limit=200000, tags=[&amp;#34;cc-datos&amp;#34;]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># equipo-soporte: max_budget=150 €/month, tpm_limit=60000, tags=[&amp;#34;cc-soporte&amp;#34;]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># The proxy counts tokens, multiplies by model_cost_map, deducts from the budget,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># and attributes the spend to the tag → showback per cost centre, without touching the engine.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The complete attribution flow, from the request to the per-team report:&lt;/p>
&lt;div class="diagram" style="max-width:800px;margin:1.5rem auto;">
&lt;svg viewBox="0 0 800 280" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="cost attribution flow with virtual key and tags">
&lt;text x="400" y="22" text-anchor="middle" font-size="15" font-weight="700" fill="currentColor">Attribution flow: from the request to per-team showback&lt;/text>
&lt;rect x="20" y="60" width="140" height="70" rx="6" fill="none" stroke="currentColor" stroke-width="1.4"/>
&lt;text x="90" y="88" text-anchor="middle" font-size="11" font-weight="700" fill="currentColor">Team request&lt;/text>
&lt;text x="90" y="106" text-anchor="middle" font-size="10" fill="currentColor">Authorization:&lt;/text>
&lt;text x="90" y="120" text-anchor="middle" font-size="10" fill="#3b82f6">Bearer sk-equipo-datos&lt;/text>
&lt;line x1="160" y1="95" x2="195" y2="95" stroke="currentColor" stroke-width="1.4" marker-end="url(#fb)"/>
&lt;rect x="200" y="50" width="170" height="90" rx="6" fill="none" stroke="#8b5cf6" stroke-width="1.8"/>
&lt;text x="285" y="74" text-anchor="middle" font-size="11" font-weight="700" fill="currentColor">LiteLLM Proxy&lt;/text>
&lt;text x="285" y="92" text-anchor="middle" font-size="9.5" fill="currentColor">1· validate key + budget&lt;/text>
&lt;text x="285" y="106" text-anchor="middle" font-size="9.5" fill="currentColor">2· apply RPM/TPM&lt;/text>
&lt;text x="285" y="120" text-anchor="middle" font-size="9.5" fill="currentColor">3· inherit tag cc-datos&lt;/text>
&lt;text x="285" y="134" text-anchor="middle" font-size="9.5" fill="currentColor">4· count tokens&lt;/text>
&lt;line x1="370" y1="95" x2="405" y2="95" stroke="currentColor" stroke-width="1.4" marker-end="url(#fb)"/>
&lt;rect x="410" y="60" width="150" height="70" rx="6" fill="none" stroke="currentColor" stroke-width="1.4"/>
&lt;text x="485" y="88" text-anchor="middle" font-size="11" font-weight="700" fill="currentColor">vLLM engine&lt;/text>
&lt;text x="485" y="106" text-anchor="middle" font-size="10" fill="currentColor">4×H100 · TP=4&lt;/text>
&lt;text x="485" y="120" text-anchor="middle" font-size="10" fill="currentColor">produces N tokens&lt;/text>
&lt;line x1="485" y1="130" x2="485" y2="165" stroke="currentColor" stroke-width="1.2" stroke-dasharray="4 2" marker-end="url(#fb)"/>
&lt;text x="555" y="152" text-anchor="middle" font-size="9.5" font-style="italic" fill="currentColor">usage.tokens back&lt;/text>
&lt;rect x="200" y="170" width="170" height="60" rx="6" fill="none" stroke="#22c55e" stroke-width="1.6"/>
&lt;text x="285" y="194" text-anchor="middle" font-size="11" font-weight="700" fill="currentColor">Spend tracking&lt;/text>
&lt;text x="285" y="210" text-anchor="middle" font-size="9.5" fill="currentColor">tokens × €/token (model_cost_map)&lt;/text>
&lt;text x="285" y="223" text-anchor="middle" font-size="9.5" fill="currentColor">→ deduct budget, store in DB&lt;/text>
&lt;line x1="285" y1="140" x2="285" y2="170" stroke="currentColor" stroke-width="1.4" marker-end="url(#fb)"/>
&lt;line x1="370" y1="200" x2="560" y2="200" stroke="currentColor" stroke-width="1.4" marker-end="url(#fb)"/>
&lt;rect x="565" y="170" width="215" height="60" rx="6" fill="none" stroke="#f59e0b" stroke-width="1.6"/>
&lt;text x="672" y="194" text-anchor="middle" font-size="11" font-weight="700" fill="currentColor">Report by tag / team&lt;/text>
&lt;text x="672" y="210" text-anchor="middle" font-size="9.5" fill="currentColor">cc-datos: 312 € · cc-soporte: 88 €&lt;/text>
&lt;text x="672" y="223" text-anchor="middle" font-size="9.5" fill="currentColor">showback (or chargeback to P&amp;amp;L)&lt;/text>
&lt;defs>
&lt;marker id="fb" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="currentColor"/>&lt;/marker>
&lt;/defs>
&lt;/svg>
&lt;/div>
&lt;h2 id="showback-vs-chargeback-the-distinction-the-finops-foundation-insists-on">Showback vs chargeback: the distinction the FinOps Foundation insists on&lt;/h2>
&lt;p>The two words are constantly confused and the difference is one of &lt;strong>accounting formality&lt;/strong>, not technology:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Showback&lt;/strong> — giving each team visibility of what it consumed and its cost, without really billing it to their budget. The report arrives, the team sees it, but the money does not move between cost centres.&lt;/li>
&lt;li>&lt;strong>Chargeback&lt;/strong> — really transferring the cost to the team&amp;rsquo;s or product&amp;rsquo;s budget, putting it in their P&amp;amp;L. The spend stops being a central IT cost and becomes an allocated cost.&lt;/li>
&lt;/ul>
&lt;p>The FinOps Foundation is explicit on two points worth carving in stone (&lt;a href="https://www.finops.org/framework/capabilities/invoicing-chargeback/">Invoicing &amp;amp; Chargeback&lt;/a>, &lt;a href="https://www.cloudzero.com/blog/chargeback-vs-showback/">Chargeback vs Showback&lt;/a>). First: &lt;strong>showback is a requirement of any FinOps practice; chargeback depends on the organisation&amp;rsquo;s accounting policy&lt;/strong>. Not all organisations can or want to move money between departments for GPU consumption; showback you can always do. Second, and counterintuitive: neither of the two is &amp;ldquo;more mature&amp;rdquo; than the other. The narrative that chargeback is the &amp;ldquo;grown-up&amp;rdquo; version of showback is false according to the framework itself. The practical recommendation: start with showback, giving visibility, then build cost allocation aligned to the organisational hierarchy, and only then, if the accounting policy supports it, switch chargeback on.&lt;/p>
&lt;p>For the on-premise GPU cluster this means: the LiteLLM machinery (keys, tags, spend tracking) always produces the showback. Turning it into chargeback is a decision for finance, not for the platform team. The platform team guarantees that the numbers are correct and reproducible; who really pays is a governance decision.&lt;/p>
&lt;h2 id="isolation-making-the-cost-genuinely-attributable">Isolation: making the cost genuinely attributable&lt;/h2>
&lt;p>Chargeback is only honest if consumption is &lt;strong>isolable&lt;/strong>. If two tenants share a GPU without partitioning and one saturates the HBM, the other suffers degradation it did not cause but which contaminates its cost attribution. Isolation has two planes.&lt;/p>
&lt;p>&lt;strong>Hard GPU isolation: MIG.&lt;/strong> Multi-Instance GPU physically partitions an H100 into up to 7 instances with dedicated memory and SMs; in a 7-way partition each instance has ~10 GB of HBM3 and its own SMs. MIG gives the strongest isolation: tenant A in its MIG instance cannot touch tenant B&amp;rsquo;s performance, and attribution is trivial because each instance is accountable separately (see &lt;a href="https://blog.lo0.es/en/posts/sharing-one-gpu-time-slicing-mps-mig/">Sharing a GPU&lt;/a> for the detail of MIG vs time-slicing vs MPS). The cost: MIG instances are fixed, they are not resized hot, and if they sit empty they are fragmented and wasted capex, the coworking desk subdivided into booths nobody rents.&lt;/p>
&lt;p>&lt;strong>Logical Kubernetes isolation.&lt;/strong> On top of the cluster:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Namespaces per tenant&lt;/strong> — the boundary for RBAC, NetworkPolicy and quotas.&lt;/li>
&lt;li>&lt;strong>&lt;code>ResourceQuota&lt;/code>&lt;/strong> — limits how many &lt;code>nvidia.com/gpu&lt;/code> (or &lt;code>nvidia.com/mig-1g.10gb&lt;/code>) a namespace can request. It is the GPU capacity quota at the scheduler level: the tenant cannot claim more GPUs than its quota grants, which &lt;strong>bounds its maximum cost&lt;/strong> by construction.&lt;/li>
&lt;li>&lt;strong>&lt;code>PriorityClass&lt;/code>&lt;/strong> — defines which workloads can evict others under pressure. The overnight re-embeddings batch runs with low priority and gives way to interactive inference; that way it fills the utilisation valleys without risking the paying tenant&amp;rsquo;s SLO. It is the piece that stops &amp;ldquo;raising utilisation with batch&amp;rdquo; from cannibalising quality of service.&lt;/li>
&lt;/ul>
&lt;p>The combination that works in the generic cluster: &lt;strong>MIG to partition the GPUs between tenants that need hard isolation and clean attribution&lt;/strong>, &lt;code>ResourceQuota&lt;/code> per namespace to bound each tenant&amp;rsquo;s cost, and &lt;code>PriorityClass&lt;/code> so that filler work raises utilisation without touching the priority tenants.&lt;/p>
&lt;h2 id="applied-to-the-generic-4h100-cluster">Applied to the generic 4×H100 cluster&lt;/h2>
&lt;p>Let us bring it all down to the series&amp;rsquo; cluster: 4 nodes × 4×H100 SXM 80 GB, 16 GPUs, ≈ 17.1 €/hour of fixed cost (≈ 150,000 €/year). Three teams share it: Data (production RAG, office-hours traffic), Support (ticket assistant, daytime peaks) and Platform (evals and re-embeddings batch, no urgency).&lt;/p>
&lt;p>&lt;strong>Splitting the cost.&lt;/strong> The cluster&amp;rsquo;s fixed cost (150,000 €/year) is split through LiteLLM&amp;rsquo;s spend tracking in proportion to the tokens consumed by each team, valued at the manufactured €/token. If in one month Data consumed 1,700 M tokens, Support 480 M and Platform 320 M, at 0.186 €/1M the showback is ≈ 316 € / 89 € / 60 €. The residue, the fixed cost of the GPUs nobody used because average utilisation was, say, 45 %, is the cost of idleness, and the governance decision is whether it is split between the tenants (penalises everybody equally) or charged to Platform as &amp;ldquo;cost of unsold capacity&amp;rdquo; (gives Platform an incentive to raise utilisation). The FinOps Foundation would say: make it visible first (showback of the cost of idleness), decide the split afterwards.&lt;/p>
&lt;p>&lt;strong>Quotas per tenant.&lt;/strong> Each team has its namespace with &lt;code>ResourceQuota&lt;/code>: Data can claim up to 8 GPUs (2 replicas at TP=4), Support up to 4, Platform up to 4 but with a low &lt;code>PriorityClass&lt;/code>, giving up its GPUs when Data or Support need them at peak. In LiteLLM, each team has its virtual key with a monthly &lt;code>max_budget&lt;/code> and a &lt;code>tpm_limit&lt;/code> that reflects its capacity quota.&lt;/p>
&lt;p>&lt;strong>MIG to isolate and attribute.&lt;/strong> For the small models (embeddings, reranker, a utility Llama 8B), an H100 split 7-way with MIG gives seven separately attributable instances: three for Data&amp;rsquo;s embeddings, two for Support&amp;rsquo;s reranker, two free for filler. Each instance is an independent accounting &amp;ldquo;desk&amp;rdquo;; the per-tenant showback comes straight from the scheduler.&lt;/p>
&lt;p>&lt;strong>The governance metric is utilisation, not the €/token.&lt;/strong> Here we close the circle. The KPI the cluster owner should look at daily is not &amp;ldquo;what the token costs&amp;rdquo;, that number only falls as a consequence, but &lt;code>DCGM_FI_DEV_GPU_UTIL&lt;/code>, the DCGM exporter metric that indicates what fraction of the time the GPU is not idle (&lt;a href="https://docs.nvidia.com/datacenter/dcgm/latest/gpu-telemetry/dcgm-exporter.html">DCGM exporter&lt;/a>). And with a critical nuance that already appeared in &lt;a href="https://blog.lo0.es/en/posts/capacity-planning-on-premise-llm-inference/">capacity planning&lt;/a>: &lt;code>DCGM_FI_DEV_GPU_UTIL&lt;/code> measures &amp;ldquo;not idle&amp;rdquo;, it does not measure useful work. A GPU can show 100 % GPU-util with the HBM saturated and low SM occupancy, producing few tokens. That is why serious FinOps crosses three signals: &lt;code>DCGM_FI_DEV_GPU_UTIL&lt;/code> (is the GPU busy?), &lt;code>DCGM_FI_PROF_SM_OCCUPANCY&lt;/code> and &lt;code>DCGM_FI_PROF_GR_ENGINE_ACTIVE&lt;/code> (is it doing real work?), and &lt;code>vllm:gpu_cache_usage_perc&lt;/code> (is the HBM well used?). The economic governance metric is useful tokens per GPU-hour, and it is watched from the &lt;a href="https://blog.lo0.es/en/posts/gpu-observability-llm-inference-twelve-dcgm-vllm-metrics/">GPU observability with DCGM&lt;/a> dashboard.&lt;/p>
&lt;p>An end-to-end assembly detail, LibreChat on top of LiteLLM with RAG, which closes the flow from tenant to cost, is covered in the (forthcoming) post on the end-to-end sovereign assistant; here it is enough to know that the metering point is always the proxy, never the engine.&lt;/p>
&lt;h2 id="four-traps-of-on-premise-gpu-finops">Four traps of on-premise GPU FinOps&lt;/h2>
&lt;p>&lt;strong>Trap 1 — comparing the on-premise €/token with the commercial API&amp;rsquo;s list price.&lt;/strong> The commercial API has margins, SLA and scale the on-premise cluster does not, but on-premise has the cost of idleness the API hides from you (they fill their GPUs with thousands of customers). The honest comparison is on-premise at its real utilisation vs the API at its real negotiated price, not the fantasy of on-premise at 100 %.&lt;/p>
&lt;p>&lt;strong>Trap 2 — forgetting the cost of idleness in the showback.&lt;/strong> If you only charge teams for the tokens they consumed, the cost of the idle GPUs disappears from the report and nobody sees it. That cost exists and somebody is paying it. Making it visible is the first step to reducing it.&lt;/p>
&lt;p>&lt;strong>Trap 3 — confusing high GPU-util with efficiency.&lt;/strong> A GPU at 100 % &lt;code>DCGM_FI_DEV_GPU_UTIL&lt;/code> with the HBM saturated and low SM occupancy spends capex without producing proportional tokens. The goal is not &amp;ldquo;GPU at 100 %&amp;rdquo;, it is &amp;ldquo;useful tokens per euro&amp;rdquo;. Always cross util with SM occupancy and real throughput.&lt;/p>
&lt;p>&lt;strong>Trap 4 — chargeback before showback.&lt;/strong> Switching chargeback on, moving money, before the teams trust that the numbers are correct generates disputes that burn the whole FinOps programme. First visibility, then trust in the data, then, if the accounting policy allows it, the money.&lt;/p>
&lt;h2 id="see-also">See also&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/multimodal-on-premise-serving-vlm-vllm/">Multimodal VLM on-premise with vLLM&lt;/a> — sibling piece: serving vision-language models on the same shared cluster whose cost we attribute here.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/speeding-up-model-cold-start-minutes-to-seconds/">Speeding up cold start with Tensorizer&lt;/a> — sibling piece: cutting the model load time makes scaling to zero viable, which is the most direct utilisation lever.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/capacity-planning-on-premise-llm-inference/">Capacity planning for on-premise LLM inference&lt;/a> — the sustained throughput the €/token comes from is computed there; FinOps monetises it.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/sharing-one-gpu-time-slicing-mps-mig/">Sharing a GPU: time-slicing, MPS and MIG&lt;/a> — the partitioning that lets you isolate tenants and raise utilisation by combining small workloads.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/llm-inference-router-l7-switchboard/">The LLM inference router: the L7 switchboard&lt;/a> — where LiteLLM lives and where every token is counted and attributed.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/llm-inference-autoscaling-kubernetes-hpa-keda/">LLM autoscaling on Kubernetes with KEDA&lt;/a> — switching off idle replicas is the number one lever to raise useful utilisation and lower the €/token.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/gpu-observability-llm-inference-twelve-dcgm-vllm-metrics/">GPU observability for LLM inference with DCGM&lt;/a> — where &lt;code>DCGM_FI_DEV_GPU_UTIL&lt;/code> comes from, the governance metric of the shared cluster.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/litellm-langfuse-operational-pair/">LiteLLM and Langfuse: the operational pair&lt;/a> — where the prices computed here get registered in LiteLLM, and why a model with no price silently produces zero spend and budgets that never run out.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/en/posts/litellm-virtual-keys-budgets-limits/">Virtual keys, budgets and limits in LiteLLM&lt;/a> — how the budgets computed here are enforced in the proxy, and the condition in the code by which a team key overrides its owner&amp;rsquo;s personal cap.&lt;/li>
&lt;/ul>
&lt;h2 id="references">References&lt;/h2>
&lt;ul>
&lt;li>NVIDIA — &lt;em>H100 Tensor Core GPU Datasheet&lt;/em> (TDP 700 W, HBM3 3,35 TB/s): &lt;code>resources.nvidia.com/en-us-gpu-resources/h100-datasheet-24306&lt;/code>.&lt;/li>
&lt;li>LiteLLM — &lt;em>Virtual Keys&lt;/em> (&lt;code>docs.litellm.ai/docs/proxy/virtual_keys&lt;/code>), &lt;em>Budgets &amp;amp; Rate Limits&lt;/em> (&lt;code>/docs/proxy/users&lt;/code>), &lt;em>Spend Tracking&lt;/em> (&lt;code>/docs/proxy/cost_tracking&lt;/code>), &lt;em>Setting Tag Budgets&lt;/em> (&lt;code>/docs/proxy/tag_budgets&lt;/code>), &lt;em>Team Budgets&lt;/em> (&lt;code>/docs/proxy/team_budgets&lt;/code>).&lt;/li>
&lt;li>FinOps Foundation — &lt;em>Invoicing &amp;amp; Chargeback Capability&lt;/em> (&lt;code>finops.org/framework/capabilities/invoicing-chargeback/&lt;/code>) y &lt;em>Data Analysis and Showback&lt;/em> (&lt;code>finops.org/framework/previous-capabilities/analysis-showback/&lt;/code>).&lt;/li>
&lt;li>CloudZero — &lt;em>Chargeback vs. Showback: Cloud Cost Allocation Models Explained&lt;/em> (&lt;code>cloudzero.com/blog/chargeback-vs-showback/&lt;/code>).&lt;/li>
&lt;li>NVIDIA — &lt;em>DCGM Exporter&lt;/em> (&lt;code>docs.nvidia.com/datacenter/dcgm/latest/gpu-telemetry/dcgm-exporter.html&lt;/code>): &lt;code>DCGM_FI_DEV_GPU_UTIL&lt;/code>, &lt;code>DCGM_FI_PROF_SM_OCCUPANCY&lt;/code>, &lt;code>DCGM_FI_PROF_GR_ENGINE_ACTIVE&lt;/code>.&lt;/li>
&lt;/ul></description></item></channel></rss>