<?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: Mr Say Nothing</title>
    <description>The latest articles on DEV Community by Mr Say Nothing (@mrsaynothing).</description>
    <link>https://dev.to/mrsaynothing</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%2F4123109%2F6e5b6d8a-fa72-4c7e-9e79-baecc60d2ee7.png</url>
      <title>DEV Community: Mr Say Nothing</title>
      <link>https://dev.to/mrsaynothing</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mrsaynothing"/>
    <language>en</language>
    <item>
      <title>Systemd Service Not Starting? How to Fix It</title>
      <dc:creator>Mr Say Nothing</dc:creator>
      <pubDate>Mon, 14 Sep 2026 16:49:12 +0000</pubDate>
      <link>https://dev.to/mrsaynothing/systemd-service-not-starting-how-to-fix-it-520a</link>
      <guid>https://dev.to/mrsaynothing/systemd-service-not-starting-how-to-fix-it-520a</guid>
      <description>&lt;p&gt;A systemd service that won't start is almost never mysterious. Run &lt;code&gt;systemctl status &amp;lt;unit&amp;gt;&lt;/code&gt;, then read the last 50 journal lines for that unit with &lt;code&gt;journalctl -u &amp;lt;unit&amp;gt; -n 50 --no-pager&lt;/code&gt; — between the two, one of five causes is usually named outright: a bad path, a missing binary, wrong permissions, an SELinux/AppArmor denial, or a unit file syntax error. The failure reason is in the log; the fixes below are just pattern matching against it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I see why a systemd service failed?
&lt;/h2&gt;

&lt;p&gt;Status first, journal second:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl status myapp.service
journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; myapp.service &lt;span class="nt"&gt;-n&lt;/span&gt; 50 &lt;span class="nt"&gt;--no-pager&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;status&lt;/code&gt; gives you the state (&lt;code&gt;inactive (dead)&lt;/code&gt;, &lt;code&gt;failed (exit-code)&lt;/code&gt;, &lt;code&gt;activating (auto-restart)&lt;/code&gt;) and the last few log lines. The journal gives you the full story: stdout, stderr, and systemd's own complaints about the unit.&lt;/p&gt;

&lt;p&gt;If the unit failed before and you want the record of &lt;em&gt;that&lt;/em&gt; run, add &lt;code&gt;-b&lt;/code&gt; for the current boot or &lt;code&gt;--since today&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; myapp.service &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="nt"&gt;--no-pager&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the full toolkit — boots, priorities, following output live — see the &lt;a href="https://dev.to/en/blog/2026-09-02/journalctl-cheat-sheet"&gt;journalctl cheat sheet&lt;/a&gt;. It's the same muscle memory.&lt;/p&gt;

&lt;p&gt;One more pair worth knowing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl list-units &lt;span class="nt"&gt;--failed&lt;/span&gt;
systemctl reset-failed myapp.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first lists every red unit on the box. The second clears the failed state after you've fixed it — cosmetic, but it stops status pages from crying wolf.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the most common causes?
&lt;/h2&gt;

&lt;p&gt;After the log names the symptom, the cause is almost always one of these five:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Log says&lt;/th&gt;
&lt;th&gt;Likely cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;status=203/EXEC&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wrong &lt;code&gt;ExecStart=&lt;/code&gt; path or missing interpreter&lt;/td&gt;
&lt;td&gt;Absolute path, &lt;code&gt;chmod +x&lt;/code&gt;, check shebang&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;status=203/EXEC&lt;/code&gt; on a script&lt;/td&gt;
&lt;td&gt;Script has CRLF line endings or bad shebang&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;dos2unix script.sh&lt;/code&gt;, fix first line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;status=1/FAILURE&lt;/code&gt;, no app log&lt;/td&gt;
&lt;td&gt;Working directory or env var missing&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;WorkingDirectory=&lt;/code&gt;, add &lt;code&gt;Environment=&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Permission denied&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;User can't read files or bind the port&lt;/td&gt;
&lt;td&gt;Fix ownership; ports below 1024 need &lt;code&gt;AmbientCapabilities=CAP_NET_BIND_SERVICE&lt;/code&gt; or root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Unit is masked&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Someone ran &lt;code&gt;systemctl mask&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;systemctl unmask myapp.service&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unit file edit "does nothing"&lt;/td&gt;
&lt;td&gt;Daemon not reloaded&lt;/td&gt;
&lt;td&gt;&lt;code&gt;systemctl daemon-reload&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;203/EXEC&lt;/code&gt; family deserves a special mention because it's the one that eats afternoons. Systemd does not use your shell to launch &lt;code&gt;ExecStart=&lt;/code&gt;. That means:&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="c"&gt;# Wrong — no shell, ~ never expands, no PATH lookup
&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;~/app/run.sh&lt;/span&gt;

