<?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>DEV Community: Dustin VanKrimpen</title>
    <description>The latest articles on DEV Community by Dustin VanKrimpen (@dustinvk).</description>
    <link>https://dev.to/dustinvk</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4026197%2F1f9dd8d0-e6bf-4714-bf36-f227c6c2f3c8.jpg</url>
      <title>DEV Community: Dustin VanKrimpen</title>
      <link>https://dev.to/dustinvk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dustinvk"/>
    <language>en</language>
    <item>
      <title>I Gave My Coding Agent A Free Tier</title>
      <dc:creator>Dustin VanKrimpen</dc:creator>
      <pubDate>Sun, 12 Jul 2026 16:33:42 +0000</pubDate>
      <link>https://dev.to/dustinvk/i-gave-my-coding-agent-a-free-tier-59cg</link>
      <guid>https://dev.to/dustinvk/i-gave-my-coding-agent-a-free-tier-59cg</guid>
      <description>&lt;p&gt;&lt;em&gt;Note: There is a TL;DR section near the bottom for anyone who just wants the setup notes without the story.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;The world of AI coding is moving at lightspeed in 2026. Every week I learn about some cool new tooling that people are using to build software with AI assistance. It's an exciting time to be a software engineer, but an uncertain one as well. To satisfy my own curiosity (and to ensure my skills and knowledge stay relevant), I've been investing a lot of my personal time in experimenting with AI development. This is outside of my day-job, so I use my own personal licenses. The deeper I go, the more I run into usage limits.&lt;/p&gt;

&lt;p&gt;I like using the latest frontier class models for the hard stuff. I do not love spending their rate limits on "rename this variable" and "write the obvious boilerplate." So I decided to host a large language model (LLM) on my own hardware, free and off of rate limits. As an aside, I also like the idea of my data being kept local, and not shipped off to some major AI corporation on &lt;em&gt;every&lt;/em&gt; prompt. But the main motivation is to soak up the easy work with the local model and leave the frontier credits for the work that actually needs a frontier model.&lt;/p&gt;

&lt;p&gt;And that's what this post is about. The first part of a series where I will explore what it takes to host your own LLM, make it securely available from anywhere, evaluate its usefulness, and explore the cool things you can do with your own self-hosted AI model. Today's post is about step #1: setting up hardware to host an LLM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sourcing hardware during the RAMpocalypse
&lt;/h2&gt;

&lt;p&gt;2026 is a brutal market for PC components. There's a massive AI-driven shortage of DRAM and GDDR which has made prices skyrocket. As of this post, a 32 GB DDR4 kit that used to cost about $50-$70 is sitting at roughly $200. Rather than get fleeced in a seller's market, I decided to just use hardware that I had lying around.&lt;/p&gt;

&lt;p&gt;Enter my gaming PC. I built it back in 2020 during the pandemic restrictions. Back then I had all the free time in the world for gaming. It's mostly been collecting dust since my first son was born in 2022. The perfect candidate for my AI server.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Specs
&lt;/h3&gt;

&lt;p&gt;Originally the gaming PC had a GTX 1080 carried over from an even earlier build. Around 3 years ago I impulse bought a RX 6750 XT 12GB on a good sale and swapped it in. The RX 6750 XT is a fine mid-level card for 1080p and some 1440p gaming today. However, it is generally not a card that you'll see recommended for running LLMs locally.&lt;/p&gt;

&lt;p&gt;That said, it's not a bad card for it either (with some caveats I will get to later). For inference, the most important factor is VRAM, because that's what determines what size of model you can hold. The 12 GB in my RX 6750 XT is enough to hold a real coding model, with extra room for context. I didn't set out to choose AMD over NVIDIA here. It's just what I had.&lt;/p&gt;

&lt;p&gt;The rest of the box is nothing special. The CPU is a genuinely dated Ryzen 5 2600, but that's ok because it doesn't need to do much during GPU inference. It has 16 GB of DDR4 RAM. A bit tight since I also want to run orchestration processes on this machine, but it's fine given the sheer redonkulousness of RAM prices in 2026.&lt;/p&gt;

