<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Sam Hartley</title>
    <description>The latest articles on Forem by Sam Hartley (@samhartley_dev).</description>
    <link>https://forem.com/samhartley_dev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3811539%2Fdd554e30-699d-42a3-a82a-77673790a186.png</url>
      <title>Forem: Sam Hartley</title>
      <link>https://forem.com/samhartley_dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/samhartley_dev"/>
    <language>en</language>
    <item>
      <title>I Expanded My GPU Rental Fleet to 6 Cards — Here's What Happened to My Earnings</title>
      <dc:creator>Sam Hartley</dc:creator>
      <pubDate>Sat, 09 May 2026 08:05:49 +0000</pubDate>
      <link>https://forem.com/samhartley_dev/i-expanded-my-gpu-rental-fleet-to-6-cards-heres-what-happened-to-my-earnings-38oj</link>
      <guid>https://forem.com/samhartley_dev/i-expanded-my-gpu-rental-fleet-to-6-cards-heres-what-happened-to-my-earnings-38oj</guid>
      <description>&lt;h1&gt;
  
  
  I Expanded My GPU Rental Fleet to 6 Cards — Here's What Happened to My Earnings
&lt;/h1&gt;

&lt;p&gt;A few weeks ago I wrote about renting out my single RTX 3060 on Vast.ai for passive income. The experiment worked better than I expected, so I did what any reasonable person would do: I went and dug out the five other GPUs sitting in my storage room.&lt;/p&gt;

&lt;p&gt;This is the honest follow-up. What actually happened when I went from 1 GPU to 6.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Backstory
&lt;/h2&gt;

&lt;p&gt;I had a bunch of GPUs from an older setup — two RTX 3070s, one RTX 3080, and two more RTX 3060s. They were collecting dust. The PC they came from got upgraded, the cards went into cardboard boxes, the boxes went under a shelf.&lt;/p&gt;

&lt;p&gt;Total VRAM across all six: around 62GB. Combined retail value when new: probably $3,000+. Current value sitting in boxes: $0/month.&lt;/p&gt;

&lt;p&gt;The math wasn't complicated.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Expansion Actually Took
&lt;/h2&gt;

&lt;p&gt;Here's what I underestimated: it's not just "plug cards in, profit."&lt;/p&gt;

&lt;h3&gt;
  
  
  The hardware side
&lt;/h3&gt;

&lt;p&gt;You can't just stack 6 GPUs into a regular PC case. I had to think about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PCIe slots and bandwidth.&lt;/strong&gt; A standard ATX board has maybe 2-3 real x16 slots. For 6 cards, you're looking at risers, which means a mining-style open frame or a server chassis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Power.&lt;/strong&gt; Each card pulls 150-250W under load. Six cards = potentially 1,200-1,500W just in GPU power. Plus CPU, drives, RAM. My existing 850W PSU was not going to cut it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cooling.&lt;/strong&gt; Cards in a tight case thermal-throttle each other. Open frame was the answer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I ended up using an open-air mining frame I found used for cheap, two PSUs daisy-chained (a sketchy-but-common approach in the mining world), and PCIe risers.&lt;/p&gt;

&lt;p&gt;Setup time: about a full weekend.&lt;/p&gt;

&lt;h3&gt;
  
  
  The software side
&lt;/h3&gt;

&lt;p&gt;Getting all six cards recognized wasn't plug-and-play either. I run Windows on the main PC (easier driver support for NVIDIA), and Vast.ai has a Windows daemon that mostly works — except when it doesn't.&lt;/p&gt;

&lt;p&gt;A few issues I hit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two risers were flaky and caused cards to drop off&lt;/li&gt;
&lt;li&gt;One 3070 had a driver conflict until I did a clean DDU reinstall&lt;/li&gt;
&lt;li&gt;Vast.ai's host dashboard showed 5 GPUs after setup; took me an hour to figure out the sixth wasn't being detected&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total debugging time before everything was stable: another weekend.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Earnings Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setup&lt;/th&gt;
&lt;th&gt;Cards&lt;/th&gt;
&lt;th&gt;VRAM&lt;/th&gt;
&lt;th&gt;Weekly Earnings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Before (1 card)&lt;/td&gt;
&lt;td&gt;RTX 3060&lt;/td&gt;
&lt;td&gt;12GB&lt;/td&gt;
&lt;td&gt;~$12-18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After (6 cards)&lt;/td&gt;
&lt;td&gt;3060 × 3, 3070 × 2, 3080 × 1&lt;/td&gt;
&lt;td&gt;62GB&lt;/td&gt;
&lt;td&gt;~$65-95&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Not exactly linear scaling. Here's why:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demand is unpredictable.&lt;/strong&gt; Sometimes 4 of my 6 cards are rented simultaneously. Sometimes 1. The RTX 3080 gets picked up more often than the 3060s — higher VRAM matters for LLM inference jobs that need room to load bigger models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not all hours are equal.&lt;/strong&gt; Utilization spikes during US business hours and drops overnight (Turkey time). I'm in a timezone where "overnight for me" overlaps with "peak US working hours," which actually helps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing matters more than I thought.&lt;/strong&gt; I dropped my per-card price slightly and saw utilization go up noticeably. A few cents per hour makes a real difference when renters are comparing a dozen similar options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current Monthly Run Rate
&lt;/h2&gt;

&lt;p&gt;Across all six cards, I'm averaging around &lt;strong&gt;$280-340/month&lt;/strong&gt; before electricity.&lt;/p&gt;

&lt;p&gt;Power costs are real. Six GPUs under load is serious wattage. My electricity bill went up — I haven't calculated the exact delta yet because my bill is shared (I'm not the only one using power in my building), but I'd estimate $40-60/month in additional costs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Net: roughly $220-280/month in real passive income.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Is that life-changing? No. Is it meaningful for money that was doing nothing? Absolutely.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Start with a proper open-frame rig, not a cobbled-together case.&lt;/strong&gt;&lt;br&gt;
The mining frame was cheap but took time to source. If I were doing this again I'd budget for it from day one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Get a proper high-wattage PSU setup.&lt;/strong&gt;&lt;br&gt;
Running two PSUs linked together works but it's inelegant. A server PSU with the right adapter is cleaner and safer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Test each card individually before combining them.&lt;/strong&gt;&lt;br&gt;
I wasted time troubleshooting "which card is the problem" when I could've confirmed each one worked before building the full rig.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Set minimum job duration.&lt;/strong&gt;&lt;br&gt;
Short jobs (under an hour) rack up overhead — container spin-up time, handshaking — without much earnings. I set a minimum of 2 hours and earnings-per-hour improved.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Unexpected Part
&lt;/h2&gt;

&lt;p&gt;I expected this to be a boring passive income setup. It mostly is. But I've learned a surprising amount about how the AI inference market actually works by watching what gets rented and when.&lt;/p&gt;

&lt;p&gt;Most renters are running:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fine-tuning jobs (need sustained GPU hours)&lt;/li&gt;
&lt;li&gt;LLM inference (need VRAM more than raw compute)&lt;/li&gt;
&lt;li&gt;Image generation (FLUX, Stable Diffusion variants)&lt;/li&gt;
&lt;li&gt;Dev environments (people testing stuff without committing to a cloud contract)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Watching the demand patterns is actually interesting data about what the AI dev community is building right now. The 3080 almost always goes first — 10GB VRAM hits a sweet spot for smaller Llama and Mistral models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is It Worth It?
&lt;/h2&gt;

&lt;p&gt;Depends on your situation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Yes, if:&lt;/strong&gt; You already have the GPUs and they're sitting idle. The marginal cost of setting this up is mostly your time, and the monthly return is real.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maybe, if:&lt;/strong&gt; You'd have to buy the GPUs. At current used-market prices, payback period is 6-12 months depending on utilization. That's not terrible but it's not obvious.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No, if:&lt;/strong&gt; You're renting out your daily-driver GPU. The rental platform can grab your card at inconvenient times. Keep at least one card reserved for your own use.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;I'm looking at adding the Ubuntu server I have running as a CPU-only Vast.ai host for smaller workloads. Less money per unit but zero additional hardware cost.&lt;/p&gt;

&lt;p&gt;Also thinking about whether it makes sense to eventually get into the dedicated hosting side rather than the rental marketplace — more stable income, more setup required. Still researching.&lt;/p&gt;

&lt;p&gt;For now, 6 cards, ~$250/month net, and a weekend's worth of setup. I'll take it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Questions about the rig setup or Vast.ai specifics? Drop them in the comments.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://www.fiverr.com/s/XLyg" rel="noopener noreferrer"&gt;Check out my automation work on Fiverr&lt;/a&gt;&lt;br&gt;
→ &lt;a href="https://t.me/celebibot_en" rel="noopener noreferrer"&gt;Follow along on Telegram&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gpu</category>
      <category>passiveincome</category>
      <category>selfhosted</category>
      <category>ai</category>
    </item>
    <item>
      <title>My 3-Machine AI Lab: How I Divide Work Between a Mac Mini, a Windows PC, and an Ubuntu Box</title>
      <dc:creator>Sam Hartley</dc:creator>
      <pubDate>Thu, 07 May 2026 08:06:27 +0000</pubDate>
      <link>https://forem.com/samhartley_dev/my-3-machine-ai-lab-how-i-divide-work-between-a-mac-mini-a-windows-pc-and-an-ubuntu-box-3gfi</link>
      <guid>https://forem.com/samhartley_dev/my-3-machine-ai-lab-how-i-divide-work-between-a-mac-mini-a-windows-pc-and-an-ubuntu-box-3gfi</guid>
      <description>&lt;p&gt;I keep seeing posts about running AI on a single machine. "Just use Ollama on your laptop!" Sure, that works — until you want to run a 30B model while your IDE is indexing, your test suite is running, and you're editing a video.&lt;/p&gt;

&lt;p&gt;I have three machines. Not because I'm rich — because I kept old hardware alive and gave each one a job. Here's how I split AI workloads across a Mac Mini M4, a Windows PC with an RTX 3060, and an old Ubuntu box, and why one machine was never enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Machines
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Machine&lt;/th&gt;
&lt;th&gt;Specs&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mac Mini M4&lt;/td&gt;
&lt;td&gt;10-core, 16GB RAM&lt;/td&gt;
&lt;td&gt;Orchestrator, coding, light inference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows PC&lt;/td&gt;
&lt;td&gt;AMD 9970X, 128GB RAM, RTX 3060 12GB&lt;/td&gt;
&lt;td&gt;Heavy inference, image generation, GPU rental&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ubuntu Box&lt;/td&gt;
&lt;td&gt;Older CPU, 16GB RAM&lt;/td&gt;
&lt;td&gt;Background services, OmniParser, CPU inference fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Total cost for the additional machines: the Windows PC was a workstation I already had, the Ubuntu box is a repurposed laptop. The Mac Mini is the only machine I bought specifically for this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Three? Because One Keeps Running Into Walls.
&lt;/h2&gt;

&lt;p&gt;Here's what happens when you try to do everything on a single machine:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On the Mac Mini alone:&lt;/strong&gt; Run a 9B model → fine. Try a 30B model → the fans sound like a jet engine and inference drops to 2 tokens/second because Apple Silicon shares RAM between CPU and GPU. Start a build while inference is running? Everything crawls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On the Windows PC alone:&lt;/strong&gt; The RTX 3060 crushes inference — a 30B model at 4-bit runs at 8-12 tok/s. But the machine is also my GPU rental host (I rent it on Vast.ai for passive income). When someone rents it, I can't use it. And running heavy inference while coding in an IDE on the same machine? Stutter city.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On the Ubuntu box alone:&lt;/strong&gt; It's slow. CPU-only inference on a 7B model takes 30+ seconds per token. But it runs 24/7 without complaints, never reboots for Windows updates, and costs almost nothing in power.&lt;/p&gt;

&lt;p&gt;The answer was never "pick the best one." It was "give each one the right job."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Division of Labor
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mac Mini: The Brain
&lt;/h3&gt;

&lt;p&gt;This is where I actually &lt;em&gt;work&lt;/em&gt;. Code editing, git operations, web browsing, writing. It runs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ollama with small models&lt;/strong&gt; — Qwen 3 4B for quick chat, granite3.2-vision for screenshots&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My automation agents&lt;/strong&gt; — OpenClaw runs here 24/7, orchestrating the other machines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dev environment&lt;/strong&gt; — VS Code, terminal, browser, all the tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key insight: the Mac Mini is for &lt;em&gt;interactive&lt;/em&gt; work. Fast response time matters more than throughput. A 4B model answering in 0.3 seconds beats a 30B model answering in 8 seconds when I'm in the middle of a thought.&lt;/p&gt;

&lt;p&gt;What it does NOT do: heavy batch processing, image generation, anything that pins the CPU for more than a minute.&lt;/p&gt;

&lt;h3&gt;
  
  
  Windows PC: The Muscle
&lt;/h3&gt;