&lt;span class="c"&gt;# Right
&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;/opt/app/run.sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the script itself must be executable and start with a real shebang (&lt;code&gt;#!/bin/bash&lt;/code&gt; or &lt;code&gt;#!/usr/bin/env bash&lt;/code&gt;). A script that runs fine from your terminal but fails with 203 under systemd is nearly always one of: not executable, CRLF endings, a shebang pointing nowhere, or a relative path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does it start manually but not on boot?
&lt;/h2&gt;

&lt;p&gt;The classic ordering bug. If the log shows failures right after boot but the unit starts fine when you &lt;code&gt;systemctl start&lt;/code&gt; it by hand, your service is losing a race — it's reaching for the network, a mounted disk, or a database before that thing exists.&lt;/p&gt;

&lt;p&gt;The fix is to declare dependencies instead of hoping:&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;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target postgresql.service&lt;/span&gt;
&lt;span class="py"&gt;Wants&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;ExecStartPre&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/test -f /opt/app/config.toml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;After=&lt;/code&gt; orders the start; &lt;code&gt;Wants=&lt;/code&gt; makes systemd actually bring the dependency up. &lt;code&gt;network-online.target&lt;/code&gt; only works if the network-wait service is enabled on your distro, so check &lt;code&gt;systemctl is-enabled NetworkManager-wait-online.service&lt;/code&gt; (or the systemd-networkd equivalent). The &lt;code&gt;ExecStartPre=&lt;/code&gt; guard is a cheap, honest way to fail loudly with a readable message instead of a stack trace.&lt;/p&gt;

&lt;p&gt;A second variant: the service starts on boot but immediately dies. Look for things your interactive environment had that boot doesn't — &lt;code&gt;PATH&lt;/code&gt; differences, a virtualenv, a &lt;code&gt;HOME&lt;/code&gt;. Set what you need explicitly:&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;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"PATH=/opt/app/venv/bin:/usr/bin"&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;appuser&lt;/span&gt;
&lt;span class="py"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/opt/app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why is my service not logging to the journal?
&lt;/h2&gt;

&lt;p&gt;If &lt;code&gt;journalctl -u&lt;/code&gt; shows nothing, check three things in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;StandardOutput=&lt;/code&gt; and &lt;code&gt;StandardError=&lt;/code&gt; in the unit — they must be &lt;code&gt;journal&lt;/code&gt; (the default) or &lt;code&gt;journal+console&lt;/code&gt;. Someone may have set them to &lt;code&gt;null&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The app writes to a file instead of stdout. Systemd only captures stdout/stderr; log-file writers bypass the journal entirely. Either point the app at stdout or read the file.&lt;/li&gt;
&lt;li&gt;Storage limits dropped old lines: &lt;code&gt;journalctl --disk-usage&lt;/code&gt;, and &lt;code&gt;SystemMaxUse=&lt;/code&gt; in &lt;code&gt;/etc/systemd/journald.conf&lt;/code&gt; if the journal is choking.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For debugging the start itself, nothing beats dropping a shell into the boot-time context:&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;[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;/bin/bash -c 'exec /opt/app/bin/server 2&amp;gt;&amp;amp;1'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, for genuine mysteries, run the exact &lt;code&gt;ExecStart=&lt;/code&gt; command as the unit's user in a shell — most environment differences surface in the first ten seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I make it restart automatically after a crash?
&lt;/h2&gt;

&lt;p&gt;Default policy is &lt;code&gt;Restart=no&lt;/code&gt;: a crashed service stays dead, and you find out from a user. Fix that per 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;[Service]&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;on-failure&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Restarts when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;no&lt;/code&gt; (default)&lt;/td&gt;
&lt;td&gt;Never&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;on-failure&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Non-zero exit, signal, timeout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;always&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any exit, even clean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;on-watchdog&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Watchdog timeout only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pair &lt;code&gt;Restart=on-failure&lt;/code&gt; with a start-rate guard so a crashing loop doesn't hammer the box: &lt;code&gt;StartLimitIntervalSec=&lt;/code&gt; and &lt;code&gt;StartLimitBurst=&lt;/code&gt; in the &lt;code&gt;[Unit]&lt;/code&gt; section. Five failures in sixty seconds should page a human, not spin a CPU.&lt;/p&gt;

&lt;p&gt;If you're wiring this up as a scheduled job rather than a daemon, weigh &lt;a href="https://dev.to/en/blog/2026-09-11/cron-vs-systemd-timer"&gt;cron vs systemd timer&lt;/a&gt; first — timers give you journal logging and dependency ordering for free, which is exactly what this article keeps reaching for.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 60-second checklist
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;systemctl status &amp;lt;unit&amp;gt;&lt;/code&gt; — read the state and last lines.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;journalctl -u &amp;lt;unit&amp;gt; -n 50 --no-pager&lt;/code&gt; — find the real error.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;203/EXEC&lt;/code&gt;? Fix path, shebang, permissions. &lt;code&gt;Permission denied&lt;/code&gt;? Fix user and file ownership.&lt;/li&gt;
&lt;li&gt;Fails only at boot? Add &lt;code&gt;After=&lt;/code&gt;/&lt;code&gt;Wants=network-online.target&lt;/code&gt; and an &lt;code&gt;ExecStartPre=&lt;/code&gt; guard.&lt;/li&gt;
&lt;li&gt;Change a unit file? &lt;code&gt;systemctl daemon-reload &amp;amp;&amp;amp; systemctl restart &amp;lt;unit&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;Restart=on-failure&lt;/code&gt; so the next crash announces itself instead of hiding.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most "systemd is complicated" moments reduce to &lt;em&gt;the error was in the journal the whole time&lt;/em&gt;. Read it before you edit the unit file, not after.&lt;/p&gt;

</description>
      <category>systemd</category>
      <category>linux</category>
      <category>journalctl</category>
      <category>troubleshooting</category>
    </item>
    <item>
      <title>GGUF Quantization: Which Level Should You Use?</title>
      <dc:creator>Mr Say Nothing</dc:creator>
      <pubDate>Sun, 13 Sep 2026 16:39:46 +0000</pubDate>
      <link>https://dev.to/mrsaynothing/gguf-quantization-which-level-should-you-use-3lbe</link>
      <guid>https://dev.to/mrsaynothing/gguf-quantization-which-level-should-you-use-3lbe</guid>
      <description>&lt;p&gt;&lt;strong&gt;Pick Q4_K_M by default; go Q6_K or Q8_0 when you have VRAM to spare and need the last few percent of quality.&lt;/strong&gt; GGUF quantization shrinks a model's weights from 16 bits to fewer — Q4_K_M stores roughly 4.85 bits per weight, so a 7B model drops from ~14 GB to ~4.1 GB with perplexity typically less than 1% worse than the original. The question "which gguf quantization to use" has a boringly stable answer that most of the drama online obscures. Below: what quantization actually does to weights, how much quality each level costs, how to do the size math yourself, and one command to measure the damage on your own hardware instead of trusting a stranger's benchmark.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does GGUF quantization actually do?
&lt;/h2&gt;

&lt;p&gt;A model is trained in 16-bit floating point (FP16 or BF16): every one of its billions of weights is a 2-byte number. Quantization compresses each weight into fewer bits. The naive way — round every weight to a 4-bit integer — destroys small-but-important values, so GGUF uses two tricks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Block scaling.&lt;/strong&gt; Weights are grouped into blocks (usually 32), and each block gets its own scale factor. The 4-bit values are offsets within that block, so a wide range of magnitudes survives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Importance-aware k-quants.&lt;/strong&gt; The "K" in Q4_K_M means super-blocks of scales, plus treating attention and feed-forward layers differently from each other, because they tolerate compression unequally.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The "I" family (IQ4_XS and friends) goes further with information-theoretic codebooks borrowed from image compression. Same idea, fancier encoding: fewer bits per weight at similar quality, at the cost of slightly slower inference on some backends.&lt;/p&gt;

&lt;p&gt;One clarification that prevents most confusion: quantization changes &lt;strong&gt;only the stored weights&lt;/strong&gt;. Architecture, tokenizer, and context handling are untouched. A Q4 file and a Q8 file of the same model are the same model, wearing different coats.&lt;/p&gt;

&lt;h2&gt;
  
  
  Q4 vs Q8: is higher quantization better?
&lt;/h2&gt;

&lt;p&gt;Yes, technically; no, perceptually. Using llama.cpp's own perplexity runs on Llama models as the reference: Q8_0 lands within ~0.02% of FP16 — for any practical purpose, lossless. Q6_K is near-indistinguishable. Q4_K_M gains roughly 1–2% perplexity, Q4_0 a bit more, and Q2_K is where coherent answers start falling apart on small models.&lt;/p&gt;

&lt;p&gt;Two rules the numbers imply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model size buys quantization headroom.&lt;/strong&gt; A 70B model survives Q2/Q3 far better than a 7B model does, because larger models are more redundant. Quantizing a 7B to Q2 is amputation; quantizing a 70B to Q3 is tailoring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The quality floor moves with the task.&lt;/strong&gt; Chat tolerates Q4. Exact code generation, math, and RAG over precise documents expose quantization noise sooner. If a Q4 model keeps writing subtly wrong code, test the same model at Q6_K before you blame the model.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Bits/weight&lt;/th&gt;
&lt;th&gt;Size vs FP16&lt;/th&gt;
&lt;th&gt;Quality loss&lt;/th&gt;
&lt;th&gt;Use it when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Q2_K&lt;/td&gt;
&lt;td&gt;~3.4&lt;/td&gt;
&lt;td&gt;~21%&lt;/td&gt;
&lt;td&gt;Severe on sub-13B&lt;/td&gt;
&lt;td&gt;Nothing else fits, large models only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q3_K_M&lt;/td&gt;
&lt;td&gt;~3.9&lt;/td&gt;
&lt;td&gt;~25%&lt;/td&gt;
&lt;td&gt;Noticeable&lt;/td&gt;
&lt;td&gt;Tight VRAM, ≥14B models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q4_K_S&lt;/td&gt;
&lt;td&gt;~4.6&lt;/td&gt;
&lt;td&gt;~29%&lt;/td&gt;
&lt;td&gt;Small&lt;/td&gt;
&lt;td&gt;Q4_K_M won't fit and it's close&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Q4_K_M&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~4.85&lt;/td&gt;
&lt;td&gt;~30%&lt;/td&gt;
&lt;td&gt;~1% perplexity&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;The default. Best quality/size trade&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q5_K_M&lt;/td&gt;
&lt;td&gt;~5.7&lt;/td&gt;
&lt;td&gt;~35%&lt;/td&gt;
&lt;td&gt;~0.5%&lt;/td&gt;
&lt;td&gt;VRAM available, quality-critical tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q6_K&lt;/td&gt;
&lt;td&gt;~6.6&lt;/td&gt;
&lt;td&gt;~41%&lt;/td&gt;
&lt;td&gt;Near-nil&lt;/td&gt;
&lt;td&gt;Code/math, still fits comfortably&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q8_0&lt;/td&gt;
&lt;td&gt;~8.5&lt;/td&gt;
&lt;td&gt;~53%&lt;/td&gt;
&lt;td&gt;Effectively none&lt;/td&gt;
&lt;td&gt;Reference runs, fine-tune bases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IQ4_XS&lt;/td&gt;
&lt;td&gt;~4.3&lt;/td&gt;
&lt;td&gt;~27%&lt;/td&gt;
&lt;td&gt;≈Q4_K_M&lt;/td&gt;
&lt;td&gt;Q4_K_M slightly too big, backend supports i-quants&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How much VRAM does each level need?
&lt;/h2&gt;

&lt;p&gt;Do the size math yourself instead of memorizing tables — it is one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;size_GB ≈ (bits_per_weight × params) / 8
# 8B model @ Q4_K_M: 4.85 × 8 / 8 ≈ 4.9 GB
# 8B model @ Q8_0:   8.50 × 8 / 8 ≈ 8.5 GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add the parts the formula leaves out: the KV cache (grows with context length — a few hundred MB to multiple GB), activations, and compute buffers. Practical margin: a "4.9 GB" model wants a 6 GB card at 4k context, and flash-attention plus KV-cache quantization to stay there at 16k. Weights are the headline, not the whole bill.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which GGUF quantization should you use?
&lt;/h2&gt;

&lt;p&gt;Decision order, no exceptions worth memorizing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Compute your context + KV budget first&lt;/strong&gt;, then weights. Context you can't fit is worse than quality you can't measure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default to Q4_K_M.&lt;/strong&gt; It is the community's default for a reason — roughly 1% perplexity for 70% of the size. Every registry, including Ollama's, ships it as the baseline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step up to Q6_K when the task punishes noise&lt;/strong&gt;: code, math, extraction, anything you feed to a pipeline unattended.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Q8_0 only for reference&lt;/strong&gt; — A/B tests, quantization-damage measurement, or a fine-tune base. As a daily driver it mostly buys warmth in your VRAM sensors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Go below Q4 only under duress&lt;/strong&gt;, and only on large models. Test with a known-hard prompt before trusting it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are choosing which &lt;em&gt;file&lt;/em&gt; to download on Hugging Face, prefer a single &lt;code&gt;Q4_K_M.gguf&lt;/code&gt; over sharded splits unless the uploader only ships the latter — fewer moving parts. And if you are choosing &lt;em&gt;where&lt;/em&gt; to run it, the engine choice is separate: see &lt;a href="https://mrsaynothing.dev/en/blog/2026-09-10/llama-cpp-vs-ollama/" rel="noopener noreferrer"&gt;llama.cpp vs Ollama&lt;/a&gt; for that axis.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you measure quantization damage yourself?
&lt;/h2&gt;

&lt;p&gt;Benchmarks differ; your prompt is constant. Build llama.cpp once, download two levels of the same model, and measure both perplexity (lower is better) and tokens/second:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/ggml-org/llama.cpp &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;llama.cpp
cmake &lt;span class="nt"&gt;-B&lt;/span&gt; build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; cmake &lt;span class="nt"&gt;--build&lt;/span&gt; build &lt;span class="nt"&gt;--config&lt;/span&gt; Release &lt;span class="nt"&gt;-j&lt;/span&gt;

huggingface-cli download bartowski/Meta-Llama-3.1-8B-Instruct-GGUF &lt;span class="se"&gt;\&lt;/span&gt;
  Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf Meta-Llama-3.1-8B-Instruct-Q8_0.gguf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--local-dir&lt;/span&gt; models

&lt;span class="c"&gt;# perplexity on a wiki-text chunk (lower = closer to the original model)&lt;/span&gt;
./build/bin/llama-perplexity &lt;span class="nt"&gt;-m&lt;/span&gt; models/Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf &lt;span class="nt"&gt;-ngl&lt;/span&gt; 99
./build/bin/llama-perplexity &lt;span class="nt"&gt;-m&lt;/span&gt; models/Meta-Llama-3.1-8B-Instruct-Q8_0.gguf  &lt;span class="nt"&gt;-ngl&lt;/span&gt; 99

&lt;span class="c"&gt;# and speed on the same hardware&lt;/span&gt;
./build/bin/llama-bench &lt;span class="nt"&gt;-m&lt;/span&gt; models/Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf &lt;span class="nt"&gt;-ngl&lt;/span&gt; 99
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Q4_K_M number will be a few hundredths of a point worse than Q8_0 and the file will be ~40% smaller. If your downstream task can't tell the difference — and for most, it can't — you have your answer without reading anyone's leaderboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does quantization hurt privacy or local-only claims?
&lt;/h2&gt;

&lt;p&gt;No — it is arithmetic on weights, entirely offline, and the quantized file is just a smaller container of the same parameters. Running a Q4_K_M locally leaks exactly as much (or little) as running the full-precision model locally: nothing leaves the machine. The privacy-relevant variable is &lt;em&gt;where inference runs&lt;/em&gt;, not the bit width. The usual caveats about model provenance apply equally to every quant level: a stolen-base "uncensored" fine-tune in Q8 is not safer than the same weights in Q4. For the file-format side of this, see &lt;a href="https://mrsaynothing.dev/en/blog/2026-09-07/how-to-run-gguf-models-locally/" rel="noopener noreferrer"&gt;how to run GGUF models locally&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which level should you pick?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q4_K_M, and stop reading forums about it.&lt;/strong&gt; Upgrade to Q6_K for precision-hungry work if VRAM allows, keep one Q8_0 around for A/B comparisons, and treat anything under Q4 as an emergency ration for large models only. The one mistake worth avoiding is symmetric: worrying about Q4-vs-Q5 while ignoring context length, which wrecks more local setups than any quant ever did.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gguf</category>
      <category>quantization</category>
      <category>localllm</category>
    </item>
    <item>
      <title>Pipeline test</title>
      <dc:creator>Mr Say Nothing</dc:creator>
      <pubDate>Sun, 13 Sep 2026 11:57:49 +0000</pubDate>
      <link>https://dev.to/mrsaynothing/pipeline-test-3e8c</link>
      <guid>https://dev.to/mrsaynothing/pipeline-test-3e8c</guid>
      <description>&lt;p&gt;Validation body, leaving now.&lt;/p&gt;

</description>
      <category>test</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