&lt;p&gt;I did a fresh install of Ubuntu 26.04 since it's free, lean, fits the tooling, and I plan on mostly using it headless via SSH anyway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full spec table for the curious:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What I Have&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Case&lt;/td&gt;
&lt;td&gt;Cooler Master NR200 (SFF, 18L)&lt;/td&gt;
&lt;td&gt;Fits triple-slot GPUs up to 330mm / 156mm; SFX PSU only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PSU&lt;/td&gt;
&lt;td&gt;Cooler Master V850 SFX Gold (850W, full-modular)&lt;/td&gt;
&lt;td&gt;Upgrade ready&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Board&lt;/td&gt;
&lt;td&gt;ASUS ROG Strix B450-I (Mini-ITX, AM4, DDR4, PCIe 3.0)&lt;/td&gt;
&lt;td&gt;AM4 tops out at Ryzen 5000, which would probably serve my use case just fine for a while&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU&lt;/td&gt;
&lt;td&gt;Ryzen 5 2600 (Zen+, 6c/12t, 2018)&lt;/td&gt;
&lt;td&gt;Adequate for now&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU&lt;/td&gt;
&lt;td&gt;Radeon RX 6750 XT 12GB (RDNA2, gfx1031)&lt;/td&gt;
&lt;td&gt;Fine for now, but I'm keeping an eye out for deals on something like an RX 7900 XTX or RTX 3090 to upgrade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAM&lt;/td&gt;
&lt;td&gt;16 GB DDR4&lt;/td&gt;
&lt;td&gt;Will upgrade when RAM prices come down&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;1 x 500 GB NVMe &amp;amp; 1 x 4 TB NVMe&lt;/td&gt;
&lt;td&gt;Lucky to have bought before the price increases. More than enough for my use&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The unsupported card problem
&lt;/h2&gt;

&lt;p&gt;Here is where the caveat of my GPU comes into play. AMD's compute stack is called ROCm. This is their answer to NVIDIA's CUDA. There is a hardcoded list of supported GPU targets for ROCm, and your card has to be on it. My RX 6750 XT reports its chip target as &lt;code&gt;gfx1031&lt;/code&gt;, which is not supported. The very similar chip in the 6800 and 6900 series cards is &lt;code&gt;gfx1030&lt;/code&gt;, and that one is supported. &lt;code&gt;gfx1031&lt;/code&gt;, the 6700 series, is not. Same architecture, one digit off, left off the list.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br&gt;
ROCm's official supported-GPU list changes between releases, and gfx1031 support has been a moving target. Best to check for yourself since things may have changed since the time of this posting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The usual workaround
&lt;/h3&gt;

&lt;p&gt;There is a very simple workaround that a lot of people have reported success with on Linux. It basically tricks ROCm into thinking your card is supported. You install the GPU drivers and ROCm runtime as usual, add your user account to the video and render groups, and then spoof the GFX version to &lt;code&gt;10.3.0&lt;/code&gt; like so:&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HSA_OVERRIDE_GFX_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10.3.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Vulkan: the alternative
&lt;/h3&gt;

