<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Free Up Space on a Developer Mac · Loosely Decoupled</title>
    <link>https://looselydecoupled.com/series/developer-mac-disk-cleanup/</link>
    <description>Find which tool caches and build leftovers fill your Mac, and get the space back without breaking a project or a running coding session.</description>
    <language>en-us</language>
    
    <copyright>© 2026 Emmanuel Tsouris</copyright>
    <lastBuildDate>Fri, 16 Oct 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://looselydecoupled.com/series/developer-mac-disk-cleanup/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Why Is the uv Cache So Large on My Mac?</title>
      <link>https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/</link>
      <guid isPermaLink="true">https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/</guid>
      <pubDate>Fri, 16 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Emmanuel Tsouris</dc:creator>
      <category>macOS</category>
      <category>Python</category>
      
      <description>Coding agents fill the uv cache when they launch MCP servers with uvx; prune it safely and see how much disk you really get back.</description>
      <content:encoded><![CDATA[<div class="answer" data-answer="true">
  <p class="answer__label" data-pagefind-ignore>TL;DR</p>
  <p>Quit every coding agent and editor that launches MCP servers, then run <code>uv cache prune</code>. A running session holds the cache lock, so prune prints &ldquo;Cache is currently in-use, waiting for other uv processes to finish&rdquo; and, after uv&rsquo;s lock timeout, gives up. Do not use <code>--force</code> to skip it. Expect much less back than prune reports: on this Mac it reported 111.1 GiB (about 119 GB) removed and the disk gained 27.5 GB, most likely because cloned files share blocks.</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">uv</span> <span class="console__token">cache</span> <span class="console__token">size</span> <span class="console__token">-H</span>
</span><span class="line"><span class="prompt">$ </span><span class="console__token">df</span> <span class="console__token">-h</span> <span class="console__token">/</span>
</span><span class="line"><span class="prompt">$ </span><span class="console__token">diskutil</span> <span class="console__token">info</span> <span class="console__token">/</span> <span class="console__token">|</span> <span class="console__token">grep</span> <span class="console__token">&#34;Container</span> <span class="console__token">Free</span> <span class="console__token">Space&#34;</span>
</span><span class="line"><span class="prompt">$ </span><span class="console__token">lsof</span> <span class="console__token">&#34;$(uv</span> <span class="console__token">cache</span> <span class="console__token">dir)/.lock&#34;</span>
</span><span class="line"><span class="prompt">$ </span><span class="console__token">uv</span> <span class="console__token">cache</span> <span class="console__token">prune</span></span></code></pre>
    </div>
  </div></div>
</div>

<p>My Mac was down to 38.4 GB free on a 1 TB disk. The largest directory on it was a 132.7 GiB uv cache that I had never thought about. The coding agents I use launch MCP servers with <code>uvx</code>, and that was enough to fill it.</p>
<p><code>uvx</code> is uv&rsquo;s command for running a Python tool from a temporary environment, without installing it. If you have never run uv by hand, an agent or editor ran it for you. This page is part of a series on <a href="https://looselydecoupled.com/series/developer-mac-disk-cleanup/">cleaning up a developer Mac&rsquo;s disk</a>. Part one of the series, finding what&rsquo;s filling the disk, is how to know the cache is the cause; you don&rsquo;t need it to follow the steps here.</p>
<div class="tested" id="tested">
  <p class="label label--seam">Tested with</p><dl class="tested__rows">
    <div class="tested__row"><dt>uv</dt> <dd>0.11.12</dd></div>
    <div class="tested__row"><dt>macOS</dt> <dd>27.0.1, Apple silicon, APFS</dd></div>
    <div class="tested__row"><dt>Date</dt> <dd>October 2026</dd></div>
  </dl>
</div>
<h2 id="check-the-size-and-location-of-the-cache">Check the size and location of the cache<a class="heading-anchor" href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/#check-the-size-and-location-of-the-cache"
     aria-label="Link to section: Check the size and location of the cache" data-pagefind-ignore></a>
</h2>
<p>The environments <code>uvx</code> builds go in uv&rsquo;s cache. Ask uv for the size and the directory:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">uv</span> <span class="console__token">cache</span> <span class="console__token">size</span> <span class="console__token">-H</span></span></code></pre>
    </div>
  </div><div class="code-block code-block--wrap console__output">
    <p class="console__output-label">Output</p>
    <div class="code-block__scroll"><pre><code><span class="line"><span class="console__token">warning:</span> <span class="console__token">`uv</span> <span class="console__token">cache</span> <span class="console__token">size`</span> <span class="console__token">is</span> <span class="console__token">experimental</span> <span class="console__token">and</span> <span class="console__token">may</span> <span class="console__token">change</span> <span class="console__token">without</span> <span class="console__token">warning.</span> <span class="console__token">Pass</span> <span class="console__token">`--preview-features</span> <span class="console__token">cache-size`</span> <span class="console__token">to</span> <span class="console__token">disable</span> <span class="console__token">this</span> <span class="console__token">warning.</span>
</span><span class="line"><span class="console__token">132.7GiB</span></span></code></pre>
    </div>
  </div><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">uv</span> <span class="console__token">cache</span> <span class="console__token">dir</span></span></code></pre>
    </div>
  </div><div class="code-block code-block--wrap console__output">
    <p class="console__output-label">Output</p>
    <div class="code-block__scroll"><pre><code><span class="line"><span class="console__token">/Users/&lt;user&gt;/.cache/uv</span></span></code></pre>
    </div>
  </div></div><p>uv labels <code>uv cache size</code> as experimental, which is the reason for the warning. The number is still the one to start from. The <code>--cache-dir</code> option, the <code>UV_CACHE_DIR</code> and <code>XDG_CACHE_HOME</code> variables and the <code>tool.uv.cache-dir</code> setting move the directory, according to <a href="https://docs.astral.sh/uv/concepts/cache/" rel="external noopener" target="_blank">uv&rsquo;s cache documentation<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a>. Every command below reads the directory from <code>uv cache dir</code>, so it follows a moved cache.</p>
<h2 id="find-what-fills-the-cache">Find what fills the cache<a class="heading-anchor" href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/#find-what-fills-the-cache"
     aria-label="Link to section: Find what fills the cache" data-pagefind-ignore></a>
</h2>
<p>Environments that <code>uvx</code> builds sit in the cache&rsquo;s <code>archive-v0</code> directory. Count its entries, then count the ones that are Python environments, which have a <code>pyvenv.cfg</code> at their root:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">ls</span> <span class="console__token">&#34;$(uv</span> <span class="console__token">cache</span> <span class="console__token">dir)&#34;/archive-v0</span> <span class="console__token">|</span> <span class="console__token">wc</span> <span class="console__token">-l</span>
</span><span class="line"><span class="prompt">$ </span><span class="console__token">find</span> <span class="console__token">&#34;$(uv</span> <span class="console__token">cache</span> <span class="console__token">dir)&#34;/archive-v0</span> <span class="console__token">-maxdepth</span> <span class="console__token">2</span> <span class="console__token">-name</span> <span class="console__token">pyvenv.cfg</span> <span class="console__token">|</span> <span class="console__token">wc</span> <span class="console__token">-l</span></span></code></pre>
    </div>
  </div></div><p>On this Mac, <code>archive-v0</code> held 4,005 entries, and 2,562 of them were environments. To see which MCP servers the environments belong to, count the executables with <code>mcp</code> in their names in the environments&rsquo; <code>bin</code> directories (a server named otherwise needs its own pattern):</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">ls</span> <span class="console__token">-d</span> <span class="console__token">&#34;$(uv</span> <span class="console__token">cache</span> <span class="console__token">dir)&#34;/archive-v0/*/bin/*mcp*</span> <span class="console__token">|</span> <span class="console__token">xargs</span> <span class="console__token">-n1</span> <span class="console__token">basename</span> <span class="console__token">|</span> <span class="console__token">sort</span> <span class="console__token">|</span> <span class="console__token">uniq</span> <span class="console__token">-c</span> <span class="console__token">|</span> <span class="console__token">sort</span> <span class="console__token">-rn</span> <span class="console__token">|</span> <span class="console__token">head</span></span></code></pre>
    </div>
  </div></div><p>Nearly all of the environments were copies of three MCP servers, with 1,150, 1,143 and 191 copies, plus 50 of a fourth. One server&rsquo;s environments held only 158 distinct sets of installed packages, and environments built on the same day had the same installed packages.</p>
<h3 id="why-each-launch-can-add-an-environment">Why each launch can add an environment<a class="heading-anchor" href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/#why-each-launch-can-add-an-environment"
     aria-label="Link to section: Why each launch can add an environment" data-pagefind-ignore></a>
</h3>
<p>An MCP server configured as <code>uvx &lt;server&gt;@latest</code> asks uv to resolve the latest version on every launch. With <code>uvx -v</code>, the log shows uv fetching the latest version and revalidating the index. Launching one server did not explain more than a thousand copies, so I measured when a launch adds an environment:</p>
<div class="table-wrap" data-scrollable data-label="Table: Launches">
  <table>
    <thead>
      <tr><th scope="col">Launches</th><th scope="col">Resolution</th><th class="align-right" scope="col">Environments added</th></tr>
    </thead>
    <tbody>
      <tr><td>One</td><td>New (not cached yet)</td><td class="align-right">1</td></tr>
      <tr><td>Four at the same moment</td><td>New</td><td class="align-right">4</td></tr>
      <tr><td>Four at the same moment</td><td>Already cached</td><td class="align-right">0</td></tr>
      <tr><td>Four, <code>@latest</code> and plain</td><td>Already cached</td><td class="align-right">0</td></tr>
    </tbody>
  </table>
</div>
<p>When several sessions start together and the resolution isn&rsquo;t cached yet, each launch builds its own environment. Once the resolution is cached, launches add nothing. That implies pinning a version, instead of <code>@latest</code>, would stop most of the copies. I didn&rsquo;t test that. I also didn&rsquo;t check whether a later release of uv reuses an environment that another process built moments earlier. All of this is uv 0.11.12.</p>
<h2 id="choose-between-uv-cache-prune-and-uv-cache-clean">Choose between uv cache prune and uv cache clean<a class="heading-anchor" href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/#choose-between-uv-cache-prune-and-uv-cache-clean"
     aria-label="Link to section: Choose between uv cache prune and uv cache clean" data-pagefind-ignore></a>
</h2>
<p><code>uv cache prune</code> removes the cache entries nothing uses, and all centralized project environments, which uv recreates as needed. <code>uv cache clean</code> removes every entry and empties the cache; <code>uv cache clean &lt;package&gt;</code> removes one package&rsquo;s entries. Both are described in <a href="https://docs.astral.sh/uv/concepts/cache/" rel="external noopener" target="_blank">uv&rsquo;s cache documentation<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a>. This page uses prune, which here took the cache from 132.7 GiB to 3.9 GiB and left 540 entries in place. I didn&rsquo;t run clean.</p>
<h2 id="quit-every-session-that-holds-the-cache-lock">Quit every session that holds the cache lock<a class="heading-anchor" href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/#quit-every-session-that-holds-the-cache-lock"
     aria-label="Link to section: Quit every session that holds the cache lock" data-pagefind-ignore></a>
</h2>
<p>Each running <code>uvx</code> process holds the cache lock, the <code>.lock</code> file in the cache directory, for as long as its server runs. Cache-modifying commands wait for the lock, so a prune started now waits too. List the holders:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">lsof</span> <span class="console__token">&#34;$(uv</span> <span class="console__token">cache</span> <span class="console__token">dir)/.lock&#34;</span></span></code></pre>
    </div>
  </div><div class="code-block code-block--wrap console__output">
    <p class="console__output-label">Output</p>
    <div class="code-block__scroll"><pre><code><span class="line"><span class="console__token">COMMAND</span>   <span class="console__token">PID</span>    <span class="console__token">USER</span>   <span class="console__token">FD</span>   <span class="console__token">TYPE</span> <span class="console__token">DEVICE</span> <span class="console__token">SIZE/OFF</span>    <span class="console__token">NODE</span> <span class="console__token">NAME</span>
</span><span class="line"><span class="console__token">uv</span>      <span class="console__token">&lt;pid&gt;</span> <span class="console__token">&lt;user&gt;</span>   <span class="console__token">9u</span>   <span class="console__token">REG</span>  <span class="console__token">&lt;dev&gt;</span>        <span class="console__token">0</span> <span class="console__token">&lt;inode&gt;</span> <span class="console__token">/Users/&lt;user&gt;/.cache/uv/.lock</span>
</span><span class="line"><span class="console__token">uv</span>      <span class="console__token">&lt;pid&gt;</span> <span class="console__token">&lt;user&gt;</span>   <span class="console__token">9u</span>   <span class="console__token">REG</span>  <span class="console__token">&lt;dev&gt;</span>        <span class="console__token">0</span> <span class="console__token">&lt;inode&gt;</span> <span class="console__token">/Users/&lt;user&gt;/.cache/uv/.lock</span></span></code></pre>
    </div>
  </div></div><p><code>lsof</code> prints nothing when the lock is free; empty output from a path that is not your cache directory is not a clear lock. Each row is one <code>uvx</code> process. To find the session that started it, look up the parent of each <code>&lt;pid&gt;</code>:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">ps</span> <span class="console__token">-o</span> <span class="console__token">ppid=</span> <span class="console__token">-p</span> <span class="console__token">&lt;pid&gt;</span>
</span><span class="line"><span class="prompt">$ </span><span class="console__token">ps</span> <span class="console__token">-o</span> <span class="console__token">comm=</span> <span class="console__token">-p</span> <span class="console__token">&lt;ppid&gt;</span></span></code></pre>
    </div>
  </div></div><p>On this Mac, one holder was launched by a Claude Code session and another by the VS Code extension host. Quitting a session or an editor ends its <code>uvx</code> children, which release the lock:</p>
<ol>
<li>Run <code>lsof &quot;$(uv cache dir)/.lock&quot;</code>.</li>
<li>Look up the parent of each <code>&lt;pid&gt;</code>.</li>
<li>Quit that session or editor.</li>
<li>Run <code>lsof</code> again until it prints nothing.</li>
</ol>
<h2 id="record-free-space-before-pruning">Record free space before pruning<a class="heading-anchor" href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/#record-free-space-before-pruning"
     aria-label="Link to section: Record free space before pruning" data-pagefind-ignore></a>
</h2>
<p>The comparison needs readings from before the prune, so take them now and save the output:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">df</span> <span class="console__token">-h</span> <span class="console__token">/</span>
</span><span class="line"><span class="prompt">$ </span><span class="console__token">diskutil</span> <span class="console__token">info</span> <span class="console__token">/</span> <span class="console__token">|</span> <span class="console__token">grep</span> <span class="console__token">&#34;Container</span> <span class="console__token">Free</span> <span class="console__token">Space&#34;</span>
</span><span class="line"><span class="prompt">$ </span><span class="console__token">uv</span> <span class="console__token">cache</span> <span class="console__token">size</span> <span class="console__token">-H</span></span></code></pre>
    </div>
  </div></div><h2 id="run-the-prune">Run the prune<a class="heading-anchor" href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/#run-the-prune"
     aria-label="Link to section: Run the prune" data-pagefind-ignore></a>
</h2>
<p>This command deletes cached environments. With nothing running, it removed every environment in the cache, not only the ones that no process referenced. The next launch of each MCP server builds a fresh environment. Run it from a plain Terminal window, not from inside one of the sessions you quit:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">uv</span> <span class="console__token">cache</span> <span class="console__token">prune</span></span></code></pre>
    </div>
  </div><div class="code-block code-block--wrap console__output">
    <p class="console__output-label">Output</p>
    <div class="code-block__scroll"><pre><code><span class="line"><span class="console__token">Pruning</span> <span class="console__token">cache</span> <span class="console__token">at:</span> <span class="console__token">.cache/uv</span>
</span><span class="line"><span class="console__token">Removed</span> <span class="console__token">7278379</span> <span class="console__token">files</span> <span class="console__token">(111.1GiB)</span></span></code></pre>
    </div>
  </div></div><p>It took 409 seconds. Do not interrupt it.</p>
<h2 id="check-the-space-you-got-back">Check the space you got back<a class="heading-anchor" href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/#check-the-space-you-got-back"
     aria-label="Link to section: Check the space you got back" data-pagefind-ignore></a>
</h2>
<p>The free space went up by much less than 111.1 GiB. Run the same three commands again and compare them with the readings you saved:</p>
<div class="table-wrap" data-scrollable data-label="Table: Reading">
  <table>
    <thead>
      <tr><th scope="col">Reading</th><th scope="col">Before</th><th scope="col">After</th></tr>
    </thead>
    <tbody>
      <tr><td><code>df -h /</code>, available</td><td>36Gi</td><td>61Gi</td></tr>
      <tr><td>Container Free Space</td><td>38.4 GB (38,372,454,400 bytes)</td><td>65.8 GB (65,847,853,056 bytes)</td></tr>
      <tr><td><code>uv cache size -H</code></td><td>132.7GiB</td><td>3.9GiB</td></tr>
    </tbody>
  </table>
</div>
<p>The container gained 27.5 GB. uv reported 111.1 GiB removed, which is about 119 GB. uv prints binary units (GiB, 2^30 bytes) and <code>diskutil</code> prints decimal ones (GB), so do not compare the two figures as equals. Even in the same unit, the disk got back about a quarter of what uv reported.</p>
<p>The explanation that fits is the link mode; I didn&rsquo;t test it directly. uv&rsquo;s default on macOS is <code>clone</code>, per <a href="https://docs.astral.sh/uv/reference/settings/#link-mode" rel="external noopener" target="_blank">uv&rsquo;s settings reference<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a>, which makes APFS copy-on-write clones. Environments share blocks with each other and with the cached wheels. uv 0.11.12 counts every cloned file at its full size, and deleting a clone frees nothing when another file still uses its blocks. <a href="https://docs.astral.sh/uv/concepts/cache/" rel="external noopener" target="_blank">uv&rsquo;s cache documentation<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a> describes a <code>cache-physical-space</code> preview feature that accounts for copy-on-write clones; I didn&rsquo;t test it.</p>
<p><code>du -sh</code> agreed with <code>uv cache size</code> at 3.9G after the prune. That is a drop of 128.8 GiB from 132.7GiB, while prune printed 111.1GiB removed; I did not find why the two differ.</p>
<p>A local snapshot could also hold space after a delete. On this Mac, <code>tmutil listlocalsnapshots /</code> listed none, and <code>diskutil apfs listSnapshots</code> showed none on the data volume, so no snapshot was holding any.</p>
<h2 id="confirm-your-mcp-servers-still-start">Confirm your MCP servers still start<a class="heading-anchor" href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/#confirm-your-mcp-servers-still-start"
     aria-label="Link to section: Confirm your MCP servers still start" data-pagefind-ignore></a>
</h2>
<p>The prune left <code>archive-v0</code> with 540 entries and no environments, and emptied <code>environments-v2</code>. Start each agent or editor that uses MCP servers and open a session. Each server builds a fresh environment on its next launch; on this Mac, one appeared at the next session start. Check:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">find</span> <span class="console__token">&#34;$(uv</span> <span class="console__token">cache</span> <span class="console__token">dir)&#34;/archive-v0</span> <span class="console__token">-maxdepth</span> <span class="console__token">2</span> <span class="console__token">-name</span> <span class="console__token">pyvenv.cfg</span> <span class="console__token">|</span> <span class="console__token">wc</span> <span class="console__token">-l</span></span></code></pre>
    </div>
  </div></div><p>A count above zero means at least one server built an environment. Check each agent&rsquo;s MCP server list or status view for errors.</p>
<h2 id="do-this-with-a-coding-agent">Do this with a coding agent<a class="heading-anchor" href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/#do-this-with-a-coding-agent"
     aria-label="Link to section: Do this with a coding agent" data-pagefind-ignore></a>
</h2>
<p>Paste this prompt into your own local coding agent. It measures and reports, and it proposes before anything is removed:</p>
<div class="code-block" data-copyable data-copy-label="Text">
  <div class="code-block__head"><span class="code-block__lang">Text</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Investigate my uv cache on this Mac. Measure and report only; change nothing yet.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">1. Report the cache directory (uv cache dir) and its size (uv cache size -H).
</span></span><span class="line"><span class="cl">2. Report how many entries in archive-v0 are Python environments (a pyvenv.cfg
</span></span><span class="line"><span class="cl">   at the root), and which tools they belong to (the executables in their bin
</span></span><span class="line"><span class="cl">   directories).
</span></span><span class="line"><span class="cl">3. Report free space two ways: df -h / and diskutil info / (Container Free
</span></span><span class="line"><span class="cl">   Space).
</span></span><span class="line"><span class="cl">4. List every process holding the cache lock (lsof on the .lock file in the
</span></span><span class="line"><span class="cl">   cache directory), and for each one its parent process, so I can tell which
</span></span><span class="line"><span class="cl">   agent or editor session to quit.
</span></span><span class="line"><span class="cl">5. Separate what is safe from what is in use: running uv processes, open agent
</span></span><span class="line"><span class="cl">   or editor sessions, and any project whose own virtual environment lives
</span></span><span class="line"><span class="cl">   inside the cache. If this session is itself one of the lock holders, say so
</span></span><span class="line"><span class="cl">   and stop; I will run the prune from a plain Terminal.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Then propose each removal as a separate item, with the command and the current
</span></span><span class="line"><span class="cl">cache size as an upper bound. uv has no dry run, so do not state an expected
</span></span><span class="line"><span class="cl">figure, and note that APFS clones make the real gain smaller. Remove nothing until I approve that item. Remove only
</span></span><span class="line"><span class="cl">through uv&#39;s own command (uv cache prune). Never delete files under the cache
</span></span><span class="line"><span class="cl">directory by hand and never use --force. Afterwards, report free space again
</span></span><span class="line"><span class="cl">with the same two readings and say how far the figure uv printed is from the
</span></span><span class="line"><span class="cl">space actually gained.</span></span></code></pre></div></div>
  </div>
</div><h2 id="common-failures">Common failures<a class="heading-anchor" href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/#common-failures"
     aria-label="Link to section: Common failures" data-pagefind-ignore></a>
</h2>
<h3 id="cache-is-currently-in-use-waiting-for-other-uv-processes-to-finish">Cache is currently in-use, waiting for other uv processes to finish<a class="heading-anchor" href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/#cache-is-currently-in-use-waiting-for-other-uv-processes-to-finish"
     aria-label="Link to section: Cache is currently in-use, waiting for other uv processes to finish" data-pagefind-ignore></a>
</h3>
<div class="console console--error"><div class="code-block code-block--wrap console__output">
    <p class="console__output-label">Error</p>
    <div class="code-block__scroll"><pre><code><span class="line"><span class="console__token">Cache</span> <span class="console__token">is</span> <span class="console__token">currently</span> <span class="console__token">in-use,</span> <span class="console__token">waiting</span> <span class="console__token">for</span> <span class="console__token">other</span> <span class="console__token">uv</span> <span class="console__token">processes</span> <span class="console__token">to</span> <span class="console__token">finish</span> <span class="console__token">(use</span> <span class="console__token">`--force`</span> <span class="console__token">to</span> <span class="console__token">override)</span></span></code></pre>
    </div>
  </div></div><p>Another uv process holds the cache lock, and <code>uv cache prune</code> and <code>uv cache clean</code> wait for it. Run <code>lsof &quot;$(uv cache dir)/.lock&quot;</code>, quit the session that owns each holder as described earlier, and run the command again. Nothing was changed while it waited: I stopped a prune that had waited eight seconds, and the cache was unchanged.</p>
<p>These <code>uv cache</code> commands give up after a timeout; <a href="https://docs.astral.sh/uv/concepts/cache/" rel="external noopener" target="_blank">uv&rsquo;s cache documentation<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a> gives its value and the <code>UV_LOCK_TIMEOUT</code> environment variable that changes it. Waiting longer does not help while a session still runs its server.</p>
<p>Do not use <code>--force</code>. The documentation reserves it for cases where no other uv process is reading or writing the cache, and a running <code>uvx</code> server is exactly such a process.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>What&#39;s Taking Up All the Space on My Developer Mac?</title>
      <link>https://looselydecoupled.com/mac-disk-full-developer-caches/</link>
      <guid isPermaLink="true">https://looselydecoupled.com/mac-disk-full-developer-caches/</guid>
      <pubDate>Thu, 15 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Emmanuel Tsouris</dc:creator>
      <category>macOS</category>
      <category>Python</category>
      
      <description>Find which developer tool is filling your Mac&#39;s disk, such as uv, Xcode or Docker, and which cleanup applies.</description>
      <content:encoded><![CDATA[<div class="answer" data-answer="true">
  <p class="answer__label" data-pagefind-ignore>TL;DR</p>
  <p>Record the free space, then narrow down one level at a time with <code>du</code> until a
directory belongs to one tool. That tells you which cleanup you need. Clean it
with that tool&rsquo;s own command where it has one, never with <code>rm</code> on a cache a
tool manages, and record the free space again; the cleanup&rsquo;s own report is not what the Mac got back.</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">diskutil</span> <span class="console__token">info</span> <span class="console__token">/</span> <span class="console__token">|</span> <span class="console__token">grep</span> <span class="console__token">&#34;Container</span> <span class="console__token">Free</span> <span class="console__token">Space&#34;</span>
</span><span class="line"><span class="prompt">$ </span><span class="console__token">du</span> <span class="console__token">-hd</span> <span class="console__token">1</span> <span class="console__token">~</span> <span class="console__token">2&gt;/dev/null</span> <span class="console__token">|</span> <span class="console__token">sort</span> <span class="console__token">-hr</span> <span class="console__token">|</span> <span class="console__token">head</span> <span class="console__token">-15</span></span></code></pre>
    </div>
  </div></div><div class="tested" id="tested">
  <p class="label label--seam">Tested with</p><dl class="tested__rows">
    <div class="tested__row"><dt>macOS</dt> <dd>27.0.1, MacBook Pro (Apple silicon)</dd></div>
    <div class="tested__row"><dt>Date</dt> <dd>October 2026</dd></div>
  </dl>
</div>

</div>

<p>My Mac had 37.7 GB free on a 1 TB disk. I expected Xcode to be the reason,
since I keep a full set of iOS simulators, and Xcode did hold 79 GiB. The
largest directory was a 132 GiB uv cache, which I had never thought about: most
of the entries in it were environments <code>uvx</code> had built for MCP servers,
covered in part 2.
Which directory is largest on your Mac depends on what you build and which
tools you run. The method for finding it is below, and the
<a href="https://looselydecoupled.com/series/developer-mac-disk-cleanup/">series</a> covers each cause.</p>
<h2 id="record-the-free-space-before-you-start">Record the free space before you start<a class="heading-anchor" href="https://looselydecoupled.com/mac-disk-full-developer-caches/#record-the-free-space-before-you-start"
     aria-label="Link to section: Record the free space before you start" data-pagefind-ignore></a>
</h2>
<p>On a modern Mac, <code>df -h /</code> reports the sealed system volume, so its <code>Used</code>
column is misleading:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">df</span> <span class="console__token">-h</span> <span class="console__token">/</span></span></code></pre>
    </div>
  </div><div class="code-block code-block--wrap console__output">
    <p class="console__output-label">Output</p>
    <div class="code-block__scroll"><pre><code><span class="line"><span class="console__token">Filesystem</span>        <span class="console__token">Size</span>    <span class="console__token">Used</span>   <span class="console__token">Avail</span> <span class="console__token">Capacity</span> <span class="console__token">iused</span> <span class="console__token">ifree</span> <span class="console__token">%iused</span>  <span class="console__token">Mounted</span> <span class="console__token">on</span>
</span><span class="line"><span class="console__token">/dev/disk3s1s1</span>   <span class="console__token">926Gi</span>    <span class="console__token">13Gi</span>    <span class="console__token">40Gi</span>    <span class="console__token">24%</span>    <span class="console__token">484k</span>  <span class="console__token">423M</span>    <span class="console__token">0%</span>   <span class="console__token">/</span></span></code></pre>
    </div>
  </div></div><p><code>/</code> is the sealed system snapshot. Your files live on the Data volume in the
same APFS container. Every volume in a container draws on one pool of free
space, so <code>Used</code> for <code>/</code> counts the system alone, and <code>Avail</code> is the pool. The
container&rsquo;s own figure comes from the startup disk&rsquo;s container only, so an
external APFS drive cannot be mistaken for it:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">diskutil</span> <span class="console__token">info</span> <span class="console__token">/</span> <span class="console__token">|</span> <span class="console__token">grep</span> <span class="console__token">&#34;Container</span> <span class="console__token">Free</span> <span class="console__token">Space&#34;</span></span></code></pre>
    </div>
  </div><div class="code-block code-block--wrap console__output">
    <p class="console__output-label">Output</p>
    <div class="code-block__scroll"><pre><code><span class="line">   <span class="console__token">Container</span> <span class="console__token">Free</span> <span class="console__token">Space:</span>      <span class="console__token">65.8</span> <span class="console__token">GB</span> <span class="console__token">(65847853056</span> <span class="console__token">Bytes)</span> <span class="console__token">(exactly</span> <span class="console__token">128609088</span> <span class="console__token">512-Byte-Units)</span></span></code></pre>
    </div>
  </div></div><p>This reading was taken after the cleanup, so it does not match the <code>df</code>
output above, which was taken partway through, or the 37.7 GB before any
cleanup. <code>df -h</code> prints GiB and
<code>diskutil</code> prints GB, so the same moment looks different in the two units. Use one of them for every reading, or the difference
between the units looks like progress. A Mac keeps writing while you work, so
treat a change of a gigabyte or two as noise.</p>
<h2 id="find-whats-taking-up-space-with-du">Find what&rsquo;s taking up space with du<a class="heading-anchor" href="https://looselydecoupled.com/mac-disk-full-developer-caches/#find-whats-taking-up-space-with-du"
     aria-label="Link to section: Find what’s taking up space with du" data-pagefind-ignore></a>
</h2>
<p><code>du</code> reads sizes and changes nothing. <code>2&gt;/dev/null</code> hides the permission
errors that protected directories print.</p>
<!-- style-check: procedure -->
<ol>
<li>
<p>List the largest directories in your home directory:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">du</span> <span class="console__token">-hd</span> <span class="console__token">1</span> <span class="console__token">~</span> <span class="console__token">2&gt;/dev/null</span> <span class="console__token">|</span> <span class="console__token">sort</span> <span class="console__token">-hr</span> <span class="console__token">|</span> <span class="console__token">head</span> <span class="console__token">-15</span></span></code></pre>
    </div>
  </div></div></li>
<li>
<p>Skip the first line, which is the total for the directory you ran it on.
Run the same command on the largest directory below it, and repeat until
the directory belongs to one tool. On this Mac, that took three levels:
<code>~/.cache</code>, then <code>~/.cache/uv</code>, then <code>~/.cache/uv/archive-v0</code>.</p>
</li>
<li>
<p>Check the two places that hide the most, even if they didn&rsquo;t top the first
list:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">du</span> <span class="console__token">-hd</span> <span class="console__token">1</span> <span class="console__token">~/Library/Caches</span> <span class="console__token">2&gt;/dev/null</span> <span class="console__token">|</span> <span class="console__token">sort</span> <span class="console__token">-hr</span> <span class="console__token">|</span> <span class="console__token">head</span> <span class="console__token">-15</span>
</span><span class="line"><span class="prompt">$ </span><span class="console__token">du</span> <span class="console__token">-hd</span> <span class="console__token">1</span> <span class="console__token">~/Library/Developer</span> <span class="console__token">2&gt;/dev/null</span> <span class="console__token">|</span> <span class="console__token">sort</span> <span class="console__token">-hr</span></span></code></pre>
    </div>
  </div></div></li>
<li>
<p>For a package manager, ask the tool where its cache is rather than
assuming a path. pip on macOS uses <code>~/Library/Caches/pip</code>, not
<code>~/.cache/pip</code>:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">uv</span> <span class="console__token">cache</span> <span class="console__token">dir</span></span></code></pre>
    </div>
  </div><div class="code-block code-block--wrap console__output">
    <p class="console__output-label">Output</p>
    <div class="code-block__scroll"><pre><code><span class="line"><span class="console__token">/Users/&lt;user&gt;/.cache/uv</span></span></code></pre>
    </div>
  </div><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">python3</span> <span class="console__token">-m</span> <span class="console__token">pip</span> <span class="console__token">cache</span> <span class="console__token">dir</span></span></code></pre>
    </div>
  </div><div class="code-block code-block--wrap console__output">
    <p class="console__output-label">Output</p>
    <div class="code-block__scroll"><pre><code><span class="line"><span class="console__token">/Users/&lt;user&gt;/Library/Caches/pip</span></span></code></pre>
    </div>
  </div></div></li>
</ol>
<!-- style-check: end procedure -->
<p>A <code>du</code> total is an upper bound on what deleting frees. APFS clones share
their blocks with the original until one of them changes, and <code>du</code> counts a
cloned file at full size wherever it appears.</p>
<h2 id="match-the-directory-to-its-cause">Match the directory to its cause<a class="heading-anchor" href="https://looselydecoupled.com/mac-disk-full-developer-caches/#match-the-directory-to-its-cause"
     aria-label="Link to section: Match the directory to its cause" data-pagefind-ignore></a>
</h2>
<p>These are the directories that were large on this Mac, and the part of this
series that covers each one:</p>
<div class="table-wrap" data-scrollable data-label="Table: Directory and what fills it">
  <table>
    <thead>
      <tr><th scope="col">Directory and what fills it</th><th class="align-right" scope="col">Size here</th><th scope="col">Covered in</th></tr>
    </thead>
    <tbody>
      <tr><td><code>~/.cache/uv</code><br>Environments that <code>uvx</code> builds for MCP servers</td><td class="align-right">132 GiB</td><td>Part 2</td></tr>
      <tr><td><code>~/Library/Developer/CoreSimulator/Devices</code><br>iOS simulators and the apps installed in them</td><td class="align-right">44 GiB</td><td>Part 5</td></tr>
      <tr><td><code>~/Library/Developer/Xcode/iOS DeviceSupport</code><br>Debug symbols for each device and iOS version you&rsquo;ve connected</td><td class="align-right">35 GiB</td><td>Part 5</td></tr>
      <tr><td><code>~/Library/Containers/com.docker.docker</code><br>Docker Desktop&rsquo;s VM disk, <code>Docker.raw</code></td><td class="align-right">32 GiB</td><td>Part 3</td></tr>
      <tr><td><code>~/.npm/_npx</code><br>Packages that <code>npx</code> runs, MCP servers among them</td><td class="align-right">8.9 GiB</td><td>Part 4</td></tr>
      <tr><td><code>.build</code>, <code>node_modules</code>, <code>.venv</code> in your projects<br>Build output and dependencies, including projects you no longer work on</td><td class="align-right">varies</td><td>Part 6</td></tr>
      <tr><td><code>~/Library/Caches/Homebrew</code><br>Cached downloads of formulae and casks</td><td class="align-right">1.1 GiB</td><td><a href="https://looselydecoupled.com/mac-disk-full-developer-caches/#clear-homebrew-downloads-and-ollama-logs">Below</a></td></tr>
      <tr><td><code>~/.ollama/logs</code><br>Ollama&rsquo;s server and app logs</td><td class="align-right">947 MB</td><td><a href="https://looselydecoupled.com/mac-disk-full-developer-caches/#clear-homebrew-downloads-and-ollama-logs">Below</a></td></tr>
    </tbody>
  </table>
</div>
<p>For the uv cache, read <a href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/">part 2</a>
before you prune: a running coding session holds a lock that makes the prune
wait, and the prune reports far more than the disk gets back. The other parts
are listed on the series page.</p>
<p>Some directories stay whatever their size. A Docker volume holds an app&rsquo;s
data, not cache. An app&rsquo;s <code>Application Support</code> directory holds its state.</p>
<h2 id="clear-homebrew-downloads-and-ollama-logs">Clear Homebrew downloads and Ollama logs<a class="heading-anchor" href="https://looselydecoupled.com/mac-disk-full-developer-caches/#clear-homebrew-downloads-and-ollama-logs"
     aria-label="Link to section: Clear Homebrew downloads and Ollama logs" data-pagefind-ignore></a>
</h2>
<p>Two directories on the list need no part of their own.</p>
<p><code>brew cleanup -s</code> removes old versions and the cached downloads of anything no
longer installed. It keeps the downloads of what is installed now, so on this
Mac it freed about 270 MB of the 1.1 GiB:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">brew</span> <span class="console__token">cleanup</span> <span class="console__token">-s</span></span></code></pre>
    </div>
  </div></div><p>An app can keep its data somewhere else and still write its logs to the
internal disk. My Ollama models live on an external SSD, linked into place:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">ls</span> <span class="console__token">-ld</span> <span class="console__token">~/.ollama/models</span></span></code></pre>
    </div>
  </div><div class="code-block code-block--wrap console__output">
    <p class="console__output-label">Output</p>
    <div class="code-block__scroll"><pre><code><span class="line"><span class="console__token">lrwxr-xr-x</span>  <span class="console__token">1</span> <span class="console__token">&lt;user&gt;</span>  <span class="console__token">staff</span>  <span class="console__token">...</span> <span class="console__token">/Users/&lt;user&gt;/.ollama/models</span> <span class="console__token">-&gt;</span> <span class="console__token">/Volumes/&lt;drive&gt;/ollama/models</span></span></code></pre>
    </div>
  </div></div><p>The logs aren&rsquo;t linked. List them first:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">ls</span> <span class="console__token">-lh</span> <span class="console__token">~/.ollama/logs</span></span></code></pre>
    </div>
  </div></div><p>Here they had reached 947 MB in two files, <code>server-1.log</code> and <code>app-1.log</code>.
Yours may have other names. Ollama has no cleanup command for them, so quit
Ollama and delete the files that list showed, by name:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">rm</span> <span class="console__token">~/.ollama/logs/server-1.log</span> <span class="console__token">~/.ollama/logs/app-1.log</span></span></code></pre>
    </div>
  </div></div><p>Deleting a log leaves the models and the settings as they are.</p>
<p>Ollama wasn&rsquo;t in Open at Login, but App Background Activity, under System
Settings &gt; General &gt; Login Items, allowed it to run in the background. I
turned that off. I didn&rsquo;t check whether background runs are what filled the
logs.</p>
<h2 id="record-the-free-space-again">Record the free space again<a class="heading-anchor" href="https://looselydecoupled.com/mac-disk-full-developer-caches/#record-the-free-space-again"
     aria-label="Link to section: Record the free space again" data-pagefind-ignore></a>
</h2>
<p>Run the same command you started with:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">diskutil</span> <span class="console__token">info</span> <span class="console__token">/</span> <span class="console__token">|</span> <span class="console__token">grep</span> <span class="console__token">&#34;Container</span> <span class="console__token">Free</span> <span class="console__token">Space&#34;</span></span></code></pre>
    </div>
  </div></div><p>Here it went from 37.7 GB before any cleanup to 65.8 GB after the uv prune.
The cleanup commands had reported more than 140 GB between them. A cleanup
command reports what it removed from its own storage; only the container&rsquo;s
figure says what the Mac got back.</p>
<h2 id="do-this-with-a-coding-agent">Do this with a coding agent<a class="heading-anchor" href="https://looselydecoupled.com/mac-disk-full-developer-caches/#do-this-with-a-coding-agent"
     aria-label="Link to section: Do this with a coding agent" data-pagefind-ignore></a>
</h2>
<p>Paste this prompt into your own local coding agent. It finds and reports, and
it deletes nothing:</p>
<div class="code-block" data-copyable data-copy-label="Text">
  <div class="code-block__head"><span class="code-block__lang">Text</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Find what is taking up space on this Mac&#39;s internal disk. Measure and report
</span></span><span class="line"><span class="cl">only; delete, prune or clean nothing. Stay in my home directory, and don&#39;t
</span></span><span class="line"><span class="cl">follow links to external drives.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">1. Report free space with diskutil info / (Container Free Space) and
</span></span><span class="line"><span class="cl">   df -h /, and say which units each prints.
</span></span><span class="line"><span class="cl">2. Run du -hd 1 on my home directory, then on the largest subdirectory it
</span></span><span class="line"><span class="cl">   lists (not the total line for the directory itself), and keep going down
</span></span><span class="line"><span class="cl">   until each large directory belongs to one tool. Also check
</span></span><span class="line"><span class="cl">   ~/Library/Caches and ~/Library/Developer. Ask package managers for their
</span></span><span class="line"><span class="cl">   cache paths (for example uv cache dir, python3 -m pip cache dir) instead of
</span></span><span class="line"><span class="cl">   assuming them.
</span></span><span class="line"><span class="cl">3. For each directory over 1 GB, report its size, which tool owns it, what
</span></span><span class="line"><span class="cl">   fills it, and the tool&#39;s own cleanup command if it has one.
</span></span><span class="line"><span class="cl">4. Mark anything that is in use or holds work: running processes using the
</span></span><span class="line"><span class="cl">   directory (lsof), open coding-agent or editor sessions, uncommitted or
</span></span><span class="line"><span class="cl">   unpushed changes in any git checkout, Docker volumes, app data under
</span></span><span class="line"><span class="cl">   Application Support. Say what would be lost if each were removed.
</span></span><span class="line"><span class="cl">5. Note that du counts APFS clones at full size, so a total is an upper bound
</span></span><span class="line"><span class="cl">   on what deleting frees.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Then stop and wait. I will choose what to clean.</span></span></code></pre></div></div>
  </div>
</div><h2 id="common-failures">Common failures<a class="heading-anchor" href="https://looselydecoupled.com/mac-disk-full-developer-caches/#common-failures"
     aria-label="Link to section: Common failures" data-pagefind-ignore></a>
</h2>
<h3 id="free-space-barely-moved-after-a-cleanup">Free space barely moved after a cleanup<a class="heading-anchor" href="https://looselydecoupled.com/mac-disk-full-developer-caches/#free-space-barely-moved-after-a-cleanup"
     aria-label="Link to section: Free space barely moved after a cleanup" data-pagefind-ignore></a>
</h3>
<p>Docker reports space inside its VM disk, and <code>du</code> and <code>uv cache size</code> count
cloned files at full size. Measure the container with <code>diskutil info /</code>
before and after; that is the only figure that says what the Mac got back.</p>
<p>With Time Machine on, a local snapshot can keep a deleted file&rsquo;s space in use.
Time Machine keeps hourly snapshots for 24 hours and deletes them as they age
or as space is needed, per <a href="https://support.apple.com/en-us/102154" rel="external noopener" target="_blank">Apple&rsquo;s Time Machine guide<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/mac-disk-full-developer-caches/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a>.
List them; on this Mac the list was empty:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">tmutil</span> <span class="console__token">listlocalsnapshots</span> <span class="console__token">/</span></span></code></pre>
    </div>
  </div></div><h3 id="uv-cache-prune-waits-then-times-out">uv cache prune waits, then times out<a class="heading-anchor" href="https://looselydecoupled.com/mac-disk-full-developer-caches/#uv-cache-prune-waits-then-times-out"
     aria-label="Link to section: uv cache prune waits, then times out" data-pagefind-ignore></a>
</h3>
<p><code>uv cache prune</code> waits while any other uv process is running, and every <code>uvx</code>
server counts. After five minutes by default (<code>UV_LOCK_TIMEOUT</code>) it gives up;
the <a href="https://docs.astral.sh/uv/concepts/cache/" rel="external noopener" target="_blank">uv cache documentation<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/mac-disk-full-developer-caches/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a> covers
the lock. Quit the coding sessions that started the servers, then run it again.
To see what holds the lock:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">lsof</span> <span class="console__token">&#34;$(uv</span> <span class="console__token">cache</span> <span class="console__token">dir)/.lock&#34;</span></span></code></pre>
    </div>
  </div></div><p><a href="https://looselydecoupled.com/uv-cache-huge-mac-uvx-mcp-servers/">Part 2</a> shows how to find which session
holds it.</p>
<h3 id="npm-cache-clean-left-npm-large">npm cache clean left ~/.npm large<a class="heading-anchor" href="https://looselydecoupled.com/mac-disk-full-developer-caches/#npm-cache-clean-left-npm-large"
     aria-label="Link to section: npm cache clean left ~/.npm large" data-pagefind-ignore></a>
</h3>
<p><code>npm cache clean --force</code> empties npm&rsquo;s package cache, <code>_cacache</code>. The space
is in <code>_npx</code>, where npx
keeps the packages it runs; part 4 covers it.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