&lt;p&gt;The RTX 3060 with 12GB VRAM is the workhorse. It runs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ollama with big models&lt;/strong&gt; — Qwen3-Coder 30B for complex code, DeepSeek R1 8B for reasoning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FLUX image generation&lt;/strong&gt; — when I need AI photos (I run a Fiverr gig for brand character photos — $0 generation cost when it's your own GPU)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vast.ai host&lt;/strong&gt; — when I'm not using it, someone else pays to rent it. ~$50-130/month passive income.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The scheduling is simple: during my working hours (roughly 9am-11pm Istanbul time), I pause Vast.ai rentals and use the GPU myself. Overnight, it goes back on the rental market.&lt;/p&gt;

&lt;p&gt;128GB of RAM means I can load truly large models or run multiple services simultaneously. The GPU handles inference; the CPU cores handle data prep and post-processing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ubuntu Box: The Backbone
&lt;/h3&gt;

&lt;p&gt;This machine's superpower is reliability. It runs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OmniParser&lt;/strong&gt; — a UI parsing service that my automation agents use to "see" screens (runs on port 8100, I SSH-tunnel to it from the Mac)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CPU inference fallback&lt;/strong&gt; — when the Windows GPU is rented out and the Mac is busy, the Ubuntu box runs minicpm-v for vision tasks. Slow (~180 seconds per query) but it works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background cron jobs&lt;/strong&gt; — data scraping, health checks, monitoring scripts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing ground&lt;/strong&gt; — I deploy new services here first because if something crashes, my main workflow isn't affected&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's the machine I interact with least directly, but it's the one I'd miss most if it went down.&lt;/p&gt;

&lt;h2&gt;
  
  
  How They Talk to Each Other
&lt;/h2&gt;

&lt;p&gt;This was the hardest part to figure out. Three machines that don't communicate are just three isolated computers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The network:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mac Mini (192.168.1.102) ←→ Windows PC (192.168.1.106) ←→ Ubuntu (192.168.1.100)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All on the same local network. No VPN needed at home, but I set up Tailscale for remote access when I'm not home.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ollama routing:&lt;/strong&gt;&lt;br&gt;
The Mac Mini's Ollama is configured with &lt;code&gt;OLLAMA_HOST=0.0.0.0&lt;/code&gt; so it's accessible from the other machines. Same for the Windows PC. My automation agent on the Mac Mini routes requests based on model size:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_ollama_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Route to the right machine based on model size.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;small_models&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qwen3:4b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;granite3.2-vision:2b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;moondream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;big_models&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qwen3-coder:30b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deepseek-r1:8b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;big_models&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://192.168.1.106:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# Windows GPU
&lt;/span&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;small_models&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;       &lt;span class="c1"&gt;# Mac Mini local
&lt;/span&gt;    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://192.168.1.100:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;   &lt;span class="c1"&gt;# Ubuntu fallback
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple. No Kubernetes, no service mesh. Just a function that knows which machine has which model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSH tunnels for services:&lt;/strong&gt;&lt;br&gt;
The Ubuntu box runs OmniParser on port 8100, but I need it on the Mac Mini. Instead of exposing it publicly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Auto-reconnecting SSH tunnel (runs via launchd on Mac)&lt;/span&gt;
ssh &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;ServerAliveInterval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60 &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;ServerAliveCountMax&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-L&lt;/span&gt; 8100:localhost:8100 exp@192.168.1.100 &lt;span class="nt"&gt;-N&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;localhost:8100&lt;/code&gt; on the Mac Mini reaches OmniParser on Ubuntu. Clean, secure, zero config on the Ubuntu side.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mistakes I Made
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Mistake 1: Trying to make the Mac Mini do everything.&lt;/strong&gt;&lt;br&gt;
For the first month, I ran all inference on the Mac Mini. The 16GB unified memory seems like a lot until a 30B model is eating 20GB and your IDE is using another 4GB and macOS starts swapping to SSD. The SSD is fast, but swap is still swap. Code completion went from instant to 3-second delays.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 2: Not setting up Ollama access from day one.&lt;/strong&gt;&lt;br&gt;
I installed Ollama separately on each machine and used them in isolation for weeks. The moment I realized I could call the Windows Ollama from the Mac Mini was a lightbulb moment. The models were always there — I just wasn't using them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 3: Ignoring the Ubuntu box because it's "slow."&lt;/strong&gt;&lt;br&gt;
CPU inference is slow. But "slow" is relative. A background task that takes 3 minutes on CPU doesn't matter if you're not waiting for it. I was treating every request as if it needed to be instant. Turns out, most don't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 4: No fallback plan.&lt;/strong&gt;&lt;br&gt;
When the Windows PC rebooted for an update, my entire inference pipeline died because I had no fallback routing. Now: if Windows is down, Ubuntu takes over. If Ubuntu is down, the Mac runs a tiny model. Something always answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Setup Enables
&lt;/h2&gt;

&lt;p&gt;Running three machines isn't about having the biggest, fastest setup. It's about &lt;strong&gt;never being blocked.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows GPU is rented out? Route to Mac or Ubuntu.&lt;/li&gt;
&lt;li&gt;Mac Mini is busy with a build? Use the Windows GPU via network.&lt;/li&gt;
&lt;li&gt;Ubuntu is running a heavy OmniParser job? Queue it, the Mac handles the request.&lt;/li&gt;
&lt;li&gt;Need to generate 50 images for a client? Windows GPU does it in batch while I keep coding on the Mac.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the cost breakdown:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Monthly Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mac Mini power&lt;/td&gt;
&lt;td&gt;~$8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows PC power (idle + rental hours)&lt;/td&gt;
&lt;td&gt;~$15-25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ubuntu box power&lt;/td&gt;
&lt;td&gt;~$5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vast.ai earnings (offset)&lt;/td&gt;
&lt;td&gt;~$50-130&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Net&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;-$17 to -$97 (net profit)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The setup literally pays for itself. The Windows PC's GPU rental income exceeds the total power cost of all three machines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do You Need Three Machines?
&lt;/h2&gt;

&lt;p&gt;Probably not. But you might need &lt;em&gt;two&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If you're running AI on a laptop: get a cheap desktop (even without a GPU) and put it on your network as a background worker. Ollama on a second machine means your main machine stays responsive.&lt;/p&gt;

&lt;p&gt;If you have a gaming PC: you already have a GPU. Install Ollama on it, set &lt;code&gt;OLLAMA_HOST=0.0.0.0&lt;/code&gt;, and call it from your laptop. You now have a two-machine AI lab for zero additional cost.&lt;/p&gt;

&lt;p&gt;The Ubuntu box in my setup could be a $50 Raspberry Pi 5 with 8GB RAM for the same purpose. It doesn't need to be fast. It needs to be &lt;em&gt;there&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm Adding Next
&lt;/h2&gt;

&lt;p&gt;I have five more GPUs in storage (2× RTX 3070, 1× RTX 3080, 2× RTX 3060). The plan is to build an open-air rig and add them all to the Windows PC for both local inference and Vast.ai rental. That'd give me 62GB total VRAM — enough to run a 70B model locally or rent out as a cluster.&lt;/p&gt;

&lt;p&gt;The Ubuntu box is getting a dedicated role as a monitoring and alerting server. Right now my health checks are scattered across cron jobs on all three machines. Centralizing them makes debugging easier.&lt;/p&gt;

&lt;p&gt;And the Mac Mini? It stays the brain. More RAM would be nice (16GB is tight with Ollama + IDE + browser), but the M4's efficiency is hard to beat for interactive work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;One machine is a workstation. Two machines is a lab. Three machines is a distributed system that happens to fit on a desk.&lt;/p&gt;

&lt;p&gt;The magic isn't in the hardware — it's in the routing. Knowing which machine should handle which task, building fallback paths, and making sure a single machine going down doesn't kill your workflow.&lt;/p&gt;

&lt;p&gt;Start with what you have. Add a second machine when you feel the friction. Route intelligently. The three-machine setup happened organically — each machine earned its place by solving a problem the others couldn't.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I write about running AI locally, home lab setups, and turning hardware into income. New post every few days — follow along if that's your thing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;My GPU rental story: &lt;a href="https://dev.to/samhartley/i-rented-out-my-gpu-for-passive-income-heres-what-happened-after-my-first-week-2l8a"&gt;One card → six cards → $250/month passive&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>infrastructure</category>
      <category>llm</category>
      <category>productivity</category>
    </item>
    <item>
      <title>From Idea to Deployed Tool in 3 Hours — How AI Coding Agents Changed My Workflow</title>
      <dc:creator>Sam Hartley</dc:creator>
      <pubDate>Tue, 05 May 2026 08:06:19 +0000</pubDate>
      <link>https://forem.com/samhartley_dev/from-idea-to-deployed-tool-in-3-hours-how-ai-coding-agents-changed-my-workflow-3dd0</link>
      <guid>https://forem.com/samhartley_dev/from-idea-to-deployed-tool-in-3-hours-how-ai-coding-agents-changed-my-workflow-3dd0</guid>
      <description>&lt;p&gt;I used to think AI coding assistants were autocomplete on steroids. Fancy IntelliSense. Then I tried using one as an actual junior developer — someone who writes the first draft while I review and refine.&lt;/p&gt;

&lt;p&gt;Two months later, my workflow is unrecognizable. I just shipped a complete B2B configuration tool — interactive maps, zone polygons, dynamic forms, the works — in under three hours. Here's what changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Old Way
&lt;/h2&gt;

&lt;p&gt;Before AI agents, my process for a new tool looked like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Research&lt;/strong&gt; — How does Leaflet.js work? What's the API for geo polygons? Stack Overflow, docs, tutorials. 45 minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boilerplate&lt;/strong&gt; — HTML structure, CSS grid, JavaScript imports, event listeners. 30 minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Core logic&lt;/strong&gt; — The actual thing the tool needs to do. 2–3 hours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging&lt;/strong&gt; — Why doesn't the map render? Why is the polygon offset? 1–2 hours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polish&lt;/strong&gt; — Styling, responsive layout, edge cases. 1 hour.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Total: 6–8 hours for a medium-complexity tool.&lt;/strong&gt; And that's if I know the stack. If it's something new (like Garmin's Monkey C or a mapping library I haven't used), double it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The New Way
&lt;/h2&gt;

&lt;p&gt;Last week a client asked for a heat zone map for 21 European countries. Click a country, see the heating zones, pick one, get the right configuration. With polygon boundaries, country-specific defaults, and a responsive UI.&lt;/p&gt;

&lt;p&gt;Here's how it went:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hour 0–0.5: Prompt engineering&lt;/strong&gt;&lt;br&gt;
I wrote a detailed spec. Not "make a map" — that's useless. I described:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The data structure (country → zones → polygon coordinates)&lt;/li&gt;
&lt;li&gt;The UI flow (dropdown → map render → zone selection)&lt;/li&gt;
&lt;li&gt;The tech stack (Leaflet.js, vanilla JS, no frameworks)&lt;/li&gt;
&lt;li&gt;Edge cases (what happens when a country has no zones?)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Hour 0.5–1.5: First draft from the agent&lt;/strong&gt;&lt;br&gt;
I fed the spec to Codex (Claude Code via CLI). It generated the full HTML file — 800+ lines — with Leaflet integration, zone polygons, event handlers, and the selection logic.&lt;/p&gt;

&lt;p&gt;Was it perfect? No. The polygon coordinates were placeholder circles. The styling was bare-bones. But the &lt;em&gt;architecture&lt;/em&gt; was right. The map rendered. The flow worked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hour 1.5–2.5: My turn — polish and fix&lt;/strong&gt;&lt;br&gt;
I replaced the placeholder polygons with real GeoJSON-ish coordinates for all 21 countries. Tweaked the CSS for mobile. Added validation. Fixed a bug where the map didn't re-center when switching countries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hour 2.5–3: Integration and deploy&lt;/strong&gt;&lt;br&gt;
Hooked it into the existing project structure. Git commit. Done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Total: 3 hours.&lt;/strong&gt; And the heavy lifting — the Leaflet setup, the polygon rendering logic, the event wiring — was handled by the agent. I did the creative/problem-solving work: defining the problem, validating the output, fixing the edge cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Works (and What Doesn't)
&lt;/h2&gt;

&lt;p&gt;After two months of daily use, here's my honest assessment:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ What works brilliantly:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Boilerplate and plumbing&lt;/strong&gt; — Setting up projects, imports, basic structure. The agent is faster and makes fewer typos than me.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API integration patterns&lt;/strong&gt; — "Here's an endpoint, here's the expected response, write the fetch and parse logic." It gets this right 90% of the time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactoring&lt;/strong&gt; — "Rename this function and update all callers across 5 files." Instant, error-free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exploring unfamiliar territory&lt;/strong&gt; — I hadn't used Leaflet in years. The agent got me to a working state without reading docs for an hour.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;❌ What doesn't work (yet):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complex business logic&lt;/strong&gt; — Anything with nuanced rules, edge cases, or domain-specific constraints. The agent generates something plausible that breaks in production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI/UX design&lt;/strong&gt; — It makes functional UIs. They look like a developer made them (because one did). You'll still need a human eye for polish.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging its own mistakes&lt;/strong&gt; — When the agent writes a bug, it's often subtle. You need to understand the code to catch it. This is &lt;em&gt;not&lt;/em&gt; a replacement for knowing how to code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Large-scale architecture&lt;/strong&gt; — It works file-by-file. Designing a system with proper separation of concerns, caching strategies, and scalability? That's still on you.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Mindset Shift
&lt;/h2&gt;

&lt;p&gt;The biggest change isn't speed. It's &lt;em&gt;how I think about problems&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Before: "How do I implement this?" → Research → Code → Debug.&lt;br&gt;
Now: "How do I describe this so an agent can implement a first draft?" → Spec → Review → Refine.&lt;/p&gt;

&lt;p&gt;I'm the architect and editor now, not the typist. The agent is the junior dev who writes fast but needs supervision.&lt;/p&gt;

&lt;p&gt;This matters because it scales. I can explore 3 approaches in the time it used to take to build 1. I can say "what if we used a canvas instead of Leaflet?" and get a working comparison in 10 minutes. The cost of experimentation dropped to near-zero.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;If you want to try this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use Claude Code or Codex CLI&lt;/strong&gt; — The terminal interface lets you iterate fast. Chat-based tools (ChatGPT, etc.) are too slow for code generation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write detailed specs&lt;/strong&gt; — The agent is only as good as your prompt. Include examples, expected outputs, and constraints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review every line&lt;/strong&gt; — Don't blindly commit. The agent writes plausible-looking bugs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep a "golden test"&lt;/strong&gt; — A known input/output pair you can run after every change. Catches regressions instantly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterate in small chunks&lt;/strong&gt; — "Add the map" → review → "Add zone polygons" → review. Don't ask for 500 lines at once.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;Two months in, my main learning: &lt;strong&gt;the spec is everything.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My fastest sessions happen when I spend 10 minutes writing a bullet-point spec before touching the agent. My slowest sessions happen when I vague-prompt my way through and spend an hour fixing misunderstandings.&lt;/p&gt;

&lt;p&gt;The second learning: &lt;strong&gt;agents excel at breadth, humans at depth.&lt;/strong&gt; Use the agent to explore options. Use your brain to pick the right one.&lt;/p&gt;




&lt;p&gt;If you're using AI coding agents, what's your experience? I'm curious if the "architect + editor" model resonates, or if you've found a different pattern that works. Drop your thoughts below — I'm still figuring this out too.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>codex</category>
      <category>development</category>
    </item>
    <item>
      <title>I Built a Model Router That Picks the Right AI for Every Task — Here's Why You Should Too</title>
      <dc:creator>Sam Hartley</dc:creator>
      <pubDate>Sun, 03 May 2026 08:01:54 +0000</pubDate>
      <link>https://forem.com/samhartley_dev/i-built-a-model-router-that-picks-the-right-ai-for-every-task-heres-why-you-should-too-5la</link>
      <guid>https://forem.com/samhartley_dev/i-built-a-model-router-that-picks-the-right-ai-for-every-task-heres-why-you-should-too-5la</guid>
      <description>&lt;p&gt;I caught myself doing something stupid the other day. I used a 30B parameter model to summarize a two-paragraph email. The GPU spun up, the fans kicked in, and 8 seconds later I had my summary. The same summary my 4B model would've given me in 0.3 seconds.&lt;/p&gt;

&lt;p&gt;That's when I realized: I'd been treating every AI request the same way. Big model for everything. Small model for nothing. No intelligence in the routing at all.&lt;/p&gt;

&lt;p&gt;So I built a model router. Not a fancy orchestrator with Kubernetes and service meshes — just a simple function that looks at what you're asking and sends it to the right model on the right machine. It's the single most impactful thing I've done for my local AI setup this year.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;I run three machines with Ollama (Mac Mini M4, Windows PC with RTX 3060, Ubuntu box). Between them, I have maybe 8 models installed. Before the router, my workflow was:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Need something → open terminal&lt;/li&gt;
&lt;li&gt;Think about which model is good enough&lt;/li&gt;
&lt;li&gt;Think about which machine has it&lt;/li&gt;
&lt;li&gt;Type the full URL: &lt;code&gt;curl http://192.168.1.106:11434/api/generate -d '{"model": "qwen3-coder:30b", ...}'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Wait&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Steps 2-4 happened every single time. I was spending more mental energy on routing than on the actual task. And half the time I'd default to the biggest model just because "it's probably better."&lt;/p&gt;

&lt;p&gt;Here's the thing: it's usually not better. A 4B model summarizing text is 95% as good as a 30B model summarizing text. But it's 25x faster and uses 1/10th the resources. The big model should earn its keep on tasks where size actually matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Router
&lt;/h2&gt;

&lt;p&gt;I started with a Python function. Nothing fancy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="c1"&gt;# Model registry: what's available where
&lt;/span&gt;&lt;span class="n"&gt;MODELS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quick&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qwen3:4b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;granite3.2-vision:2b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://192.168.1.106:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qwen3-coder:30b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://192.168.1.106:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reasoning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deepseek-r1:8b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://192.168.1.106:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fallback&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;minicpm-v&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://192.168.1.100:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;screenshot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;picture&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;function&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;class&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;debug&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;refactor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;implement&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prove&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;solve&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;calculate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;math&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reasoning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quick&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. That's the whole router. It classifies prompts by keywords and sends them to the appropriate model. Is it perfect? No. Does it need to be? Also no.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters More Than You Think
&lt;/h2&gt;

&lt;p&gt;Before the router, my typical day looked like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;30 quick questions (summarize this, what does this error mean, rephrase this)&lt;/li&gt;
&lt;li&gt;5 code tasks (write a function, debug this, add tests)&lt;/li&gt;
&lt;li&gt;2 vision tasks (what's in this screenshot)&lt;/li&gt;
&lt;li&gt;1 deep reasoning task (complex analysis)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without routing, I'd use the big model for everything. 38 requests to the 30B model. Each taking 5-15 seconds. Total wait time: ~4-5 minutes of just... waiting.&lt;/p&gt;

&lt;p&gt;With routing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;30 quick → 4B model on Mac Mini, 0.3s each → &lt;strong&gt;9 seconds total&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;5 code → 30B on Windows GPU, 8-12s each → &lt;strong&gt;~50 seconds&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;2 vision → vision model on GPU, 4-13s each → &lt;strong&gt;~15 seconds&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;1 reasoning → 8B model on GPU, 5-8s → &lt;strong&gt;~6 seconds&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Total: ~80 seconds&lt;/strong&gt; instead of ~5 minutes. And my GPU is free for most of that time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fallback System
&lt;/h2&gt;

&lt;p&gt;The real value hit me when the Windows PC went down for a Windows Update mid-session. Before, this was catastrophic.&lt;/p&gt;

&lt;p&gt;Now the router has fallback logic — when a machine is down, it finds the next best option. Code tasks go to the small model (worse but functional). Vision tasks try Ubuntu. Quick tasks keep humming on the Mac Mini. Something always answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Token Cost Angle
&lt;/h2&gt;

&lt;p&gt;I track tokens per model per day. Not because I'm cheap (these are all free — local), but because it tells me where I'm spending compute:&lt;/p&gt;

&lt;p&gt;After a week: &lt;strong&gt;72% of my requests went to the quick model.&lt;/strong&gt; Only 15% needed the big code model.&lt;/p&gt;

&lt;p&gt;I was using a sledgehammer for 72% of my nails. No wonder the GPU always felt busy.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Build the router on day one.&lt;/strong&gt; It took an afternoon. I wasted months manually routing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Start with keyword routing.&lt;/strong&gt; I considered embeddings, classifiers, even using an LLM to pick the LLM. Keywords work for 90% of cases. Ship the simple thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Make the fallback automatic.&lt;/strong&gt; My first version just errored when the GPU machine was down. A degraded response is infinitely better than no response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Log everything.&lt;/strong&gt; You can't optimize what you don't measure. The 72% stat jumped out immediately once I started tracking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond Keywords
&lt;/h2&gt;

&lt;p&gt;The keyword router works but has blind spots. So I'm adding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Confidence scoring:&lt;/strong&gt; If a prompt matches multiple categories, try the cheaper model first. Auto-retry with bigger model if quality seems low.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context-aware routing:&lt;/strong&gt; If the last 3 prompts were about the same codebase, keep using the code model even without explicit keywords.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost-aware fallback:&lt;/strong&gt; When local models can't handle something, the router should know whether a cloud API call is "worth it" or not.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Do You Need This?
&lt;/h2&gt;

&lt;p&gt;If you run one model on one machine: no. You're fine.&lt;/p&gt;

&lt;p&gt;If you have more than 2 models: &lt;strong&gt;yes.&lt;/strong&gt; Otherwise you'll default to the biggest one every time, and that's a waste.&lt;/p&gt;

&lt;p&gt;If you have multiple machines: &lt;strong&gt;absolutely.&lt;/strong&gt; The router isn't just about picking the right model — it's about picking the right machine. Code tasks go where the GPU is. Quick tasks stay local. Background tasks go to the always-on box.&lt;/p&gt;

&lt;p&gt;Start with 2 models and a simple if/else. Add more as you grow. The architecture stays the same.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I write about running AI locally, home lab setups, and turning hardware into income. If that's your jam, I post every few days.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>ollama</category>
      <category>selfhosted</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I Built a Model Router That Picks the Right AI for Every Task — Here's Why You Should Too</title>
      <dc:creator>Sam Hartley</dc:creator>
      <pubDate>Fri, 01 May 2026 08:02:21 +0000</pubDate>
      <link>https://forem.com/samhartley_dev/i-built-a-model-router-that-picks-the-right-ai-for-every-task-heres-why-you-should-too-1603</link>
      <guid>https://forem.com/samhartley_dev/i-built-a-model-router-that-picks-the-right-ai-for-every-task-heres-why-you-should-too-1603</guid>
      <description>&lt;p&gt;I caught myself doing something stupid the other day. I used a 30B parameter model to summarize a two-paragraph email. The GPU spun up, the fans kicked in, and 8 seconds later I had my summary. The same summary my 4B model would've given me in 0.3 seconds.&lt;/p&gt;

&lt;p&gt;That's when I realized: I'd been treating every AI request the same way. Big model for everything. Small model for nothing. No intelligence in the routing at all.&lt;/p&gt;

&lt;p&gt;So I built a model router. Not a fancy orchestrator with Kubernetes and service meshes — just a simple function that looks at what you're asking and sends it to the right model on the right machine. It's the single most impactful thing I've done for my local AI setup this year.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;I run three machines with Ollama (Mac Mini M4, Windows PC with RTX 3060, Ubuntu box). Between them, I have maybe 8 models installed. Before the router, my workflow was:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Need something → open terminal&lt;/li&gt;
&lt;li&gt;Think about which model is good enough&lt;/li&gt;
&lt;li&gt;Think about which machine has it&lt;/li&gt;
&lt;li&gt;Type the full URL: &lt;code&gt;curl http://192.168.1.106:11434/api/generate -d '{\"model\": \"qwen3-coder:30b\", ...}'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Wait&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Steps 2-4 happened every single time. I was spending more mental energy on routing than on the actual task. And half the time I'd default to the biggest model just because "it's probably better."&lt;/p&gt;

&lt;p&gt;Here's the thing: it's usually not better. A 4B model summarizing text is 95% as good as a 30B model summarizing text. But it's 25x faster and uses 1/10th the resources. The big model should earn its keep on tasks where size actually matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Router
&lt;/h2&gt;

&lt;p&gt;I started with a Python function. Nothing fancy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="c1"&gt;# Model registry: what's available where
&lt;/span&gt;&lt;span class="n"&gt;MODELS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# Quick chat, summaries, simple Q&amp;amp;A → Mac Mini (fast response)
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quick&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qwen3:4b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;# Vision / image analysis → Windows GPU (needs VRAM)
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;granite3.2-vision:2b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://192.168.1.106:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;# Code generation, complex reasoning → Windows GPU
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qwen3-coder:30b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://192.168.1.106:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;# Deep reasoning, math, logic puzzles → Windows GPU
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reasoning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deepseek-r1:8b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://192.168.1.106:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;# Fallback for when Windows is rented out → Ubuntu CPU
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fallback&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;minicpm-v&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://192.168.1.100:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Decide which model should handle this prompt.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Vision tasks
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;screenshot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;picture&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;see this&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;what&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s on&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Code tasks
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;function&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;class&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;debug&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;refactor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;implement&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                             &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;write code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;python&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;javascript&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;typescript&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                             &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sql&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;regex&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Reasoning tasks
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prove&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;solve&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;calculate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;math&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                             &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reason&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;analyze&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;compare&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;evaluate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reasoning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Default: quick model for everything else
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quick&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. That's the whole router. It classifies prompts by keywords and sends them to the appropriate model. Is it perfect? No. Does it need to be? Also no.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters More Than You Think
&lt;/h2&gt;

&lt;p&gt;Before the router, my typical day looked like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;30 quick questions (summarize this, what does this error mean, rephrase this)&lt;/li&gt;
&lt;li&gt;5 code tasks (write a function, debug this, add tests)&lt;/li&gt;
&lt;li&gt;2 vision tasks (what's in this screenshot)&lt;/li&gt;
&lt;li&gt;1 deep reasoning task (complex analysis)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without routing, I'd use the big model for everything. 38 requests to the 30B model. Each taking 5-15 seconds. Total wait time: ~4-5 minutes of just... waiting. While my GPU is maxed out and I can't run anything else.&lt;/p&gt;

&lt;p&gt;With routing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;30 quick → 4B model on Mac Mini, 0.3s each → &lt;strong&gt;9 seconds total&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;5 code → 30B on Windows GPU, 8-12s each → &lt;strong&gt;~50 seconds&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;2 vision → vision model on GPU, 4-13s each → &lt;strong&gt;~15 seconds&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;1 reasoning → 8B model on GPU, 5-8s → &lt;strong&gt;~6 seconds&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Total: ~80 seconds&lt;/strong&gt; instead of ~5 minutes. And my GPU is free for most of that time, so I could be generating images or renting it out simultaneously.&lt;/p&gt;

&lt;p&gt;The router doesn't just save time. It frees capacity.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fallback System
&lt;/h2&gt;

&lt;p&gt;The real value of routing hit me when the Windows PC went down for a Windows Update mid-session. Before, this was catastrophic — all my "important" models were on that machine.&lt;/p&gt;

&lt;p&gt;Now the router has fallback logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;route_with_fallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Route with health checks and fallbacks.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;primary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Check if primary endpoint is alive
&lt;/span&gt;    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;endpoint&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/api/tags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;primary&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;  &lt;span class="c1"&gt;# Primary is down, find fallback
&lt;/span&gt;
    &lt;span class="c1"&gt;# Fallback chain based on task type
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;primary&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="c1"&gt;# Can't run 30B on Mac, but 4B is better than nothing
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quick&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;primary&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="c1"&gt;# Try Ubuntu's slow vision model
&lt;/span&gt;        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fallback&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;endpoint&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/api/tags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fallback&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quick&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# No vision available, use text
&lt;/span&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;primary&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reasoning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quick&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Quick model can reason a bit
&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quick&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Windows reboots, my workflow doesn't stop. The router sends code tasks to the small model (worse but functional), vision tasks to Ubuntu (slow but works), and quick tasks keep humming along on the Mac Mini. Something always answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Token Cost Angle
&lt;/h2&gt;

&lt;p&gt;I also track tokens per model per day. Not because I'm cheap (these are all free — they run locally), but because it tells me where I'm spending compute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Simple token counter per route
&lt;/span&gt;&lt;span class="n"&gt;token_log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;track_tokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;route_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;token_log&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;route_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;
    &lt;span class="c1"&gt;# Log daily totals to a file
&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token-usage-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;today&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;route&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;route_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()})&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a week of tracking, I found: &lt;strong&gt;72% of my requests were going to the quick model.&lt;/strong&gt; Only 15% actually needed the big code model. 8% were vision. 5% reasoning.&lt;/p&gt;

&lt;p&gt;I was using a sledgehammer for 72% of my nails. No wonder the GPU always felt busy.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently If I Started Over
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Build the router on day one, not month three.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I wasted months manually routing. The router took an afternoon to write. If you're running multiple models, build the routing first. You can always make it smarter later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Start with keyword routing. Don't over-engineer it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I considered using embeddings to classify prompts. I considered training a tiny classifier. I considered using an LLM to decide which LLM to use (yes, really). Keyword matching works for 90% of cases. Ship the simple thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Make the fallback automatic.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My first version just errored when the GPU machine was down. The fallback logic was an afterthought. It should've been the first thing I built — because machines go down, and a degraded response is infinitely better than no response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Log everything.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can't optimize what you don't measure. Once I started logging which routes were used, the 72% quick-model stat jumped out immediately. Without logs, I would've kept thinking I needed the big model "most of the time."&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond Keywords: What I'm Adding Next
&lt;/h2&gt;

&lt;p&gt;The keyword router works, but it has blind spots. A prompt like "help me understand why this function is slow" could be code OR reasoning. So I'm adding:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confidence scoring:&lt;/strong&gt; If the prompt matches multiple categories, route to the cheaper model first. If the response quality seems low (response is very short, model seems confused), auto-retry with the bigger model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context-aware routing:&lt;/strong&gt; If the last 3 prompts were all about the same codebase, keep using the code model even if the current prompt doesn't have code keywords. Context continuity matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost-aware routing for cloud fallback:&lt;/strong&gt; When local models can't handle something, I fall back to cloud APIs. The router should know: "this task isn't worth a $0.03 GPT-4 call, use the local model." Or: "this is worth it, spend the API credits."&lt;/p&gt;

&lt;h2&gt;
  
  
  Do You Need This?
&lt;/h2&gt;

&lt;p&gt;If you run one model on one machine: no. You're fine. The router solves a multi-model, multi-machine problem.&lt;/p&gt;

&lt;p&gt;If you're starting to accumulate models (a small one for chat, a big one for code, a vision model): &lt;strong&gt;yes.&lt;/strong&gt; The moment you have more than 2 models, you need routing. Otherwise you'll default to the biggest one every time, and that's a waste.&lt;/p&gt;

&lt;p&gt;If you have multiple machines: &lt;strong&gt;absolutely.&lt;/strong&gt; The router isn't just about picking the right model — it's about picking the right machine. Code tasks go where the GPU is. Quick tasks stay local. Background tasks go to the always-on box.&lt;/p&gt;

&lt;p&gt;The beauty is that the router grows with you. Start with 2 models and a simple if/else. Add more models, add more rules. Add more machines, add more endpoints. The architecture stays the same.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I write about running AI locally, home lab setups, and turning hardware into income. If that's your jam, I post every few days.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;My AI lab setup: &lt;a href="https://dev.to/samhartley/my-3-machine-ai-lab-how-i-divide-work-between-a-mac-mini-a-windows-pc-and-an-ubuntu-box-3b8a"&gt;3 machines, 1 desk, zero cloud dependency&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>ollama</category>
      <category>selfhosted</category>
      <category>productivity</category>
    </item>
    <item>
      <title>My 3-Machine AI Lab: How I Divide Work Between a Mac Mini, a Windows PC, and an Ubuntu Box</title>
      <dc:creator>Sam Hartley</dc:creator>
      <pubDate>Wed, 29 Apr 2026 08:02:28 +0000</pubDate>
      <link>https://forem.com/samhartley_dev/my-3-machine-ai-lab-how-i-divide-work-between-a-mac-mini-a-windows-pc-and-an-ubuntu-box-1p8i</link>
      <guid>https://forem.com/samhartley_dev/my-3-machine-ai-lab-how-i-divide-work-between-a-mac-mini-a-windows-pc-and-an-ubuntu-box-1p8i</guid>
      <description>&lt;p&gt;I keep seeing posts about running AI on a single machine. "Just use Ollama on your laptop!" Sure, that works — until you want to run a 30B model while your IDE is indexing, your test suite is running, and you're editing a video.&lt;/p&gt;

&lt;p&gt;I have three machines. Not because I'm rich — because I kept old hardware alive and gave each one a job. Here's how I split AI workloads across a Mac Mini M4, a Windows PC with an RTX 3060, and an old Ubuntu box, and why one machine was never enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Machines
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Machine&lt;/th&gt;
&lt;th&gt;Specs&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mac Mini M4&lt;/td&gt;
&lt;td&gt;10-core, 16GB RAM&lt;/td&gt;
&lt;td&gt;Orchestrator, coding, light inference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows PC&lt;/td&gt;
&lt;td&gt;AMD 9970X, 128GB RAM, RTX 3060 12GB&lt;/td&gt;
&lt;td&gt;Heavy inference, image generation, GPU rental&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ubuntu Box&lt;/td&gt;
&lt;td&gt;Older CPU, 16GB RAM&lt;/td&gt;
&lt;td&gt;Background services, OmniParser, CPU inference fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Total cost for the additional machines: the Windows PC was a workstation I already had, the Ubuntu box is a repurposed laptop. The Mac Mini is the only machine I bought specifically for this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Three? Because One Keeps Running Into Walls.
&lt;/h2&gt;

&lt;p&gt;Here's what happens when you try to do everything on a single machine:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On the Mac Mini alone:&lt;/strong&gt; Run a 9B model → fine. Try a 30B model → the fans sound like a jet engine and inference drops to 2 tokens/second because Apple Silicon shares RAM between CPU and GPU. Start a build while inference is running? Everything crawls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On the Windows PC alone:&lt;/strong&gt; The RTX 3060 crushes inference — a 30B model at 4-bit runs at 8-12 tok/s. But the machine is also my GPU rental host (I rent it on Vast.ai for passive income). When someone rents it, I can't use it. And running heavy inference while coding in an IDE on the same machine? Stutter city.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On the Ubuntu box alone:&lt;/strong&gt; It's slow. CPU-only inference on a 7B model takes 30+ seconds per token. But it runs 24/7 without complaints, never reboots for Windows updates, and costs almost nothing in power.&lt;/p&gt;

&lt;p&gt;The answer was never "pick the best one." It was "give each one the right job."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Division of Labor
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mac Mini: The Brain
&lt;/h3&gt;

&lt;p&gt;This is where I actually &lt;em&gt;work&lt;/em&gt;. Code editing, git operations, web browsing, writing. It runs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ollama with small models&lt;/strong&gt; — Qwen 3 4B for quick chat, granite3.2-vision for screenshots&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My automation agents&lt;/strong&gt; — OpenClaw runs here 24/7, orchestrating the other machines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dev environment&lt;/strong&gt; — VS Code, terminal, browser, all the tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key insight: the Mac Mini is for &lt;em&gt;interactive&lt;/em&gt; work. Fast response time matters more than throughput. A 4B model answering in 0.3 seconds beats a 30B model answering in 8 seconds when I'm in the middle of a thought.&lt;/p&gt;

&lt;p&gt;What it does NOT do: heavy batch processing, image generation, anything that pins the CPU for more than a minute.&lt;/p&gt;

&lt;h3&gt;
  
  
  Windows PC: The Muscle
&lt;/h3&gt;

&lt;p&gt;The RTX 3060 with 12GB VRAM is the workhorse. It runs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ollama with big models&lt;/strong&gt; — Qwen3-Coder 30B for complex code, DeepSeek R1 8B for reasoning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FLUX image generation&lt;/strong&gt; — when I need AI photos (I run a Fiverr gig for brand character photos — $0 generation cost when it's your own GPU)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vast.ai host&lt;/strong&gt; — when I'm not using it, someone else pays to rent it. ~$50-130/month passive income.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The scheduling is simple: during my working hours (roughly 9am-11pm Istanbul time), I pause Vast.ai rentals and use the GPU myself. Overnight, it goes back on the rental market.&lt;/p&gt;

&lt;p&gt;128GB of RAM means I can load truly large models or run multiple services simultaneously. The GPU handles inference; the CPU cores handle data prep and post-processing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ubuntu Box: The Backbone
&lt;/h3&gt;

&lt;p&gt;This machine's superpower is reliability. It runs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OmniParser&lt;/strong&gt; — a UI parsing service that my automation agents use to "see" screens (runs on port 8100, I SSH-tunnel to it from the Mac)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CPU inference fallback&lt;/strong&gt; — when the Windows GPU is rented out and the Mac is busy, the Ubuntu box runs minicpm-v for vision tasks. Slow (~180 seconds per query) but it works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background cron jobs&lt;/strong&gt; — data scraping, health checks, monitoring scripts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing ground&lt;/strong&gt; — I deploy new services here first because if something crashes, my main workflow isn't affected&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's the machine I interact with least directly, but it's the one I'd miss most if it went down.&lt;/p&gt;

&lt;h2&gt;
  
  
  How They Talk to Each Other
&lt;/h2&gt;

&lt;p&gt;This was the hardest part to figure out. Three machines that don't communicate are just three isolated computers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The network:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mac Mini (192.168.1.102) ←→ Windows PC (192.168.1.106) ←→ Ubuntu (192.168.1.100)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All on the same local network. No VPN needed at home, but I set up Tailscale for remote access when I'm not home.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ollama routing:&lt;/strong&gt;&lt;br&gt;
The Mac Mini's Ollama is configured with &lt;code&gt;OLLAMA_HOST=0.0.0.0&lt;/code&gt; so it's accessible from the other machines. Same for the Windows PC. My automation agent on the Mac Mini routes requests based on model size:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_ollama_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Route to the right machine based on model size.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;small_models&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qwen3:4b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;granite3.2-vision:2b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;moondream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;big_models&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qwen3-coder:30b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deepseek-r1:8b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;big_models&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://192.168.1.106:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# Windows GPU
&lt;/span&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;small_models&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;       &lt;span class="c1"&gt;# Mac Mini local
&lt;/span&gt;    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://192.168.1.100:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;   &lt;span class="c1"&gt;# Ubuntu fallback
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple. No Kubernetes, no service mesh. Just a function that knows which machine has which model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSH tunnels for services:&lt;/strong&gt;&lt;br&gt;
The Ubuntu box runs OmniParser on port 8100, but I need it on the Mac Mini. Instead of exposing it publicly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Auto-reconnecting SSH tunnel (runs via launchd on Mac)&lt;/span&gt;
ssh &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;ServerAliveInterval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60 &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;ServerAliveCountMax&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-L&lt;/span&gt; 8100:localhost:8100 exp@192.168.1.100 &lt;span class="nt"&gt;-N&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;localhost:8100&lt;/code&gt; on the Mac Mini reaches OmniParser on Ubuntu. Clean, secure, zero config on the Ubuntu side.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mistakes I Made
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Mistake 1: Trying to make the Mac Mini do everything.&lt;/strong&gt;&lt;br&gt;
For the first month, I ran all inference on the Mac Mini. The 16GB unified memory seems like a lot until a 30B model is eating 20GB and your IDE is using another 4GB and macOS starts swapping to SSD. The SSD is fast, but swap is still swap. Code completion went from instant to 3-second delays.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 2: Not setting up Ollama access from day one.&lt;/strong&gt;&lt;br&gt;
I installed Ollama separately on each machine and used them in isolation for weeks. The moment I realized I could call the Windows Ollama from the Mac Mini was a lightbulb moment. The models were always there — I just wasn't using them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 3: Ignoring the Ubuntu box because it's "slow."&lt;/strong&gt;&lt;br&gt;
CPU inference is slow. But "slow" is relative. A background task that takes 3 minutes on CPU doesn't matter if you're not waiting for it. I was treating every request as if it needed to be instant. Turns out, most don't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 4: No fallback plan.&lt;/strong&gt;&lt;br&gt;
When the Windows PC rebooted for an update, my entire inference pipeline died because I had no fallback routing. Now: if Windows is down, Ubuntu takes over. If Ubuntu is down, the Mac runs a tiny model. Something always answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Setup Enables
&lt;/h2&gt;

&lt;p&gt;Running three machines isn't about having the biggest, fastest setup. It's about &lt;strong&gt;never being blocked.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows GPU is rented out? Route to Mac or Ubuntu.&lt;/li&gt;
&lt;li&gt;Mac Mini is busy with a build? Use the Windows GPU via network.&lt;/li&gt;
&lt;li&gt;Ubuntu is running a heavy OmniParser job? Queue it, the Mac handles the request.&lt;/li&gt;
&lt;li&gt;Need to generate 50 images for a client? Windows GPU does it in batch while I keep coding on the Mac.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the cost breakdown:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Monthly Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mac Mini power&lt;/td&gt;
&lt;td&gt;~$8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows PC power (idle + rental hours)&lt;/td&gt;
&lt;td&gt;~$15-25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ubuntu box power&lt;/td&gt;
&lt;td&gt;~$5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vast.ai earnings (offset)&lt;/td&gt;
&lt;td&gt;~$50-130&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Net&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;-$17 to -$97 (net profit)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The setup literally pays for itself. The Windows PC's GPU rental income exceeds the total power cost of all three machines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do You Need Three Machines?
&lt;/h2&gt;

&lt;p&gt;Probably not. But you might need &lt;em&gt;two&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If you're running AI on a laptop: get a cheap desktop (even without a GPU) and put it on your network as a background worker. Ollama on a second machine means your main machine stays responsive.&lt;/p&gt;

&lt;p&gt;If you have a gaming PC: you already have a GPU. Install Ollama on it, set &lt;code&gt;OLLAMA_HOST=0.0.0.0&lt;/code&gt;, and call it from your laptop. You now have a two-machine AI lab for zero additional cost.&lt;/p&gt;

&lt;p&gt;The Ubuntu box in my setup could be a $50 Raspberry Pi 5 with 8GB RAM for the same purpose. It doesn't need to be fast. It needs to be &lt;em&gt;there&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm Adding Next
&lt;/h2&gt;

&lt;p&gt;I have five more GPUs in storage (2× RTX 3070, 1× RTX 3080, 2× RTX 3060). The plan is to build an open-air rig and add them all to the Windows PC for both local inference and Vast.ai rental. That'd give me 62GB total VRAM — enough to run a 70B model locally or rent out as a cluster.&lt;/p&gt;

&lt;p&gt;The Ubuntu box is getting a dedicated role as a monitoring and alerting server. Right now my health checks are scattered across cron jobs on all three machines. Centralizing them makes debugging easier.&lt;/p&gt;

&lt;p&gt;And the Mac Mini? It stays the brain. More RAM would be nice (16GB is tight with Ollama + IDE + browser), but the M4's efficiency is hard to beat for interactive work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;One machine is a workstation. Two machines is a lab. Three machines is a distributed system that happens to fit on a desk.&lt;/p&gt;

&lt;p&gt;The magic isn't in the hardware — it's in the routing. Knowing which machine should handle which task, building fallback paths, and making sure a single machine going down doesn't kill your workflow.&lt;/p&gt;

&lt;p&gt;Start with what you have. Add a second machine when you feel the friction. Route intelligently. The three-machine setup happened organically — each machine earned its place by solving a problem the others couldn't.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I write about running AI locally, home lab setups, and turning hardware into income. New post every few days — follow along if that's your thing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;My GPU rental story: &lt;a href="https://dev.to/samhartley/i-rented-out-my-gpu-for-passive-income-heres-what-happened-after-my-first-week-2l8a"&gt;One card → six cards → $250/month passive&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>selfhosted</category>
      <category>homelab</category>
      <category>devops</category>
    </item>
    <item>
      <title>3 Months Running Everything Locally — What Broke, What Worked, What I'd Do Differently</title>
      <dc:creator>Sam Hartley</dc:creator>
      <pubDate>Thu, 09 Apr 2026 08:03:48 +0000</pubDate>
      <link>https://forem.com/samhartley_dev/3-months-running-everything-locally-what-broke-what-worked-what-id-do-differently-3e1b</link>
      <guid>https://forem.com/samhartley_dev/3-months-running-everything-locally-what-broke-what-worked-what-id-do-differently-3e1b</guid>
      <description>&lt;p&gt;It's been about three months since I made the switch. No more ChatGPT Plus. No more Claude subscription. No more Copilot. Everything I use day-to-day now runs on hardware sitting in my apartment — a Mac mini M4 and a PC with a mix of consumer GPUs.&lt;/p&gt;

&lt;p&gt;I wrote the enthusiastic "I ditched OpenAI" post back in early March. This is the honest follow-up, because a lot of people asked me to come back after the honeymoon phase.&lt;/p&gt;

&lt;p&gt;Some of it worked better than I expected. Some of it was genuinely annoying. Here's the unfiltered version.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup (short version)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mac mini M4&lt;/strong&gt;, 16 GB RAM — runs the orchestrator, small models, all the glue code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PC with an RTX 3060 12GB&lt;/strong&gt; (plus spare 3070s and 3080s in a drawer) — runs the heavier models via Ollama&lt;/li&gt;
&lt;li&gt;Everything talks over my LAN. Nothing leaves the house unless I tell it to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I use this stuff for coding help, writing drafts, summarizing articles, transcribing voice notes, and a handful of personal automations.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually worked
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Coding help for "normal" tasks
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;qwen3-coder:30b&lt;/code&gt; on the 3060 handles maybe 80% of what I used to ask GPT-4 for. Refactoring a function, explaining a gnarly regex, writing a quick shell script, sketching a React component. It's fast enough that I don't miss the cloud.&lt;/p&gt;

&lt;p&gt;The latency is actually &lt;em&gt;better&lt;/em&gt; than cloud APIs because there's no round trip to the US. I'll type a prompt and have tokens streaming back in under a second.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Voice notes and transcription
&lt;/h3&gt;

&lt;p&gt;I didn't expect this to be the killer app, but it is. I talk to my Mac mini while I'm cooking, and Whisper on-device dumps the text into a daily markdown file. Zero cost, zero privacy worries, zero "oh I forgot to renew my API key."&lt;/p&gt;

&lt;p&gt;This alone probably saved me more time than the coding stuff.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Batch jobs that used to rack up API bills
&lt;/h3&gt;

&lt;p&gt;Summarizing 200 PDFs. Tagging a folder of screenshots. Generating alt text for a blog's image archive. These were the things that made me nervous to click "run" on OpenAI. Now I just let them chew overnight on the PC. Electricity is cheaper than tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  What broke or annoyed me
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The "it's almost right" gap on hard stuff
&lt;/h3&gt;

&lt;p&gt;For anything genuinely hard — like debugging a weird async bug in a codebase the model hasn't seen, or reasoning about a tricky algorithm — the gap between local 30B models and frontier cloud models is still real. It's not huge, but it's there.&lt;/p&gt;

&lt;p&gt;I caught myself a few times thinking "I bet GPT-5 would get this in one shot" while I was on my fifth prompt with a local model. That's the honest truth.&lt;/p&gt;

&lt;p&gt;My workaround: I keep a very small pay-as-you-go budget for the 2-3 times a month I actually need a frontier model. Probably $5/month total. Way cheaper than any subscription.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Context windows
&lt;/h3&gt;

&lt;p&gt;Local models with huge contexts exist, but they get &lt;em&gt;slow&lt;/em&gt;. Pasting a 50-file codebase into an 8B model and waiting is painful. I ended up writing a little script that does smart file selection instead of just dumping everything — basically a poor man's RAG. Worked better than I expected, but it was a weekend of yak-shaving I wasn't planning on.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Model sprawl
&lt;/h3&gt;

&lt;p&gt;I have like 14 models pulled right now. &lt;code&gt;qwen3-coder&lt;/code&gt; for code. &lt;code&gt;deepseek-r1&lt;/code&gt; for reasoning. A vision model for screenshots. Whisper for audio. A small embedding model. A translator. Each one made sense at the time. Now my Ollama directory is 180 GB and I can't remember what half of them are for.&lt;/p&gt;

&lt;p&gt;I need to do a spring cleaning. I keep putting it off.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The "is it plugged in" problem
&lt;/h3&gt;

&lt;p&gt;My PC is in another room. Sometimes my wife moves stuff and unplugs the switch. Sometimes Windows decides to reboot for updates at 3am. Sometimes the LAN cable gets bumped.&lt;/p&gt;

&lt;p&gt;Cloud APIs just... work. Local stuff requires you to be your own SRE. I have health checks now. I have a Telegram bot that pings me when Ollama stops responding. This is not the kind of "home lab tinkering" I signed up for, but here we are.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently if I started today
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Skip the "run everything locally or nothing" purity thing.&lt;/strong&gt; I wasted a few weeks trying to make local models do things they're just not good at yet. The sweet spot is a hybrid setup: local for the 90% of boring stuff, a small cloud budget for the 10% that genuinely needs a bigger brain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Buy one strong GPU instead of three mediocre ones.&lt;/strong&gt; I have a drawer full of 3070s and 3080s I thought I'd use for "multi-GPU inference." In practice, a single 12GB card running a good 30B model handles almost everything I need, and juggling multiple cards adds complexity I don't want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Put the models behind one API, not five.&lt;/strong&gt; Ollama, llama.cpp, a Whisper server, a vision endpoint, embeddings... I should have stood up a single gateway that routes to the right backend. Instead I have five different base URLs in five different config files. It's fine, but it's ugly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write down what each model is for.&lt;/strong&gt; Past-me assumed future-me would remember. Future-me does not remember.&lt;/p&gt;

&lt;h2&gt;
  
  
  Would I go back?
&lt;/h2&gt;

&lt;p&gt;No. But I'd stop telling people "local AI is ready, ditch the subscriptions" like it's some binary choice. It's not.&lt;/p&gt;

&lt;p&gt;If you're a developer who likes tinkering, has a decent GPU already, and wants to cut a $20-40/month subscription — yeah, it's great. You'll learn a lot, you'll own your stack, and you won't feel weird about pasting private code into someone else's API.&lt;/p&gt;

&lt;p&gt;If you just want the best possible model for your work and don't want to babysit anything — stay on the cloud. There's no shame in it.&lt;/p&gt;

&lt;p&gt;I'm in the first camp, but I was wrong to pretend the second camp was being lazy. They were being reasonable.&lt;/p&gt;




&lt;p&gt;Anyone else running a mixed setup like this? I'm curious what other people's "local for X, cloud for Y" split looks like. Drop it in the comments — I'm especially interested in how you're handling the context-window problem without building your own RAG from scratch.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>selfhosted</category>
      <category>homelab</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I Let AI Coding Agents Build My Side Projects for a Month — Here's My Honest Take</title>
      <dc:creator>Sam Hartley</dc:creator>
      <pubDate>Sun, 05 Apr 2026 08:04:00 +0000</pubDate>
      <link>https://forem.com/samhartley_dev/i-let-ai-coding-agents-build-my-side-projects-for-a-month-heres-my-honest-take-52l3</link>
      <guid>https://forem.com/samhartley_dev/i-let-ai-coding-agents-build-my-side-projects-for-a-month-heres-my-honest-take-52l3</guid>
      <description>&lt;p&gt;Last month I ran an experiment: instead of writing code myself, I delegated as much as possible to AI coding agents. Not just autocomplete — full autonomous agents that read files, run commands, and ship features.&lt;/p&gt;

&lt;p&gt;I've been running a home lab (Mac Mini M4 + a Windows PC with GPUs + an Ubuntu box) for a while now, and I already had &lt;a href="https://dev.to/samhartley/how-i-automated-my-entire-dev-workflow-with-ai-agents-running-247-on-a-mac-mini-1gdi"&gt;my dev workflow automated with AI agents&lt;/a&gt;. But this time I pushed further: what if the agents didn't just &lt;em&gt;help&lt;/em&gt; me code, but actually &lt;em&gt;wrote&lt;/em&gt; the code?&lt;/p&gt;

&lt;p&gt;Here's what happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;I used a mix of tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code&lt;/strong&gt; (CLI) — my go-to for complex, multi-file tasks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codex&lt;/strong&gt; (OpenAI) — good for one-shot generation with clear specs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local models via Ollama&lt;/strong&gt; — for quick iterations without burning API credits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The workflow: I'd describe what I wanted in plain English, point the agent at the right directory, and let it work. Sometimes I'd review the output. Sometimes I'd just run the tests and ship it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Worked
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Boilerplate and scaffolding — 10/10
&lt;/h3&gt;

&lt;p&gt;This is where agents shine. "Set up a FastAPI project with SQLite, async endpoints, and Pydantic models for a subscriber management system." Done in 90 seconds. Would've taken me 20 minutes of copy-pasting from old projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Refactoring — 8/10
&lt;/h3&gt;

&lt;p&gt;"Refactor this 400-line script into separate modules with proper error handling." The agents were surprisingly good at this. They understood the intent, split things logically, and even added type hints I'd been too lazy to write.&lt;/p&gt;

&lt;p&gt;The two points I'm docking: they sometimes over-abstract. I'd ask for "cleaner code" and get an enterprise-grade factory pattern for a 50-line script. You still need taste.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Writing tests — 9/10
&lt;/h3&gt;

&lt;p&gt;This was the biggest win I didn't expect. I hate writing tests. The agents &lt;em&gt;love&lt;/em&gt; writing tests. "Write pytest tests for this module, cover edge cases" → comprehensive test suite in under a minute.&lt;/p&gt;

&lt;p&gt;The catch: you need to actually &lt;em&gt;read&lt;/em&gt; the tests. I caught a few that were testing the wrong thing — they'd pass, but they weren't testing what mattered. Still, it's a massive time saver.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Debugging — 7/10
&lt;/h3&gt;

&lt;p&gt;Hit or miss. For straightforward bugs ("this function returns None when it should return a list"), agents crush it. For subtle timing issues or race conditions? They'd suggest fixes that looked right but didn't address the root cause.&lt;/p&gt;

&lt;p&gt;My rule now: agents for the first pass, then I debug the debugger's output.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Documentation — 9/10
&lt;/h3&gt;

&lt;p&gt;README files, docstrings, API docs. Agents are better at this than I am, honestly. They're more thorough, more consistent, and they don't get lazy halfway through.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Didn't Work
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Complex architecture decisions
&lt;/h3&gt;

&lt;p&gt;"Should I use Redis or just in-memory caching for this?" The agent will give you a perfectly reasonable answer either way. That's the problem — it doesn't &lt;em&gt;know&lt;/em&gt; your constraints the way you do. How many users? What's your memory budget? Are you running on a Raspberry Pi or a 128GB server?&lt;/p&gt;

&lt;p&gt;I stopped asking agents for architecture advice. I make the decision, then let them implement it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-service orchestration
&lt;/h3&gt;

&lt;p&gt;When I needed to coordinate between my Telegram bot, a background worker, and a database — and they all needed to agree on a shared state model — the agent would nail each piece individually but miss the integration points. I'd end up with three perfectly written services that didn't quite talk to each other.&lt;/p&gt;

&lt;h3&gt;
  
  
  Anything involving my specific hardware
&lt;/h3&gt;

&lt;p&gt;"Configure this for my RTX 3060 with 12GB VRAM running on Windows with WSL2." The agents would give me generic CUDA setup instructions instead of what actually works on my rig. Local knowledge is still human knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;p&gt;Over the month, across 4 side projects:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before (manual)&lt;/th&gt;
&lt;th&gt;After (agent-assisted)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Time to MVP&lt;/td&gt;
&lt;td&gt;~2 weeks&lt;/td&gt;
&lt;td&gt;~4 days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lines written by me&lt;/td&gt;
&lt;td&gt;~80%&lt;/td&gt;
&lt;td&gt;~30%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bugs in first deploy&lt;/td&gt;
&lt;td&gt;~8-12&lt;/td&gt;
&lt;td&gt;~5-8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time spent reviewing agent code&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;~2h/day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overall velocity&lt;/td&gt;
&lt;td&gt;1x&lt;/td&gt;
&lt;td&gt;~2.5x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 2.5x multiplier is real but misleading. I'm faster at &lt;em&gt;producing code&lt;/em&gt;, but I spend more time &lt;em&gt;reviewing&lt;/em&gt; code. The net gain is still significant — maybe 1.8x when you account for review time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Changed About My Workflow
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. I write specs, not code.&lt;/strong&gt;&lt;br&gt;
My job shifted from "programmer" to "technical product manager." I write clear descriptions of what I want, define the interfaces, and let the agent fill in the implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. I review harder.&lt;/strong&gt;&lt;br&gt;
When I wrote the code myself, I'd eyeball it and move on. When an agent writes it, I actually read every line. Paradoxically, this has made me a better reviewer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. I prototype faster, throw away more.&lt;/strong&gt;&lt;br&gt;
Since generating a prototype takes minutes instead of hours, I build 2-3 approaches and pick the best one. This was a luxury I couldn't afford before.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. I still write the hard parts.&lt;/strong&gt;&lt;br&gt;
State machines, complex business logic, performance-critical paths — I write these myself. Not because the agents can't, but because I need to &lt;em&gt;understand&lt;/em&gt; them deeply to maintain them later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Uncomfortable Truth
&lt;/h2&gt;

&lt;p&gt;Here's what nobody in the "AI will replace developers" discourse talks about: &lt;strong&gt;you need to be a good developer to use AI coding agents well.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every time the agent produced something subtly wrong, I caught it because I knew what right looked like. Every time it over-engineered a solution, I could simplify it because I understood the problem. Every time it picked the wrong library or pattern, I could redirect it because I had opinions forged by years of mistakes.&lt;/p&gt;

&lt;p&gt;Junior devs using these tools will ship faster. They'll also ship more bugs, more over-engineering, and more "it works but nobody can maintain it" code. The tools amplify whatever skill level you bring.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Recommendation
&lt;/h2&gt;

&lt;p&gt;If you're not using AI coding agents yet, start with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tests first.&lt;/strong&gt; Let agents write your test suites. Low risk, high reward.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boilerplate second.&lt;/strong&gt; Project setup, CRUD endpoints, config files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactoring third.&lt;/strong&gt; Point it at your worst file and say "clean this up."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex features last.&lt;/strong&gt; Only after you trust the tool and know its limits.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And always, always review the output. The agent is your fastest junior developer. It's also your most confident one — and confidence without experience is how bugs get shipped.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I write about building things with AI, self-hosting, and turning side projects into income. If you're into that, I post a new article every few days.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Running my own AI setup locally? &lt;a href="https://dev.to/samhartley/i-ditched-openai-and-run-ai-locally-for-free-heres-how-57o6"&gt;Here's how I do it for $0/month.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>coding</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I Expanded My GPU Rental Fleet to 6 Cards — Here's What Happened to My Earnings</title>
      <dc:creator>Sam Hartley</dc:creator>
      <pubDate>Sat, 04 Apr 2026 15:44:11 +0000</pubDate>
      <link>https://forem.com/samhartley_dev/i-expanded-my-gpu-rental-fleet-to-6-cards-heres-what-happened-to-my-earnings-10d5</link>
      <guid>https://forem.com/samhartley_dev/i-expanded-my-gpu-rental-fleet-to-6-cards-heres-what-happened-to-my-earnings-10d5</guid>
      <description>&lt;h1&gt;
  
  
  I Expanded My GPU Rental Fleet to 6 Cards — Here's What Happened to My Earnings
&lt;/h1&gt;

&lt;p&gt;A few weeks ago I wrote about renting out my single RTX 3060 on Vast.ai for passive income. The experiment worked better than I expected, so I did what any reasonable person would do: I went and dug out the five other GPUs sitting in my storage room.&lt;/p&gt;

&lt;p&gt;This is the honest follow-up. What actually happened when I went from 1 GPU to 6.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Backstory
&lt;/h2&gt;

&lt;p&gt;I had a bunch of GPUs from an older setup — two RTX 3070s, one RTX 3080, and two more RTX 3060s. They were collecting dust. The PC they came from got upgraded, the cards went into cardboard boxes, the boxes went under a shelf.&lt;/p&gt;

&lt;p&gt;Total VRAM across all six: around 62GB. Combined retail value when new: probably $3,000+. Current value sitting in boxes: $0/month.&lt;/p&gt;

&lt;p&gt;The math wasn't complicated.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Expansion Actually Took
&lt;/h2&gt;

&lt;p&gt;Here's what I underestimated: it's not just "plug cards in, profit."&lt;/p&gt;

&lt;h3&gt;
  
  
  The hardware side
&lt;/h3&gt;

&lt;p&gt;You can't just stack 6 GPUs into a regular PC case. I had to think about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PCIe slots and bandwidth.&lt;/strong&gt; A standard ATX board has maybe 2-3 real x16 slots. For 6 cards, you're looking at risers, which means a mining-style open frame or a server chassis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Power.&lt;/strong&gt; Each card pulls 150-250W under load. Six cards = potentially 1,200-1,500W just in GPU power. Plus CPU, drives, RAM. My existing 850W PSU was not going to cut it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cooling.&lt;/strong&gt; Cards in a tight case thermal-throttle each other. Open frame was the answer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I ended up using an open-air mining frame I found used for cheap, two PSUs daisy-chained (a sketchy-but-common approach in the mining world), and PCIe risers.&lt;/p&gt;

&lt;p&gt;Setup time: about a full weekend.&lt;/p&gt;

&lt;h3&gt;
  
  
  The software side
&lt;/h3&gt;

&lt;p&gt;Getting all six cards recognized wasn't plug-and-play either. I run Windows on the main PC (easier driver support for NVIDIA), and Vast.ai has a Windows daemon that mostly works — except when it doesn't.&lt;/p&gt;

&lt;p&gt;A few issues I hit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two risers were flaky and caused cards to drop off&lt;/li&gt;
&lt;li&gt;One 3070 had a driver conflict until I did a clean DDU reinstall&lt;/li&gt;
&lt;li&gt;Vast.ai's host dashboard showed 5 GPUs after setup; took me an hour to figure out the sixth wasn't being detected&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total debugging time before everything was stable: another weekend.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Earnings Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setup&lt;/th&gt;
&lt;th&gt;Cards&lt;/th&gt;
&lt;th&gt;VRAM&lt;/th&gt;
&lt;th&gt;Weekly Earnings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Before (1 card)&lt;/td&gt;
&lt;td&gt;RTX 3060&lt;/td&gt;
&lt;td&gt;12GB&lt;/td&gt;
&lt;td&gt;~$12-18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After (6 cards)&lt;/td&gt;
&lt;td&gt;3060 × 3, 3070 × 2, 3080 × 1&lt;/td&gt;
&lt;td&gt;62GB&lt;/td&gt;
&lt;td&gt;~$65-95&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Not exactly linear scaling. Here's why:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demand is unpredictable.&lt;/strong&gt; Sometimes 4 of my 6 cards are rented simultaneously. Sometimes 1. The RTX 3080 gets picked up more often than the 3060s — higher VRAM matters for LLM inference jobs that need room to load bigger models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not all hours are equal.&lt;/strong&gt; Utilization spikes during US business hours and drops overnight (Turkey time). I'm in a timezone where "overnight for me" overlaps with "peak US working hours," which actually helps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing matters more than I thought.&lt;/strong&gt; I dropped my per-card price slightly and saw utilization go up noticeably. A few cents per hour makes a real difference when renters are comparing a dozen similar options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current Monthly Run Rate
&lt;/h2&gt;

&lt;p&gt;Across all six cards, I'm averaging around &lt;strong&gt;$280-340/month&lt;/strong&gt; before electricity.&lt;/p&gt;

&lt;p&gt;Power costs are real. Six GPUs under load is serious wattage. My electricity bill went up — I haven't calculated the exact delta yet because my bill is shared (I'm not the only one using power in my building), but I'd estimate $40-60/month in additional costs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Net: roughly $220-280/month in real passive income.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Is that life-changing? No. Is it meaningful for money that was doing nothing? Absolutely.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Start with a proper open-frame rig, not a cobbled-together case.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The mining frame was cheap but took time to source. If I were doing this again I'd budget for it from day one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Get a proper high-wattage PSU setup.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Running two PSUs linked together works but it's inelegant. A server PSU with the right adapter is cleaner and safer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Test each card individually before combining them.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
I wasted time troubleshooting "which card is the problem" when I could've confirmed each one worked before building the full rig.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Set minimum job duration.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Short jobs (under an hour) rack up overhead — container spin-up time, handshaking — without much earnings. I set a minimum of 2 hours and earnings-per-hour improved.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Unexpected Part
&lt;/h2&gt;

&lt;p&gt;I expected this to be a boring passive income setup. It mostly is. But I've learned a surprising amount about how the AI inference market actually works by watching what gets rented and when.&lt;/p&gt;

&lt;p&gt;Most renters are running:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fine-tuning jobs (need sustained GPU hours)&lt;/li&gt;
&lt;li&gt;LLM inference (need VRAM more than raw compute)&lt;/li&gt;
&lt;li&gt;Image generation (FLUX, Stable Diffusion variants)&lt;/li&gt;
&lt;li&gt;Dev environments (people testing stuff without committing to a cloud contract)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Watching the demand patterns is actually interesting data about what the AI dev community is building right now. The 3080 almost always goes first — 10GB VRAM hits a sweet spot for smaller Llama and Mistral models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is It Worth It?
&lt;/h2&gt;

&lt;p&gt;Depends on your situation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Yes, if:&lt;/strong&gt; You already have the GPUs and they're sitting idle. The marginal cost of setting this up is mostly your time, and the monthly return is real.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maybe, if:&lt;/strong&gt; You'd have to buy the GPUs. At current used-market prices, payback period is 6-12 months depending on utilization. That's not terrible but it's not obvious.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No, if:&lt;/strong&gt; You're renting out your daily-driver GPU. The rental platform can grab your card at inconvenient times. Keep at least one card reserved for your own use.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;I'm looking at adding the Ubuntu server I have running as a CPU-only Vast.ai host for smaller workloads. Less money per unit but zero additional hardware cost.&lt;/p&gt;

&lt;p&gt;Also thinking about whether it makes sense to eventually get into the dedicated hosting side rather than the rental marketplace — more stable income, more setup required. Still researching.&lt;/p&gt;

&lt;p&gt;For now, 6 cards, ~$250/month net, and a weekend's worth of setup. I'll take it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Questions about the rig setup or Vast.ai specifics? Drop them in the comments.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;a href="http://www.fiverr.com/s/XLyg" rel="noopener noreferrer"&gt;Check out my automation work on Fiverr&lt;/a&gt;&lt;br&gt;&lt;br&gt;
→ &lt;a href="https://t.me/celebibot_en" rel="noopener noreferrer"&gt;Follow along on Telegram&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gpu</category>
      <category>passiveincome</category>
      <category>selfhosted</category>
      <category>ai</category>
    </item>
    <item>
      <title>I Use Telegram as My DevOps Dashboard — No Web UI, No VPN, Just Works</title>
      <dc:creator>Sam Hartley</dc:creator>
      <pubDate>Mon, 23 Mar 2026 08:02:32 +0000</pubDate>
      <link>https://forem.com/samhartley_dev/i-use-telegram-as-my-devops-dashboard-no-web-ui-no-vpn-just-works-10bn</link>
      <guid>https://forem.com/samhartley_dev/i-use-telegram-as-my-devops-dashboard-no-web-ui-no-vpn-just-works-10bn</guid>
      <description>&lt;p&gt;I have a bunch of things running 24/7 on a Mac Mini. GPU rental jobs, a Garmin watch face updater, a Fiverr inbox monitor, a funding rate tracker, a few cron jobs. &lt;/p&gt;

&lt;p&gt;For a while I ran a Grafana dashboard to keep an eye on them. It looked impressive. I never opened it.&lt;/p&gt;

&lt;p&gt;What I actually do is check my phone. So I built the monitoring layer there.&lt;/p&gt;

&lt;p&gt;Here's the setup: a lightweight Telegram bot that serves as my entire DevOps interface. Status checks, alerts, and even simple commands — all from the Telegram app I already have open.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not a Proper Dashboard?
&lt;/h2&gt;

&lt;p&gt;Honest answer: dashboards are for teams. If you're a solo dev with a few projects, a fancy web UI creates more overhead than it solves.&lt;/p&gt;

&lt;p&gt;Problems I had with Grafana:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPN required to reach it from outside my home network&lt;/li&gt;
&lt;li&gt;Needs to stay running (another thing to maintain)&lt;/li&gt;
&lt;li&gt;I never actually opened the browser tab&lt;/li&gt;
&lt;li&gt;It didn't &lt;em&gt;push&lt;/em&gt; me information — I had to pull it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Telegram flips this: it pushes alerts to me. I glance at my phone, see what's happening, and move on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Services (cron jobs, Python scripts, shell scripts)
  ↓
Central alert script: notify.sh
  ↓
Telegram Bot API → my phone
  ↓ (optional)
Command bot → runs queries on server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two pieces:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Outbound alerts&lt;/strong&gt; — services send me messages when things happen&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inbound commands&lt;/strong&gt; — I can ask the bot questions from my phone&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Part 1: Dead Simple Alert Script
&lt;/h2&gt;

&lt;p&gt;Every service on my server can call this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# notify.sh — send a Telegram message from any script&lt;/span&gt;
&lt;span class="c"&gt;# Usage: ./notify.sh "Your GPU job finished"&lt;/span&gt;

&lt;span class="nv"&gt;BOT_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_bot_token_here"&lt;/span&gt;
&lt;span class="nv"&gt;CHAT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_chat_id_here"&lt;/span&gt;
&lt;span class="nv"&gt;MESSAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.telegram.org/bot&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BOT_TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/sendMessage"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;chat_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CHAT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MESSAGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;parse_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"HTML"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Any script can now send me a message in one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./notify.sh &lt;span class="s2"&gt;"✅ GPU rental job completed — earned &lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;.40"&lt;/span&gt;
./notify.sh &lt;span class="s2"&gt;"⚠️ Funding rate dropped below threshold on LYN_USDT"&lt;/span&gt;
./notify.sh &lt;span class="s2"&gt;"📬 New Fiverr inquiry from user987"&lt;/span&gt;
./notify.sh &lt;span class="s2"&gt;"❌ Garmin watch face API returned 503"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I spent maybe 20 minutes on this. It replaced a monitoring stack I spent days configuring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Examples from My Setup
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GPU rental monitor:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Runs every 30 min&lt;/span&gt;
&lt;span class="nv"&gt;earnings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;./check_gpu_earnings.sh&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$earnings&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; &lt;span class="s2"&gt;"0"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
  ./notify.sh &lt;span class="s2"&gt;"💰 GPU earned: &lt;/span&gt;&lt;span class="nv"&gt;$earnings&lt;/span&gt;&lt;span class="s2"&gt; today"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Funding rate watcher:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Python script, runs every 15 min via cron
&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_funding_rate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LYN_USDT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# negative = people paying longs
&lt;/span&gt;    &lt;span class="nf"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🔥 LYN funding rate: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;% — worth checking&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Daily summary (9 AM cron):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"📊 Daily Summary — &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y-%m-%d&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;

GPU Jobs: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;get_gpu_count&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; completed
Funding Earned: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;get_funding_total&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;
Fiverr Inquiries: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;get_fiverr_count&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;
Watch Face Updates: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;get_garmin_count&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;

Server uptime: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uptime&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

./notify.sh &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$msg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wake up, check my phone, and immediately know if anything needs attention. No browser, no VPN, no dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: The Command Interface
&lt;/h2&gt;

&lt;p&gt;Outbound alerts are great. But sometimes I want to query the server from my phone.&lt;/p&gt;

&lt;p&gt;I wrote a simple Python bot that listens for commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;telebot&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;

&lt;span class="n"&gt;BOT_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;ALLOWED_USER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;123456789&lt;/span&gt;  &lt;span class="c1"&gt;# your Telegram user ID
&lt;/span&gt;
&lt;span class="n"&gt;bot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;telebot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TeleBot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BOT_TOKEN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;COMMANDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uptime &amp;amp;&amp;amp; free -h &amp;amp;&amp;amp; df -h /&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/gpu&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./check_gpu_status.sh&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/funding&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;python3 check_funding_rates.py --summary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/services&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ps aux | grep -E &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(python|node|ollama)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; | grep -v grep&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@bot.message_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;COMMANDS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;ALLOWED_USER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Not authorized.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="n"&gt;cmd_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# handle /status@botname format
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cmd_text&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;COMMANDS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;COMMANDS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cmd_text&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
            &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;```
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;endraw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
```&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parse_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Markdown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;polling&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;none_stop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now from Telegram I can type &lt;code&gt;/status&lt;/code&gt; and get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; 11:23:15 up 14 days, 3:41,  1 user
Mem:   16Gi   8.2Gi   7.8Gi
/dev/sda1        245G   82G  163G  34%
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or &lt;code&gt;/funding&lt;/code&gt; and get the current rate snapshot. &lt;/p&gt;

&lt;p&gt;The key detail: &lt;code&gt;ALLOWED_USER&lt;/code&gt; check. Only my Telegram ID can run commands. Everyone else gets "Not authorized." Bot tokens are public in the sense that anyone can message your bot — you need to validate the sender.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping It Running
&lt;/h2&gt;

&lt;p&gt;The command bot needs to stay alive. I use a simple systemd service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Telegram DevOps Bot&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network.target&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/python3 /home/user/telegram-bot/bot.py&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;span class="py"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;systemctl enable telegram-bot &amp;amp;&amp;amp; systemctl start telegram-bot&lt;/code&gt; — and it survives reboots.&lt;/p&gt;

&lt;p&gt;On macOS (my setup) I use a launchd plist, same concept.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually Get Alerts For
&lt;/h2&gt;

&lt;p&gt;Not everything. Alert fatigue is real.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alert on:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Job completions (GPU task done, funding cycle closed)&lt;/li&gt;
&lt;li&gt;❌ Errors that need action&lt;/li&gt;
&lt;li&gt;📬 New customer inquiries (Fiverr inbox)&lt;/li&gt;
&lt;li&gt;⚠️ Thresholds crossed (rate drops, disk usage, memory spikes)&lt;/li&gt;
&lt;li&gt;📊 Daily summaries (once a day, morning)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Silence:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Routine successful runs (no news is good news)&lt;/li&gt;
&lt;li&gt;Health checks that pass&lt;/li&gt;
&lt;li&gt;Regular cron completions with no anomalies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is: every message I receive from the bot is something I actually care about. If I'm ignoring 80% of notifications, I'm alerting on the wrong things.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Full Cost
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Telegram Bot API: &lt;strong&gt;free&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;curl&lt;/code&gt; command: comes with your OS&lt;/li&gt;
&lt;li&gt;Python + telebot library: &lt;strong&gt;free&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Running this bot: negligible CPU, ~20MB RAM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My entire monitoring setup costs $0/month and runs on the same Mac Mini as everything else. No SaaS, no cloud logging, no dashboard subscription.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Months Later
&lt;/h2&gt;

&lt;p&gt;I send about 15-20 alerts per day. Daily summary at 9 AM, event-driven messages the rest of the day. I check my phone, see green checkmarks and earnings summaries, and know the server is doing its job.&lt;/p&gt;

&lt;p&gt;The one time the GPU host went offline, I got a message within 5 minutes. Fixed it from my phone during lunch.&lt;/p&gt;

&lt;p&gt;That's the whole point: not more tooling, just the right interface for how I actually work.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Got a monitoring setup you like? Drop it in the comments — always curious what others are running.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;a href="http://www.fiverr.com/s/XLyg" rel="noopener noreferrer"&gt;I build these kinds of automation setups on Fiverr&lt;/a&gt;&lt;br&gt;
→ &lt;a href="https://t.me/celebibot_en" rel="noopener noreferrer"&gt;Follow CelebiBots on Telegram&lt;/a&gt;&lt;/p&gt;

</description>
      <category>telegram</category>
      <category>devops</category>
      <category>selfhosted</category>
      <category>automation</category>
    </item>
    <item>
      <title>I Rented Out My GPU for Passive Income — Here’s What Happened After My First Week</title>
      <dc:creator>Sam Hartley</dc:creator>
      <pubDate>Sat, 21 Mar 2026 08:02:08 +0000</pubDate>
      <link>https://forem.com/samhartley_dev/i-rented-out-my-gpu-for-passive-income-heres-what-happened-after-my-first-week-2689</link>
      <guid>https://forem.com/samhartley_dev/i-rented-out-my-gpu-for-passive-income-heres-what-happened-after-my-first-week-2689</guid>
      <description>&lt;p&gt;I had an RTX 3060 sitting on a shelf.&lt;/p&gt;

&lt;p&gt;Not broken. Not old. Just... not doing anything. My Windows PC runs models when I need them, but most of the time it's idle. The fans spin, the power draw ticks along, and that 12GB of VRAM just sits there.&lt;/p&gt;

&lt;p&gt;A week ago I connected it to &lt;a href="https://vast.ai" rel="noopener noreferrer"&gt;Vast.ai&lt;/a&gt; — a GPU marketplace where people rent compute time. No code required. You install a daemon, set a price, and wait for someone to rent your machine.&lt;/p&gt;

&lt;p&gt;Here's what actually happened.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I Didn't Just Mine Crypto
&lt;/h2&gt;

&lt;p&gt;First thing people ask: "Why not just mine?"&lt;/p&gt;

&lt;p&gt;Short answer: it's 2026, the margins are brutal, and I didn't want to deal with it. GPU compute rental is different — you're renting raw processing power, and the demand right now is AI inference and training. People building LLMs, running diffusion models, doing batch jobs.&lt;/p&gt;

&lt;p&gt;The upside: no mining pool setup, no daily coin price anxiety, no special software. Your machine runs Docker containers, gets paid per second of use, you get a payout.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Setup (Genuinely About 90 Minutes)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Created a Vast.ai account&lt;/li&gt;
&lt;li&gt;Installed the host daemon on Windows (it's a one-click installer)&lt;/li&gt;
&lt;li&gt;Set my RTX 3060 12GB at &lt;strong&gt;$0.15/hour&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Went to bed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. No configuration rabbit holes, no drivers to hunt down. The daemon manages everything — spinning up containers, cleaning up after renters, reporting uptime.&lt;/p&gt;

&lt;p&gt;I set the minimum rental duration to 1 hour so I wouldn't get hit with a dozen 5-minute jobs.&lt;/p&gt;




&lt;h2&gt;
  
  
  The First Week Numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Day&lt;/th&gt;
&lt;th&gt;Hours Rented&lt;/th&gt;
&lt;th&gt;Earnings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Day 1&lt;/td&gt;
&lt;td&gt;3.2h&lt;/td&gt;
&lt;td&gt;$0.48&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Day 2&lt;/td&gt;
&lt;td&gt;11.5h&lt;/td&gt;
&lt;td&gt;$1.73&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Day 3&lt;/td&gt;
&lt;td&gt;0h&lt;/td&gt;
&lt;td&gt;$0.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Day 4&lt;/td&gt;
&lt;td&gt;16.8h&lt;/td&gt;
&lt;td&gt;$2.52&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Day 5&lt;/td&gt;
&lt;td&gt;9.1h&lt;/td&gt;
&lt;td&gt;$1.37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Day 6&lt;/td&gt;
&lt;td&gt;22.0h&lt;/td&gt;
&lt;td&gt;$3.30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Day 7&lt;/td&gt;
&lt;td&gt;14.4h&lt;/td&gt;
&lt;td&gt;$2.16&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Week 1 total: ~$11.56&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Annualized naively? About $600/year. Which would be great except day 3 was $0 and utilization is inconsistent.&lt;/p&gt;

&lt;p&gt;A more realistic steady-state: &lt;strong&gt;$50–130/month&lt;/strong&gt; depending on demand.&lt;/p&gt;




&lt;h2&gt;
  
  
  What People Actually Rent It For
&lt;/h2&gt;

&lt;p&gt;Vast.ai shows you the jobs (anonymized). Mine has been used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running &lt;code&gt;vllm&lt;/code&gt; inference servers (Mistral, Qwen, LLaMA variants)&lt;/li&gt;
&lt;li&gt;Stable Diffusion batch jobs&lt;/li&gt;
&lt;li&gt;Some kind of PyTorch training run that lasted 8 hours&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The 3060 with 12GB VRAM is actually sweet for inference — fits most 7B–13B models at 4-bit quantization without breaking a sweat. It's not the fastest card, but it's affordable to rent, which means demand is there.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Honest Downsides
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;You can't use your GPU while it's rented.&lt;/strong&gt; Sounds obvious, but the practical implication: if you need your machine for local inference and someone's rented it, tough luck. I started routing heavy tasks to my Mac Mini during rental periods.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Electricity.&lt;/strong&gt; My RTX 3060 at load pulls about 150W. At Turkish electricity rates, that's roughly $8–15/month in power at typical utilization. So the net is lower than the gross numbers above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's genuinely passive but not predictable.&lt;/strong&gt; Day 3 was $0. Day 6 was near-full utilization. There's no way to forecast demand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Payouts have a minimum.&lt;/strong&gt; Vast.ai pays out once you hit a threshold. Nothing to worry about, just something to know going in.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'm Going to Try Next
&lt;/h2&gt;

&lt;p&gt;The obvious play is adding more GPUs. I have a few more in storage — an RTX 3080 and some older 3060s. If I rack those up, the math gets interesting:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GPU&lt;/th&gt;
&lt;th&gt;Rate&lt;/th&gt;
&lt;th&gt;Monthly (50% util)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RTX 3060 (current)&lt;/td&gt;
&lt;td&gt;$0.15/h&lt;/td&gt;
&lt;td&gt;~$54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RTX 3080 10GB&lt;/td&gt;
&lt;td&gt;$0.20/h&lt;/td&gt;
&lt;td&gt;~$72&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2x RTX 3070 8GB&lt;/td&gt;
&lt;td&gt;$0.16/h&lt;/td&gt;
&lt;td&gt;~$115&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's ~$240/month without doing anything after setup. At 70% utilization: ~$340.&lt;/p&gt;

&lt;p&gt;The real work is physical — pulling GPUs from storage, getting them into a rig, managing thermals. But the software side is almost zero maintenance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Should You Try This?
&lt;/h2&gt;

&lt;p&gt;If you have a spare GPU collecting dust: &lt;strong&gt;yes, probably.&lt;/strong&gt; The setup is low friction, the risk is near-zero (worst case, you uninstall the daemon and move on), and even modest earnings beat $0.&lt;/p&gt;

&lt;p&gt;If you're thinking about buying a GPU specifically for this: &lt;strong&gt;do the math carefully.&lt;/strong&gt; At current rates, an RTX 3060 costs ~$300–350 used. Payback period at $50/month is 6–7 months, which is fine — but don't expect to fund your retirement from a single card.&lt;/p&gt;

&lt;p&gt;The real value for me isn't the income (yet). It's that I now have a system running, I understand the demand patterns, and I know the path to scale looks viable.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Setup Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Platform: &lt;a href="https://vast.ai" rel="noopener noreferrer"&gt;Vast.ai&lt;/a&gt; (there's also RunPod if you want alternatives)&lt;/li&gt;
&lt;li&gt;Time to set up: ~90 minutes&lt;/li&gt;
&lt;li&gt;Technical skill required: Know how to install software on Windows&lt;/li&gt;
&lt;li&gt;Ongoing maintenance: Almost none&lt;/li&gt;
&lt;li&gt;Realistic earnings: $50–130/month per mid-tier GPU&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy to answer questions if you try this and run into something weird. The daemon is pretty solid but there's always an edge case or two.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I write about running AI locally, automation side projects, and occasionally making money from hardware that would otherwise just collect dust. If any of this is useful, feel free to follow.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>gpu</category>
      <category>passiveincome</category>
      <category>ai</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>Local LLMs vs Cloud APIs — A Real Cost Comparison (2026)</title>
      <dc:creator>Sam Hartley</dc:creator>
      <pubDate>Thu, 19 Mar 2026 08:03:20 +0000</pubDate>
      <link>https://forem.com/samhartley_dev/local-llms-vs-cloud-apis-a-real-cost-comparison-2026-2igh</link>
      <guid>https://forem.com/samhartley_dev/local-llms-vs-cloud-apis-a-real-cost-comparison-2026-2igh</guid>
      <description>&lt;p&gt;"Just use ChatGPT" — sure, until your API bill hits $500/month.&lt;/p&gt;

&lt;p&gt;I've been running both local and cloud AI for over a year. Here are the real numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Test Setup
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cloud:&lt;/strong&gt; OpenAI GPT-4o, Anthropic Claude Sonnet, Google Gemini Pro&lt;br&gt;
&lt;strong&gt;Local:&lt;/strong&gt; Ollama with Qwen 3.5 9B (Mac Mini M4) + Qwen 3 Coder 30B (RTX 3060)&lt;/p&gt;

&lt;p&gt;Workload: ~500 queries/day — code review, content generation, customer support, data analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monthly Cloud API Costs
&lt;/h2&gt;

&lt;p&gt;For 500 queries/day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI GPT-4o (200 queries): ~$90/month&lt;/li&gt;
&lt;li&gt;Anthropic Claude Sonnet (200 queries): ~$72/month&lt;/li&gt;
&lt;li&gt;Google Gemini Pro (100 queries): ~$25/month&lt;/li&gt;
&lt;li&gt;Total: ~$187/month&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Monthly Local Setup Costs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Mac Mini M4 (already owned): $0&lt;/li&gt;
&lt;li&gt;RTX 3060 12GB (used, eBay): $150 one-time&lt;/li&gt;
&lt;li&gt;Electricity 24/7: ~$12/month&lt;/li&gt;
&lt;li&gt;Total: ~$12/month ongoing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Break-even: less than 1 month.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quality Comparison (What Surprised Me)
&lt;/h2&gt;

&lt;p&gt;For 80% of daily tasks, local models are good enough:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;General chat: Qwen 3.5 9B is roughly GPT-4o quality (~90%)&lt;/li&gt;
&lt;li&gt;Code generation: Qwen 3 Coder 30B is close to Claude Sonnet (~85-90%)&lt;/li&gt;
&lt;li&gt;Simple Q&amp;amp;A and extraction: any 7B model matches cloud (~95%+)&lt;/li&gt;
&lt;li&gt;Complex multi-step reasoning: cloud still wins here&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Hybrid Approach I Use
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User query
  -&amp;gt; Simple? (Q&amp;amp;A, formatting, extraction)
       -&amp;gt; Local Qwen 3.5 9B  (free, instant)
  -&amp;gt; Code-heavy?
       -&amp;gt; Local Qwen 3 Coder 30B  (free, ~12s)
  -&amp;gt; Complex reasoning?
       -&amp;gt; Cloud Claude Sonnet  ($0.003-0.015 per query)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: cloud costs dropped from ~$187/month to ~$25/month.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hidden Costs of Cloud
&lt;/h2&gt;

&lt;p&gt;Things people forget:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rate limits — hit the ceiling during a deadline? Too bad.&lt;/li&gt;
&lt;li&gt;Latency — 500-2000ms per request vs 100-500ms local&lt;/li&gt;
&lt;li&gt;Privacy — your code and data live on someone else's server&lt;/li&gt;
&lt;li&gt;Vendor lock-in — OpenAI changes pricing, you're stuck&lt;/li&gt;
&lt;li&gt;Downtime — their outage = your workflow stops&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Hidden Costs of Local
&lt;/h2&gt;

&lt;p&gt;Being fair:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Initial hardware — $150-500 for a GPU (pays off in under a month)&lt;/li&gt;
&lt;li&gt;Setup time — 30 minutes with Ollama these days&lt;/li&gt;
&lt;li&gt;Storage — models are 4-40GB each&lt;/li&gt;
&lt;li&gt;Power — $10-15/month for 24/7 operation&lt;/li&gt;
&lt;li&gt;No frontier models — you won't run GPT-4 locally yet&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Getting Started in 10 Minutes
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install Ollama&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://ollama.com/install.sh | sh

&lt;span class="c"&gt;# Pull a model&lt;/span&gt;
ollama pull qwen3.5:9b

&lt;span class="c"&gt;# Start chatting&lt;/span&gt;
ollama run qwen3.5:9b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Total time: 10 minutes. Total cost: $0.&lt;/p&gt;




&lt;p&gt;Need help setting up a local AI server? I do this professionally.&lt;/p&gt;

&lt;p&gt;Follow along: &lt;a href="https://t.me/celebibot_en" rel="noopener noreferrer"&gt;Telegram @celebibot_en&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sam Hartley — building AI things that actually work.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>selfhosted</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