&lt;p&gt;Vulkan is a graphics and compute API that these cards support natively with no ROCm needed. I am using Ollama to serve models, which runs inference on the Vulkan backend no problem. The main downside is it's inference-only, meaning you can't fine-tune or train models. Since I'm only trying to serve an existing model for now, it's a non-issue for me.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; It's not that Vulkan is inherently incapable of training/tuning models. It's just that the typical training stack targets CUDA/ROCm.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Comparison: ROCm workaround vs. Vulkan (RX 6750 XT / gfx1031)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;ROCm (via &lt;code&gt;HSA_OVERRIDE_GFX_VERSION=10.3.0&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;Vulkan&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Setup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requires the override env var to spoof GPU target&lt;/td&gt;
&lt;td&gt;Natively supported (no workaround)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reliability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fragile: can fail to init (known "Tensile host" error); kernel/ROCm updates can silently break it&lt;/td&gt;
&lt;td&gt;Stable and low-maintenance; part of Mesa, survives kernel updates, set-and-forget&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Legitimacy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Unsanctioned spoof that's untested by AMD on this chip&lt;/td&gt;
&lt;td&gt;Uses the card as itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inference speed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Historically faster, but the gap has narrowed. Recent benchmarks range from ROCm ahead ~10–20% (mostly on prompt processing) to Vulkan matching or beating it&lt;/td&gt;
&lt;td&gt;Very competitive on RDNA2 specifically. The backend was first developed on an RDNA2 card, so token-generation speed is often on par with ROCm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fine-tuning / training&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Supported. ROCm is the ML-native path (PyTorch-ROCm, vLLM, training libs)&lt;/td&gt;
&lt;td&gt;Unsupported. Inference-only; cannot fine-tune or run PyTorch training&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tooling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Richer (&lt;code&gt;rocm-smi&lt;/code&gt; for monitoring/management)&lt;/td&gt;
&lt;td&gt;Lighter (&lt;code&gt;radeontop&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Footprint&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Large stack to install and keep working&lt;/td&gt;
&lt;td&gt;Small, often already present&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Verdict
&lt;/h3&gt;

&lt;p&gt;For my use case (mostly-headless inference box), Vulkan wins. And I'm not giving up much for it. The old assumption that ROCm is meaningfully faster has largely eroded, and on RDNA2 in particular (the generation the Vulkan backend was first tuned on) the two are close enough that speed isn't the deciding factor.&lt;/p&gt;

&lt;p&gt;The goal of this project is to make AI-assisted workflows easier for me. I want to spend my time building cool things, not debugging why the latest update broke my hacky workaround. Vulkan just works, and keeps working across driver updates, which is what I want for an always-on server.&lt;/p&gt;

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

&lt;p&gt;I started with a clean slate. A fresh install of Ubuntu 26.04 LTS Desktop. I went with desktop because I do have a monitor attached (set up at a desk in my basement), and will occasionally work from it directly using IDEs and such. But most of the time it will be connected to and used headless via my MacBook Air.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: The Vulkan graphics stack
&lt;/h3&gt;

&lt;p&gt;Update the system and install the Vulkan userspace drivers and a couple of&lt;br&gt;
diagnostic tools:&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="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt full-upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; mesa-vulkan-drivers vulkan-tools radeontop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm the card is visible to Vulkan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vulkaninfo | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; deviceName
&lt;span class="c"&gt;# should print "AMD Radeon RX 6750 XT" (mine shows RADV NAVI22)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;radeontop&lt;/code&gt; is the tool we'll use later to prove the GPU is actually doing the&lt;br&gt;
math, so it's worth installing now.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2: Install Ollama and pull a model
&lt;/h3&gt;

&lt;p&gt;Ollama is the easiest way to serve a local model. Install it with 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;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://ollama.com/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On this hardware Ollama installs as a systemd service. During the first run, it&lt;br&gt;
resolves the Vulkan backend, drops the unsupported ROCm device, and&lt;br&gt;
loads onto the discrete RX 6750 XT with ~11.6 GiB of usable VRAM.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; When Ollama starts, it will show a warning that it dropped the ROCm device. This is expected on a &lt;code&gt;gfx1031&lt;/code&gt; card. It realized that the GPU doesn't support ROCm and fell back to Vulkan. That's what you want.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  Picking a model for 12GB of VRAM
&lt;/h4&gt;

&lt;p&gt;Now that Ollama is set up, it's time to choose a model for it to serve. This is where the VRAM budgeting gets real.&lt;/p&gt;

&lt;p&gt;My VRAM limitation rules out the current flagships: qwen3-coder:30b (~19GB), Codestral 22B (~13GB), and anything 24B+ dense. What's left is still a solid field of 7–16B coders.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Params&lt;/th&gt;
&lt;th&gt;~Q4 size&lt;/th&gt;
&lt;th&gt;Context&lt;/th&gt;
&lt;th&gt;Trade-off&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;qwen2.5-coder:14b&lt;/strong&gt; &lt;em&gt;(chosen)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;14.8B dense&lt;/td&gt;
&lt;td&gt;~9GB&lt;/td&gt;
&lt;td&gt;32K&lt;/td&gt;
&lt;td&gt;Best dense code quality in the tier; native tool-calling + fill-in-the-middle. Non-reasoning, so it's fast.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen2.5-coder:7b&lt;/td&gt;
&lt;td&gt;7B dense&lt;/td&gt;
&lt;td&gt;~4.7GB&lt;/td&gt;
&lt;td&gt;32K&lt;/td&gt;
&lt;td&gt;Same family, more headroom/speed, lower ceiling.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen3:14b&lt;/td&gt;
&lt;td&gt;14.8B dense&lt;/td&gt;
&lt;td&gt;~9GB&lt;/td&gt;
&lt;td&gt;32K+&lt;/td&gt;
&lt;td&gt;Strong generalist that codes well, but not code-specialized.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;deepseek-coder-v2:16b&lt;/td&gt;
&lt;td&gt;16B MoE (2.4B active)&lt;/td&gt;
&lt;td&gt;~8.9GB&lt;/td&gt;
&lt;td&gt;160K&lt;/td&gt;
&lt;td&gt;Fastest option (MoE) and by far the largest context. Weaker at multi-step tool use.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;deepseek-r1-distill-qwen:14b&lt;/td&gt;
&lt;td&gt;14B dense (reasoning)&lt;/td&gt;
&lt;td&gt;~9GB&lt;/td&gt;
&lt;td&gt;32K&lt;/td&gt;
&lt;td&gt;Thinks step-by-step. Great for hard debugging, slower for everyday generation.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I went with &lt;strong&gt;qwen2.5-coder:14b&lt;/strong&gt; because this box is intended to be the &lt;em&gt;worker tier&lt;/em&gt; of a two-tier setup: easy tasks run locally, hard ones escalate to externally hosted models. I wanted the fastest, most reliable, tool-capable code specialist that fits. If I need a higher-reasoning model, I will use an externally hosted 'frontier-class' model.&lt;/p&gt;

&lt;p&gt;The closest competitor for my use was &lt;code&gt;deepseek-coder-v2:16b&lt;/code&gt;. I may yet try it out, and benchmark it just to see how it compares. We'll see.&lt;/p&gt;
&lt;h4&gt;
  
  
  Pulling
&lt;/h4&gt;

&lt;p&gt;With the model chosen, pulling it is 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;ollama pull qwen2.5-coder:14b-instruct-q4_K_M
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quick smoke test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run qwen2.5-coder:14b-instruct-q4_K_M &lt;span class="s2"&gt;"write a python function that reverses a string"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it answers, the model runs. However, that does &lt;strong&gt;not&lt;/strong&gt; prove it's using the&lt;br&gt;
GPU. That part is next.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3: Prove it's actually on the GPU (not silently on the CPU)
&lt;/h3&gt;

&lt;p&gt;It's possible for Ollama to discover your GPU, load the weights into VRAM, and still run the compute on CPU.&lt;br&gt;
The logs will look normal, VRAM will show occupied, and the tokens will trickle while your CPU melts.&lt;/p&gt;

&lt;p&gt;To verify the GPU compute, I used &lt;code&gt;radeontop&lt;/code&gt;. Open it in one terminal like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;radeontop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, in a second terminal, fire off a prompt long enough to sustain load for several seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run qwen2.5-coder:14b-instruct-q4_K_M &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"write a detailed, heavily commented python implementation of a red-black tree with insert, delete, and search"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;A tmux tip: split the terminal (&lt;code&gt;Ctrl-b %&lt;/code&gt;), run &lt;code&gt;radeontop&lt;/code&gt; on the left and&lt;br&gt;
your &lt;code&gt;ollama run&lt;/code&gt; on the right, and you can literally watch the GPU spike as it&lt;br&gt;
"thinks." It's oddly satisfying.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now watch &lt;code&gt;radeontop&lt;/code&gt; while tokens are streaming. What you're looking for:&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;Idle (between generations)&lt;/th&gt;
&lt;th&gt;Active (mid-generation)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Graphics pipe&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~1–2%&lt;/td&gt;
&lt;td&gt;jumps high (&lt;strong&gt;~93%&lt;/strong&gt; on mine)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shader Clock&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~0.4%&lt;/td&gt;
&lt;td&gt;climbs toward max (&lt;strong&gt;~90%&lt;/strong&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;VRAM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~10.3 / 12.2 GB&lt;/td&gt;
&lt;td&gt;same (model stays resident)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The signal is the &lt;strong&gt;transition&lt;/strong&gt;: idle → active → idle, synced to token output.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Watch Graphics pipe and Shader Clock, not VRAM. The weights can be in VRAM while the math runs on the CPU. You have to watch during streaming. The compute drops to zero as soon as generation ends.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On the VRAM numbers:&lt;/strong&gt; Ollama reports usable VRAM in &lt;strong&gt;GiB&lt;/strong&gt; (it logged ~11.6 GiB available), while &lt;code&gt;radeontop&lt;/code&gt; reads out in &lt;strong&gt;GB&lt;/strong&gt; using its own accounting. The two don't line up exactly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On my box, Graphics pipe hit ~93% and Shader Clock ~90% during generation, then fell to 0% and ~0.11% after. That confirms GPU inference&lt;br&gt;
over Vulkan. If your compute stays flat while tokens are streaming, that indicates CPU fallback, so you'll need to do some debugging.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F34u1n84jorbejynbo3sm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F34u1n84jorbejynbo3sm.png" alt="GPU Compute Validated" width="800" height="718"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 4: The context-window decision (8K, on purpose)
&lt;/h3&gt;

&lt;p&gt;Ollama picks a default context length for you, but I wanted to set it explicitly&lt;br&gt;
because context costs VRAM. A bigger context window means a bigger KV cache&lt;br&gt;
sitting in memory alongside the weights. On a 12 GB card, that budget is tight, so&lt;br&gt;
I capped the default served context at 8K, which gives ~10.3 GB resident&lt;br&gt;
with headroom to spare (even though Qwen2.5-Coder's native context is 32K).&lt;/p&gt;

&lt;p&gt;I set it with a systemd drop-in, so I'm not editing Ollama's packaged unit file:&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="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/systemd/system/ollama.service.d
&lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/systemd/system/ollama.service.d/override.conf &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
[Service]
Environment="OLLAMA_CONTEXT_LENGTH=8192"
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart ollama
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify it took:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl show ollama &lt;span class="nt"&gt;-p&lt;/span&gt; Environment &lt;span class="nt"&gt;--no-pager&lt;/span&gt;   &lt;span class="c"&gt;# echoes OLLAMA_CONTEXT_LENGTH=8192&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;8K is a fine default for most coding prompts. If a specific task needs&lt;br&gt;
more, you don't have to change the service. You can override context per&lt;br&gt;
request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:11434/api/generate &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
  "model": "qwen2.5-coder:14b-instruct-q4_K_M",
  "prompt": "...",
  "stream": false,
  "options": { "num_ctx": 16384, "temperature": 0.2 }
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Raise &lt;code&gt;num_ctx&lt;/code&gt; for the one long-context call that needs it. Don't leave it high&lt;br&gt;
globally or you'll pay the VRAM tax on every request.&lt;/p&gt;

&lt;p&gt;A couple of other handy per-request knobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;temperature&lt;/code&gt; 0.1–0.3 for deterministic code, higher for brainstorming&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;keep_alive&lt;/code&gt; pass &lt;code&gt;-1&lt;/code&gt; to pin the model resident when you're about to hammer it (by default it unloads after 5 idle minutes and the next request eats a few-second reload).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ollama also exposes an OpenAI-compatible API at &lt;code&gt;http://localhost:11434/v1&lt;/code&gt;. &lt;br&gt;
Any tool compatible with "OpenAI API, custom base&lt;br&gt;
URL" can point at it locally with a dummy key. The server is stateless. It takes&lt;br&gt;
the full message history on every call, so your client holds the conversation.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 5: zram, and a packaging gotcha
&lt;/h3&gt;

&lt;p&gt;This box has 16 GB of RAM, and the DDR4 shortage of 2026 makes adding more&lt;br&gt;
expensive. The model itself lives in &lt;strong&gt;VRAM&lt;/strong&gt;, so system RAM isn't the hot path,&lt;br&gt;
but the OS plus whatever I stack on top can spike. I'm planning on setting up some&lt;br&gt;
orchestration and automation for unattended workflows, so &lt;strong&gt;zram&lt;/strong&gt; is cheap insurance.&lt;/p&gt;

&lt;p&gt;If you don't know, &lt;strong&gt;zram&lt;/strong&gt; is a compressed swap device that lives in RAM, giving you meaningfully more&lt;br&gt;
usable memory at a small CPU cost.&lt;/p&gt;

&lt;p&gt;On Ubuntu 26.04 the right tool is &lt;code&gt;systemd-zram-generator&lt;/code&gt; (not the older&lt;br&gt;
&lt;code&gt;zram-tools&lt;/code&gt; / &lt;code&gt;zram-config&lt;/code&gt; packages, which use different config paths and will&lt;br&gt;
just confuse you):&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="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; systemd-zram-generator

&lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/systemd/zram-generator.conf &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
[zram0]
zram-size = min(ram, 8192)
compression-algorithm = zstd
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/sysctl.d/99-zram.conf &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
vm.swappiness = 180
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;--system&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;&lt;code&gt;zram-size = min(ram, 8192)&lt;/code&gt; is evaluated in MiB, so on a&lt;br&gt;
16 GB machine that's an &lt;strong&gt;8 GB&lt;/strong&gt; compressed device.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;vm.swappiness = 180&lt;/code&gt; looks crazy if you remember the old "100 is the max" rule,&lt;br&gt;
but kernels since 5.8 cap at 200. Since zram swap is RAM-fast, biasing the kernel&lt;br&gt;
toward evicting pages into compressed RAM is cheap and frees space for page cache.&lt;br&gt;
150 is a good option too. The difference is noise.&lt;/p&gt;
&lt;h4&gt;
  
  
  The zram gotcha
&lt;/h4&gt;

&lt;p&gt;Here's what confused me. The package ships a vendor default that activates a&lt;br&gt;
4 GB / lzo-rle device at install time, &lt;em&gt;before&lt;/em&gt; my config file existed. A&lt;br&gt;
plain &lt;code&gt;systemctl start&lt;/code&gt; on an already-active zram device does nothing. It keeps&lt;br&gt;
the install-time parameters and silently ignores your config.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; you set up 8 GB / zstd, run &lt;code&gt;zramctl&lt;/code&gt;, and see &lt;code&gt;4G&lt;/code&gt; / &lt;code&gt;lzo-rle&lt;/code&gt;&lt;br&gt;
instead. Your config appears to do nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; use &lt;code&gt;restart&lt;/code&gt;, not &lt;code&gt;start&lt;/code&gt;. It tears the device down and rebuilds it&lt;br&gt;
from your &lt;code&gt;/etc&lt;/code&gt; config:&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="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart systemd-zram-setup@zram0.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the end state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zramctl            &lt;span class="c"&gt;# want: /dev/zram0  zstd  8G  [SWAP]&lt;/span&gt;
swapon &lt;span class="nt"&gt;--show&lt;/span&gt;      &lt;span class="c"&gt;# zram0 at priority 100; disk swapfile as the low-priority tail&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/sys/vm/swappiness   &lt;span class="c"&gt;# 180&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One more caveat: zram is not an OOM guarantee. A hard enough runaway process&lt;br&gt;
blows past RAM + zram and still hits the OOM killer. The on-disk swapfile&lt;br&gt;
(priority -1) is the tail that catches overflow; zram (priority 100) fills first.&lt;br&gt;
For a box that's going to run unattended workloads, the layering is worth having.&lt;/p&gt;

&lt;h2&gt;
  
  
  The end result
&lt;/h2&gt;

&lt;p&gt;I ended up with a machine that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;runs &lt;strong&gt;Qwen2.5-Coder 14B&lt;/strong&gt; on a 12 GB AMD GPU over &lt;strong&gt;Vulkan&lt;/strong&gt; (no ROCm, no
override hacks), with GPU compute &lt;em&gt;verified&lt;/em&gt;, not assumed&lt;/li&gt;
&lt;li&gt;serves it on both the native Ollama API and an &lt;strong&gt;OpenAI-compatible endpoint&lt;/strong&gt;,
so any local tool or editor plugin can point at it&lt;/li&gt;
&lt;li&gt;caps context at a &lt;strong&gt;VRAM-sane 8K&lt;/strong&gt; by default with per-request overrides for the
rare long task&lt;/li&gt;
&lt;li&gt;has &lt;strong&gt;zram&lt;/strong&gt; as a RAM-pressure cushion for whatever I stack on top&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the whole thing runs on hardware I already owned, for the cost of electricity.&lt;/p&gt;

&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;p&gt;The cliffnotes on setting up your own self-hosted LLM server:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install an LTS Linux&lt;/strong&gt; (Ubuntu 24.04/26.04) with a recent kernel for your GPU.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check your GPU's compute support.&lt;/strong&gt; AMD: is your &lt;code&gt;gfx&lt;/code&gt; target on ROCm's list?
If not (e.g. RX 6700-series gfx1031), plan on &lt;strong&gt;Vulkan&lt;/strong&gt;:
&lt;code&gt;sudo apt install mesa-vulkan-drivers vulkan-tools radeontop&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install Ollama:&lt;/strong&gt; &lt;code&gt;curl -fsSL https://ollama.com/install.sh | sh&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pull a model that fits your VRAM.&lt;/strong&gt; 12 GB → a 14B at Q4_K_M (~10 GB). Don't
try to cram a 30B into 12 GB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify GPU compute&lt;/strong&gt; with &lt;code&gt;radeontop&lt;/code&gt; &lt;em&gt;during&lt;/em&gt; generation. Watch Graphics
pipe / Shader Clock, not VRAM. Discovery ≠ compute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set the context ceiling&lt;/strong&gt; with a systemd drop-in
(&lt;code&gt;OLLAMA_CONTEXT_LENGTH=8192&lt;/code&gt;) to keep the KV cache inside your VRAM budget;
override per request with &lt;code&gt;num_ctx&lt;/code&gt; when needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up zram&lt;/strong&gt; with &lt;code&gt;systemd-zram-generator&lt;/code&gt;, and remember to &lt;strong&gt;&lt;code&gt;restart&lt;/code&gt;&lt;/strong&gt;
(not &lt;code&gt;start&lt;/code&gt;) the setup service so your config isn't shadowed by the vendor
default.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;Now I have my very own local-tier coding model, running for free (minus the cost of electricity) on a GPU that I had lying around in a computer that lives in my basement.&lt;/p&gt;

&lt;p&gt;What I am building towards is a coding agent that can do the easy work locally for free, allowing me to save my frontier model usage limits for harder work. Eventually, I would like to have a fully automated workflow that I can leave unattended (with guardrails in place) while I am doing other things. This box is the first step in reaching that.&lt;/p&gt;

&lt;p&gt;There are a few more tasks that need to be done, with more posts to come documenting my process and results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reaching it from anywhere, privately
&lt;/h3&gt;

&lt;p&gt;This post covers setting up a local AI server that only serves on localhost. Next I will cover getting to it from my laptop or my phone, from anywhere, over an encrypted mesh network with nothing exposed to the public internet. That's the next post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is it actually any good? The uncomfortable question.
&lt;/h3&gt;

&lt;p&gt;Can I really trust a quantized 14B running on a consumer card with real work? I put it through the standard coding benchmarks, and the results were good, but surprising, and also alarming in a way that turned out to be the benchmark's fault, not the model's. I am working on a full write-up, and may even try out some other models for comparison.&lt;/p&gt;

&lt;h3&gt;
  
  
  The agent loop itself
&lt;/h3&gt;

&lt;p&gt;The whole point of doing all this. I'm still experimenting with the many tools available for orchestrating multiple models, and running feedback loops so they can keep running even when I am not attending to them. Maybe I'll even end up writing some scripts of my own to do this. There are a few side projects that I have in the works to test everything out with, so expect future write-ups on what I discover.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final notes
&lt;/h2&gt;

&lt;p&gt;For now, I am happy to have my own free model running on a mid-level card that I didn't have to pay 2026 prices for. You don't need the latest, most expensive hardware to run a real and useful model at home. All you need is something with enough VRAM. It doesn't even have to be on AMD's supported list.&lt;/p&gt;

&lt;p&gt;Go and check out what your idle GPU could be doing instead.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>ollama</category>
      <category>homelab</category>
      <category>llm</category>
    </item>
  </channel>
</rss>
