{"course":{"slug":"running-models-yourself","title":"Running Models Yourself","url":"https://addaly.com/learn/running-models-yourself","level":"intermediate","track":"deep"},"lesson":{"slug":"the-break-even-model","title":"Building the break-even model","url":"https://addaly.com/learn/running-models-yourself/the-break-even-model","position":72,"of":82,"minutes":10,"answer":"Assemble the inputs into two cost curves and report where they cross as a monthly token volume translated into daily exchanges, because the crossing point is usually far higher than people expect.","keyIdea":"Assemble the inputs into two cost curves and report where they cross as a monthly token volume translated into daily exchanges, because the crossing point is usually far higher than people expect, duty cycle and unpriced hours dominate every other input, and the non-cost reasons for local belong outside the arithmetic."},"body":{"format":"markdown","text":"## Stop arguing and write the function\n\nEvery preceding lesson contributed one input. This one assembles them into a single number that can be defended, challenged and corrected: the monthly token volume above which running it yourself is cheaper.\n\nThe structure is two cost curves. The hosted curve passes through the origin and rises with volume. The local curve starts high (you pay the fixed costs at volume zero) and rises barely at all, because the marginal cost of one more token on hardware you already have is a fraction of a watt-hour. They cross once. That crossing point is the whole answer, and it is a volume rather than a verdict.\n\n```python\ndef local_monthly(card_price, resale, months_held, watts, tariff,\n                  duty_cycle, setup_hours, hourly_rate, months_amortise_time=12):\n    capital = (card_price - resale) / months_held\n    hours_running = 730 * duty_cycle          # 730 hours in an average month\n    power = (watts / 1000) * tariff * hours_running\n    idle = (90 / 1000) * tariff * (730 - hours_running)   # idle draw, still billed\n    people = (setup_hours * hourly_rate) / months_amortise_time + 2 * hourly_rate\n    return capital + power + idle + people\n\ndef tokens_per_month(peak_tps, duty_cycle):\n    return peak_tps * 730 * 3600 * duty_cycle / 1e6       # in millions\n\ndef break_even(local_cost, api_price_per_million):\n    return local_cost / api_price_per_million             # millions of tokens\n```\n\nThe `2 * hourly_rate` term is two hours a month of maintenance. Change it if your experience differs, but do not delete it.\n\n## A worked case, with every assumption visible\n\nA used card at 700, expected resale 350, held 36 months. 500W under load, 90W idle, tariff 0.20. Duty cycle 8%, which is a team of several people plus some overnight batch work. Peak 1,200 tokens per second. Thirty setup hours and two maintenance hours a month at a rate of 25.\n\n> Figure: Where the monthly cost of a small deployment goes. Bars (currency units per month): Capital: card less resale, over 36 months 9.7; Power while generating, 58 hours 5.8; Idle power, the other 672 hours 12.1; Setup and maintenance hours 112.5. A used card at 700 with 350 expected back over 36 months, 500 W under load and 90 W idle at a tariff of 0.20, an eight per cent duty cycle, thirty setup hours amortised over a year and two hours a month after that, at a rate of 25. Four fifths of the bill is the person, which is why a disagreement about the electricity price is not worth the meeting.\n\n- Capital: (700 − 350) / 36 = **9.7 per month**\n- Power under load: 0.5 × 0.20 × 58 hours = **5.8**\n- Idle power: 0.09 × 0.20 × 672 hours = **12.1**\n- People: (30 × 25) / 12 + 2 × 25 = 62.5 + 50 = **112.5**\n- **Total: about 140 per month**, of which 80% is the person.\n\nTokens produced: 1200 × 730 × 3600 × 0.08 ÷ 1e6 ≈ **252 million a month**, giving about **0.56 per million tokens**.\n\nAgainst a hosted model at, say, 0.50 per million blended, break-even is 140 / 0.50 = **280 million tokens a month**. The deployment produces 252 million, so on these assumptions it is marginally behind; and it becomes clearly ahead the moment you either raise the duty cycle or stop counting the person's hours.\n\nThat result is the honest one for a great many small deployments, and it is why the conclusion of this module is not \"local is cheaper\".\n\n## Which input to argue about\n\nVary each input by a factor of two and see what happens to the answer. In almost every configuration the order is the same:\n\n1. **Duty cycle**: moves the result nearly proportionally. Dominant.\n2. **Whether people's hours are counted**: often changes the sign of the conclusion by itself.\n3. **Amortisation window**: meaningful, and entirely a choice.\n4. **Card price and resale**: meaningful but bounded.\n5. **Electricity tariff**: small when busy, and mostly showing up as idle draw.\n\nSo a disagreement about the electricity price is not worth the meeting, and a disagreement about duty cycle is the only conversation that matters. Ask for the measurement.\n\n## The reality check on the break-even volume\n\nTranslate the crossing point into something human before presenting it. 280 million tokens a month is roughly 9.3 million a day. At around 700 tokens per exchange (a question, some context and an answer) that is about 13,000 exchanges a day, every day.\n\nWrite that sentence down and ask whether it describes your organisation. For a team of ten people using a chat interface, it does not, and no arrangement of the hardware will make it. For a pipeline classifying every incoming support message, enriching a product catalogue, or generating embeddings over an archive, it is easily exceeded, and those are precisely the tasks a small local model does well.\n\n## What the model does not capture\n\nBe explicit about this when you present it, because someone will otherwise use the number as though it settled everything. The model prices tokens. It does not price data that cannot leave the building, a regulatory requirement, working without connectivity, a fixed and predictable cost where a metered one is hard to approve, or the value of the skill acquired. Those are real reasons that justify local at any volume, and they should be argued on their own terms rather than smuggled into the arithmetic.\n\n> Present the break-even as a volume and a list of things it does not count. A single number presented as an answer will be believed, and then blamed."},"check":{"question":"A break-even model puts the crossing point at 280 million tokens a month. A manager objects that the electricity tariff used was 20% too low. What is the appropriate response?","options":[{"text":"Recompute with the corrected tariff, since electricity is a recurring cost and the model's accuracy depends on it.","correct":false,"tempting":"Polite and technically fine, but it spends the discussion on the input with the smallest influence on the result."},{"text":"Electricity moves the result very little; the conversation worth having is about measured duty cycle and staff hours.","correct":true},{"text":"Note that tariff errors cancel out, because both the local and hosted curves are affected by energy prices.","correct":false,"tempting":"The hosted price is a contracted rate that does not move with your tariff, so nothing cancels here."},{"text":"Recompute with the corrected tariff and present a range, since a 20% error in any input invalidates a single-point estimate.","correct":false,"tempting":"Treats all inputs as equally influential; a range is good practice but should be built around the inputs that actually move the answer."}]},"prev":{"slug":"the-hours-nobody-invoices","title":"The hours nobody invoices","url":"https://addaly.com/learn/running-models-yourself/the-hours-nobody-invoices"},"next":{"slug":"when-the-api-simply-wins","title":"When the API simply wins, and when it cannot","url":"https://addaly.com/learn/running-models-yourself/when-the-api-simply-wins"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Building the break-even model', in Running Models Yourself. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/running-models-yourself/the-break-even-model (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Building the break-even model","courseTitle":"Running Models Yourself","module":{"index":8,"slug":"what-it-actually-costs","title":"What it actually costs"},"answer":null,"keyIdea":"Assemble the inputs into two cost curves and report where they cross as a monthly token volume translated into daily exchanges, because the crossing point is usually far higher than people expect, duty cycle and unpriced hours dominate every other input, and the non-cost reasons for local belong outside the arithmetic.","hero":{"kind":"figure","figure":{"t":"figure","label":"Figure","figure":{"type":"bars","unit":"currency units per month","title":"Where the monthly cost of a small deployment goes","series":[{"label":"Capital: card less resale, over 36 months","value":9.7},{"label":"Power while generating, 58 hours","value":5.8},{"label":"Idle power, the other 672 hours","value":12.1},{"label":"Setup and maintenance hours","value":112.5,"highlight":true}],"caption":"A used card at 700 with 350 expected back over 36 months, 500 W under load and 90 W idle at a tariff of 0.20, an eight per cent duty cycle, thirty setup hours amortised over a year and two hours a month after that, at a rate of 25. Four fifths of the bill is the person, which is why a disagreement about the electricity price is not worth the meeting."},"alt":"Figure: Where the monthly cost of a small deployment goes. Bars (currency units per month): Capital: card less resale, over 36 months 9.7; Power while generating, 58 hours 5.8; Idle power, the other 672 hours 12.1; Setup and maintenance hours 112.5. A used card at 700 with 350 expected back over 36 months, 500 W under load and 90 W idle at a tariff of 0.20, an eight per cent duty cycle, thirty setup hours amortised over a year and two hours a month after that, at a rate of 25. Four fifths of the bill is the person, which is why a disagreement about the electricity price is not worth the meeting."}},"blocks":[{"t":"h2","id":"stop-arguing-and-write-the-function","runs":[{"text":"Stop arguing and write the function"}]},{"t":"p","runs":[{"text":"Every preceding lesson contributed one input. This one assembles them into a single number that can be defended, challenged and corrected: the monthly token volume above which running it yourself is cheaper."}]},{"t":"p","runs":[{"text":"The structure is two cost curves. The hosted curve passes through the origin and rises with volume. The local curve starts high (you pay the fixed costs at volume zero) and rises barely at all, because the marginal cost of one more token on hardware you already have is a fraction of a watt-hour. They cross once. That crossing point is the whole answer, and it is a volume rather than a verdict."}]},{"t":"code","lang":"python","label":"Python","text":"def local_monthly(card_price, resale, months_held, watts, tariff,\n                  duty_cycle, setup_hours, hourly_rate, months_amortise_time=12):\n    capital = (card_price - resale) / months_held\n    hours_running = 730 * duty_cycle          # 730 hours in an average month\n    power = (watts / 1000) * tariff * hours_running\n    idle = (90 / 1000) * tariff * (730 - hours_running)   # idle draw, still billed\n    people = (setup_hours * hourly_rate) / months_amortise_time + 2 * hourly_rate\n    return capital + power + idle + people\n\ndef tokens_per_month(peak_tps, duty_cycle):\n    return peak_tps * 730 * 3600 * duty_cycle / 1e6       # in millions\n\ndef break_even(local_cost, api_price_per_million):\n    return local_cost / api_price_per_million             # millions of tokens"},{"t":"p","runs":[{"text":"The "},{"text":"2 * hourly_rate","code":true},{"text":" term is two hours a month of maintenance. Change it if your experience differs, but do not delete it."}]},{"t":"h2","id":"a-worked-case-with-every-assumption-visible","runs":[{"text":"A worked case, with every assumption visible"}]},{"t":"p","runs":[{"text":"A used card at 700, expected resale 350, held 36 months. 500W under load, 90W idle, tariff 0.20. Duty cycle 8%, which is a team of several people plus some overnight batch work. Peak 1,200 tokens per second. Thirty setup hours and two maintenance hours a month at a rate of 25."}]},{"t":"ul","items":[[{"text":"Capital: (700 − 350) / 36 = "},{"text":"9.7 per month","b":true}],[{"text":"Power under load: 0.5 × 0.20 × 58 hours = "},{"text":"5.8","b":true}],[{"text":"Idle power: 0.09 × 0.20 × 672 hours = "},{"text":"12.1","b":true}],[{"text":"People: (30 × 25) / 12 + 2 × 25 = 62.5 + 50 = "},{"text":"112.5","b":true}],[{"text":"Total: about 140 per month","b":true},{"text":", of which 80% is the person."}]]},{"t":"p","runs":[{"text":"Tokens produced: 1200 × 730 × 3600 × 0.08 ÷ 1e6 ≈ "},{"text":"252 million a month","b":true},{"text":", giving about "},{"text":"0.56 per million tokens","b":true},{"text":"."}]},{"t":"p","runs":[{"text":"Against a hosted model at, say, 0.50 per million blended, break-even is 140 / 0.50 = "},{"text":"280 million tokens a month","b":true},{"text":". The deployment produces 252 million, so on these assumptions it is marginally behind; and it becomes clearly ahead the moment you either raise the duty cycle or stop counting the person’s hours."}]},{"t":"p","runs":[{"text":"That result is the honest one for a great many small deployments, and it is why the conclusion of this module is not “local is cheaper”."}]},{"t":"h2","id":"which-input-to-argue-about","runs":[{"text":"Which input to argue about"}]},{"t":"p","runs":[{"text":"Vary each input by a factor of two and see what happens to the answer. In almost every configuration the order is the same:"}]},{"t":"ol","start":1,"items":[[{"text":"Duty cycle","b":true},{"text":": moves the result nearly proportionally. Dominant."}],[{"text":"Whether people’s hours are counted","b":true},{"text":": often changes the sign of the conclusion by itself."}],[{"text":"Amortisation window","b":true},{"text":": meaningful, and entirely a choice."}],[{"text":"Card price and resale","b":true},{"text":": meaningful but bounded."}],[{"text":"Electricity tariff","b":true},{"text":": small when busy, and mostly showing up as idle draw."}]]},{"t":"p","runs":[{"text":"So a disagreement about the electricity price is not worth the meeting, and a disagreement about duty cycle is the only conversation that matters. Ask for the measurement."}]},{"t":"h2","id":"the-reality-check-on-the-break-even-volume","runs":[{"text":"The reality check on the break-even volume"}]},{"t":"p","runs":[{"text":"Translate the crossing point into something human before presenting it. 280 million tokens a month is roughly 9.3 million a day. At around 700 tokens per exchange (a question, some context and an answer) that is about 13,000 exchanges a day, every day."}]},{"t":"p","runs":[{"text":"Write that sentence down and ask whether it describes your organisation. For a team of ten people using a chat interface, it does not, and no arrangement of the hardware will make it. For a pipeline classifying every incoming support message, enriching a product catalogue, or generating embeddings over an archive, it is easily exceeded, and those are precisely the tasks a small local model does well."}]},{"t":"h2","id":"what-the-model-does-not-capture","runs":[{"text":"What the model does not capture"}]},{"t":"p","runs":[{"text":"Be explicit about this when you present it, because someone will otherwise use the number as though it settled everything. The model prices tokens. It does not price data that cannot leave the building, a regulatory requirement, working without connectivity, a fixed and predictable cost where a metered one is hard to approve, or the value of the skill acquired. Those are real reasons that justify local at any volume, and they should be argued on their own terms rather than smuggled into the arithmetic."}]},{"t":"quote","runs":[{"text":"Present the break-even as a volume and a list of things it does not count. A single number presented as an answer will be believed, and then blamed."}]}],"check":{"question":[{"text":"A break-even model puts the crossing point at 280 million tokens a month. A manager objects that the electricity tariff used was 20% too low. What is the appropriate response?"}],"options":[{"text":[{"text":"Recompute with the corrected tariff, since electricity is a recurring cost and the model’s accuracy depends on it."}],"correct":false,"tempting":[{"text":"Polite and technically fine, but it spends the discussion on the input with the smallest influence on the result."}]},{"text":[{"text":"Electricity moves the result very little; the conversation worth having is about measured duty cycle and staff hours."}],"correct":true,"tempting":null},{"text":[{"text":"Note that tariff errors cancel out, because both the local and hosted curves are affected by energy prices."}],"correct":false,"tempting":[{"text":"The hosted price is a contracted rate that does not move with your tariff, so nothing cancels here."}]},{"text":[{"text":"Recompute with the corrected tariff and present a range, since a 20% error in any input invalidates a single-point estimate."}],"correct":false,"tempting":[{"text":"Treats all inputs as equally influential; a range is good practice but should be built around the inputs that actually move the answer."}]}],"order":[3,2,0,1],"why":null},"prev":{"slug":"the-hours-nobody-invoices","title":"The hours nobody invoices","minutes":8},"next":{"slug":"when-the-api-simply-wins","title":"When the API simply wins, and when it cannot","minutes":8}}}