<?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: Temir Kussainov</title>
    <description>The latest articles on DEV Community by Temir Kussainov (@protemir).</description>
    <link>https://dev.to/protemir</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%2F4143530%2F43211e42-6857-44c4-9482-cc9ffaa1a556.JPG</url>
      <title>DEV Community: Temir Kussainov</title>
      <link>https://dev.to/protemir</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/protemir"/>
    <language>en</language>
    <item>
      <title>ModHeader in Selenium stopped working. Here's what sets HTTP headers in tests in 2026</title>
      <dc:creator>Temir Kussainov</dc:creator>
      <pubDate>Mon, 12 Oct 2026 10:00:59 +0000</pubDate>
      <link>https://dev.to/protemir/modheader-in-selenium-stopped-working-heres-what-sets-http-headers-in-tests-in-2026-1jgg</link>
      <guid>https://dev.to/protemir/modheader-in-selenium-stopped-working-heres-what-sets-http-headers-in-tests-in-2026-1jgg</guid>
      <description>&lt;p&gt;If your browser tests set headers through ModHeader (&lt;code&gt;chrome-modheader&lt;/code&gt; on npm, &lt;code&gt;modheader_selenium&lt;/code&gt; for Python and Java), they probably broke this year, and not only because ModHeader was pulled from the stores in July. Two other things happened:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The extension bundled in &lt;code&gt;chrome-modheader&lt;/code&gt; is ModHeader 4.3.2, a Manifest V2 build. Chrome turned off MV2 extensions, so it never loads, and the package hasn't been updated since.&lt;/li&gt;
&lt;li&gt;Google Chrome 137 and newer ignore &lt;code&gt;--load-extension&lt;/code&gt;, which is how ChromeDriver installs any extension you pass with &lt;code&gt;addExtensions&lt;/code&gt;. Chrome for Testing and Chromium still take it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The package still gets about 150k downloads a month, mostly from CI, so a lot of pipelines are installing something that does nothing. Here's what works now, from the simplest option up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Request headers on every request: no extension needed
&lt;/h2&gt;

&lt;p&gt;Playwright has it built in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newContext&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;extraHTTPHeaders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-Env&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;staging&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Selenium 4 with Chrome can ask the DevTools protocol:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_cdp_cmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Network.enable&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_cdp_cmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Network.setExtraHTTPHeaders&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;X-Env&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;staging&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both add the headers to every request from that context or tab, which covers most "log in as a test user" and "route to staging" cases. They don't change response headers, can't remove a header the browser sends, and don't limit headers to some URLs (in Playwright, &lt;code&gt;page.route()&lt;/code&gt; can, at the cost of handling each request in your test).&lt;/p&gt;

&lt;h2&gt;
  
  
  Response headers, removing headers, some URLs only
&lt;/h2&gt;

&lt;p&gt;That's where an extension still earns its place: CORS or CSP response headers, stripping &lt;code&gt;Referer&lt;/code&gt; or &lt;code&gt;Cookie&lt;/code&gt;, a header only for &lt;code&gt;api.example.com&lt;/code&gt;. A proxy like mitmproxy can do all of that too, but then you manage certificates, and on a cloud grid that's often not an option.&lt;/p&gt;

&lt;p&gt;Disclosure: I maintain &lt;a href="https://github.com/Protemir/headerwise" rel="noopener noreferrer"&gt;Headerwise&lt;/a&gt;, an open source (MIT) MV3 header extension, and it has an automation build for this. It has a fixed extension id, and your test sets headers by opening one URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chrome-extension://ogjbgamdhnjnagcdgboifgmhlgddcdce/automation.html?X-Env=staging&amp;amp;res:Access-Control-Allow-Origin=*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The page waits until Chrome has the rules and then reports &lt;code&gt;ready&lt;/code&gt;. Playwright, with its own Chromium:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/path/to/headerwise-automation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launchPersistentContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chromium&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`--disable-extensions-except=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`--load-extension=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chrome-extension://ogjbgamdhnjnagcdgboifgmhlgddcdce/automation.html?X-Env=staging&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForFunction&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ready&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Selenium needs Chrome for Testing, which Selenium Manager downloads when you ask for a version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ChromeOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;browser_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;stable&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--load-extension=/path/to/headerwise-automation&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Chrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  If your tests are written for ModHeader
&lt;/h2&gt;

&lt;p&gt;The automation build also answers ModHeader's own &lt;code&gt;webdriver.modheader.com/add&lt;/code&gt;, &lt;code&gt;/clear&lt;/code&gt; and &lt;code&gt;/load&lt;/code&gt; URLs (nothing is sent to modheader.com: the browser redirects them to the extension before any request leaves) and titles the page "Done", like ModHeader did. So a test like this only changes the extension it loads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getAddHeaderUrl&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chrome-modheader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;addExtensions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;headerwise-automation.crx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// was getExtension()&lt;/span&gt;
&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBrowserVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;forBrowser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chrome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setChromeOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getAddHeaderUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-Env&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;staging&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;until&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;titleIs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Done&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;.crx&lt;/code&gt; is signed, so it also works on grids that take a base64 extension in the capabilities. Ask the grid for Chrome for Testing (BrowserStack calls it &lt;code&gt;chromefortesting&lt;/code&gt;), for the same &lt;code&gt;--load-extension&lt;/code&gt; reason.&lt;/p&gt;

&lt;p&gt;Setup for Selenium, Playwright and Puppeteer: &lt;a href="https://protemir.github.io/headerwise/automation.html" rel="noopener noreferrer"&gt;https://protemir.github.io/headerwise/automation.html&lt;/a&gt;. CI runs those snippets on every change, including one test written for ModHeader with only the extension swapped.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one to pick
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Same request headers everywhere: &lt;code&gt;extraHTTPHeaders&lt;/code&gt; or CDP. Nothing to install.&lt;/li&gt;
&lt;li&gt;Response headers, removals, per-URL rules, or a lot of existing ModHeader URLs in your tests: an extension (Headerwise or whatever you trust).&lt;/li&gt;
&lt;li&gt;Firefox, or traffic outside the browser too: a proxy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If ModHeader itself got switched off in your everyday browser too, I wrote up &lt;a href="https://dev.to/protemir/modheader-got-pulled-from-the-chrome-web-store-heres-how-to-get-your-profiles-back-1mc0"&gt;how to get your profiles back&lt;/a&gt;. And if something in your test setup doesn't fit any of these, tell me in the comments.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>selenium</category>
      <category>playwright</category>
      <category>webdev</category>
    </item>
    <item>
      <title>ModHeader got pulled from the Chrome Web Store. Here's how to get your profiles back</title>
      <dc:creator>Temir Kussainov</dc:creator>
      <pubDate>Mon, 12 Oct 2026 09:58:17 +0000</pubDate>
      <link>https://dev.to/protemir/modheader-got-pulled-from-the-chrome-web-store-heres-how-to-get-your-profiles-back-1mc0</link>
      <guid>https://dev.to/protemir/modheader-got-pulled-from-the-chrome-web-store-heres-how-to-get-your-profiles-back-1mc0</guid>
      <description>&lt;p&gt;If you opened Chrome recently and ModHeader was gone, or sitting there turned off, you're not alone. Microsoft pulled it from Edge Add-ons on July 3, 2026 and Google followed on July 10. Version 7.0.18 shipped a dormant collector for the domains you visit, plus install and update pings to a third-party server (&lt;a href="https://thehackernews.com/2026/07/google-and-microsoft-pull-modheader.html" rel="noopener noreferrer"&gt;The Hacker News has the details&lt;/a&gt;). The collector reportedly never switched on, but the listings are gone either way.&lt;/p&gt;

&lt;p&gt;The annoying part: once Chrome turns an extension off, you can't open it, so the Export button is gone too. If you had a dozen profiles with tokens, URL filters and staging headers, that hurts.&lt;/p&gt;

&lt;p&gt;Your profiles are still on your disk, though. Chrome keeps every extension's &lt;code&gt;chrome.storage.local&lt;/code&gt; in a LevelDB folder under your browser profile, and ModHeader keeps everything under one key called &lt;code&gt;profiles&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the folder is
&lt;/h2&gt;

&lt;p&gt;ModHeader's id in the Chrome Web Store was &lt;code&gt;idgpnmonknjnojddfkpgkljpfnnfcklj&lt;/code&gt; (on Edge Add-ons it was &lt;code&gt;opgbiafapkbbnbnjcdomjaghbckfkglc&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Windows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%LOCALAPPDATA%\Google\Chrome\User Data\Default\Local Extension Settings\idgpnmonknjnojddfkpgkljpfnnfcklj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;macOS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/Library/Application Support/Google/Chrome/Default/Local Extension Settings/idgpnmonknjnojddfkpgkljpfnnfcklj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.config/google-chrome/Default/Local Extension Settings/idgpnmonknjnojddfkpgkljpfnnfcklj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use several Chrome profiles, open &lt;code&gt;chrome://version&lt;/code&gt; in the one where you had ModHeader and use its "Profile Path" instead of &lt;code&gt;Default&lt;/code&gt;. If the folder isn't there, Chrome may have deleted it when it removed the extension, and then there's nothing left to recover. So do this soon, and don't click "Remove" on ModHeader before you have your profiles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 1: a command line tool
&lt;/h2&gt;

&lt;p&gt;Requestly published a small CLI that finds the folder and writes the same JSON file ModHeader's own export would have: &lt;code&gt;npx @requestly/modheader-export-backup&lt;/code&gt;. You need Node 20 or newer. The file works with any tool that imports ModHeader JSON.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 2: read it in the browser
&lt;/h2&gt;

&lt;p&gt;This is partly why I built Headerwise, a free and open source header editor. Its "Move from ModHeader" page lets you pick that folder and reads the LevelDB files right in the page (I wrote a small LevelDB and snappy reader in TypeScript for it), with nothing uploaded. Headers, URL filters, site, type and method filters, URL replacements and cookie rules come over, and the profile you had selected stays on. &lt;a href="https://chromewebstore.google.com/detail/jedoaeaapdofoldmkacjbpojbnpapkna" rel="noopener noreferrer"&gt;Chrome Web Store&lt;/a&gt;, &lt;a href="https://github.com/Protemir/headerwise" rel="noopener noreferrer"&gt;source&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 3: by hand
&lt;/h2&gt;

&lt;p&gt;If you'd rather not run anything: copy the folder somewhere safe first, then open the copy with any LevelDB viewer and read the value of the &lt;code&gt;profiles&lt;/code&gt; key. It's plain JSON.&lt;/p&gt;

&lt;h2&gt;
  
  
  Whatever you pick
&lt;/h2&gt;

&lt;p&gt;Exports often hold Authorization tokens and session cookies. Treat the file like a password: don't commit it, and rotate anything sensitive you kept in ModHeader if you're not sure what happened to it.&lt;/p&gt;

&lt;p&gt;If you used ModHeader in Selenium or Playwright tests, that broke separately: its test package ships a Manifest V2 build that current Chrome won't load. &lt;a href="https://protemir.github.io/headerwise/automation.html" rel="noopener noreferrer"&gt;Here's what works now&lt;/a&gt;. And if your setup needs something none of these handle, tell me in the comments.&lt;/p&gt;

</description>
      <category>chrome</category>
      <category>webdev</category>
      <category>privacy</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Why ClickHouse® fills the disk in self-hosted Langfuse and SigNoz, and how to fix it</title>
      <dc:creator>Temir Kussainov</dc:creator>
      <pubDate>Sat, 26 Sep 2026 04:40:18 +0000</pubDate>
      <link>https://dev.to/protemir/why-clickhouser-fills-the-disk-in-self-hosted-langfuse-and-signoz-and-how-to-fix-it-3l4c</link>
      <guid>https://dev.to/protemir/why-clickhouser-fills-the-disk-in-self-hosted-langfuse-and-signoz-and-how-to-fix-it-3l4c</guid>
      <description>&lt;p&gt;&lt;em&gt;Also in &lt;a href="https://diskvet.dev/es/guide/" rel="noopener noreferrer"&gt;Español&lt;/a&gt; · &lt;a href="https://diskvet.dev/pt/guide/" rel="noopener noreferrer"&gt;Português&lt;/a&gt; · &lt;a href="https://diskvet.dev/ru/guide/" rel="noopener noreferrer"&gt;Русский&lt;/a&gt; · &lt;a href="https://diskvet.dev/ja/guide/" rel="noopener noreferrer"&gt;日本語&lt;/a&gt; · &lt;a href="https://diskvet.dev/ko/guide/" rel="noopener noreferrer"&gt;한국어&lt;/a&gt; · &lt;a href="https://diskvet.dev/zh/guide/" rel="noopener noreferrer"&gt;中文&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Your Langfuse or SigNoz server is running out of disk, but your traces are small. In most public reports the space goes to ClickHouse® itself: to its own log tables, such as &lt;code&gt;system.trace_log&lt;/code&gt; and &lt;code&gt;system.text_log&lt;/code&gt;, which have no size limit by default. ClickStack has the same problem.&lt;/p&gt;

&lt;p&gt;This guide shows how to confirm it with one read-only query, free the space now, and stop it from coming back without hitting the known traps. You don't need any extra tools.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tested on stock &lt;code&gt;clickhouse/clickhouse-server&lt;/code&gt; images 24.8, 25.12 and 26.9 (the SigNoz commands on 25.5 and 25.12), with the ClickHouse service and config taken from Langfuse's and SigNoz's compose files.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check.&lt;/strong&gt; Run one read-only query on &lt;code&gt;system.parts&lt;/code&gt;. If &lt;code&gt;system.*_log&lt;/code&gt; tables are bigger than your data, ClickHouse's own logs are the problem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free space now.&lt;/strong&gt; &lt;code&gt;TRUNCATE&lt;/code&gt; the big logs. For a table over 50 GB, add &lt;code&gt;SETTINGS max_table_size_to_drop = 0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stop it coming back.&lt;/strong&gt; Add a &lt;code&gt;config.d&lt;/code&gt; file with a TTL for each log (for &lt;code&gt;opentelemetry_span_log&lt;/code&gt; the TTL goes inside &lt;code&gt;&amp;lt;engine&amp;gt;&lt;/code&gt;), then restart ClickHouse.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean up.&lt;/strong&gt; Drop the &lt;code&gt;*_log_0&lt;/code&gt;, &lt;code&gt;*_log_1&lt;/code&gt; copies that the restart leaves behind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Still full?&lt;/strong&gt; Check Docker's container logs, deleted rows and stuck parts. On Langfuse, update Langfuse before you move ClickHouse to 26.8+.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. What is using the disk? Check ClickHouse disk usage by table
&lt;/h2&gt;

&lt;p&gt;Save this query as &lt;code&gt;size.sql&lt;/code&gt;. It lists the biggest tables by size on disk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;formatReadableSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes_on_disk&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes_on_disk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Langfuse (docker compose).&lt;/strong&gt; Run this in the folder with Langfuse's &lt;code&gt;docker-compose.yml&lt;/code&gt;. Its compose file already sets &lt;code&gt;CLICKHOUSE_USER&lt;/code&gt; and &lt;code&gt;CLICKHOUSE_PASSWORD&lt;/code&gt; inside the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; clickhouse sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'clickhouse-client --user "$CLICKHOUSE_USER" --password "$CLICKHOUSE_PASSWORD" --readonly=1 --format PrettyCompact'&lt;/span&gt; &amp;lt; size.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;SigNoz (docker compose).&lt;/strong&gt; The container is usually called &lt;code&gt;signoz-clickhouse&lt;/code&gt; (check with &lt;code&gt;docker ps&lt;/code&gt;). SigNoz's &lt;code&gt;users.xml&lt;/code&gt; leaves the &lt;code&gt;default&lt;/code&gt; user without a password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; signoz-clickhouse clickhouse-client &lt;span class="nt"&gt;--readonly&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nt"&gt;--format&lt;/span&gt; PrettyCompact &amp;lt; size.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--readonly=1&lt;/code&gt; makes the session read-only: ClickHouse refuses any change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to read it.&lt;/strong&gt; Compare the &lt;code&gt;system&lt;/code&gt; rows with your own data (&lt;code&gt;default&lt;/code&gt; for Langfuse, &lt;code&gt;signoz_*&lt;/code&gt; for SigNoz). If &lt;code&gt;system.trace_log&lt;/code&gt; or &lt;code&gt;system.text_log&lt;/code&gt; is at the top, go on to section 2.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. How do I free the disk space right now?
&lt;/h2&gt;

&lt;p&gt;Open a client with write access:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Langfuse&lt;/span&gt;
docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;clickhouse sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'clickhouse-client --user "$CLICKHOUSE_USER" --password "$CLICKHOUSE_PASSWORD"'&lt;/span&gt;
&lt;span class="c"&gt;# SigNoz&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; signoz-clickhouse clickhouse-client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Empty the big logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;TRUNCATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trace_log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;TRUNCATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text_log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;TRUNCATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metric_log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;TRUNCATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This deletes only ClickHouse's own diagnostics, not your traces, and needs no restart.&lt;/p&gt;

&lt;p&gt;Langfuse reads &lt;code&gt;system.query_log&lt;/code&gt; to follow some of its own queries (&lt;a href="https://github.com/langfuse/langfuse/issues/13123" rel="noopener noreferrer"&gt;#13123&lt;/a&gt;). So empty it, but don't switch it off.&lt;/p&gt;

&lt;h3&gt;
  
  
  TRUNCATE fails with Code 359? Tables over 50 GB
&lt;/h3&gt;

&lt;p&gt;ClickHouse won't drop or truncate a table bigger than &lt;code&gt;max_table_size_to_drop&lt;/code&gt; (default 50,000,000,000 bytes, about 46.6 GiB). The limit applies to &lt;code&gt;TRUNCATE&lt;/code&gt; too, and a bigger table fails with &lt;code&gt;Code: 359 ... (TABLE_SIZE_EXCEEDS_MAX_DROP_SIZE_LIMIT)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Big logs do hit it: the &lt;code&gt;text_log&lt;/code&gt; in &lt;a href="https://github.com/orgs/langfuse/discussions/15024" rel="noopener noreferrer"&gt;Langfuse discussion #15024&lt;/a&gt; was 59.20 GiB. There are two ways around it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A.&lt;/strong&gt; Lift the limit for one statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- ClickHouse 23.12+: lift the limit for this one statement&lt;/span&gt;
&lt;span class="k"&gt;TRUNCATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text_log&lt;/span&gt; &lt;span class="n"&gt;SETTINGS&lt;/span&gt; &lt;span class="n"&gt;max_table_size_to_drop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option B.&lt;/strong&gt; Create the one-time flag file, then run the plain &lt;code&gt;TRUNCATE&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;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;clickhouse sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'touch /var/lib/clickhouse/flags/force_drop_table &amp;amp;&amp;amp; chmod 666 /var/lib/clickhouse/flags/force_drop_table'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(SigNoz: &lt;code&gt;docker exec signoz-clickhouse sh -c '...'&lt;/code&gt; with the same command.) ClickHouse deletes the flag after the first &lt;code&gt;DROP&lt;/code&gt; or &lt;code&gt;TRUNCATE&lt;/code&gt; that needs it, so create it again before the next big table.&lt;/p&gt;

&lt;p&gt;The space is back now, but the logs start growing again right away. Section 4 stops that. Section 3 explains why it happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Why are trace_log and text_log so big?
&lt;/h2&gt;

&lt;p&gt;ClickHouse writes its own diagnostics into tables in the &lt;code&gt;system&lt;/code&gt; database: &lt;code&gt;query_log&lt;/code&gt;, &lt;code&gt;trace_log&lt;/code&gt;, &lt;code&gt;text_log&lt;/code&gt;, &lt;code&gt;metric_log&lt;/code&gt; and more. The &lt;a href="https://clickhouse.com/docs/reference/system-tables/overview" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; says it plainly: "By default, table growth is unlimited."&lt;/p&gt;

&lt;p&gt;Recent &lt;a href="https://github.com/ClickHouse/ClickHouse/blob/master/programs/server/config.xml" rel="noopener noreferrer"&gt;default configs&lt;/a&gt; set a TTL only for a few small logs (since 25.9, &lt;code&gt;processors_profile_log&lt;/code&gt; keeps 30 days), not for the big ones. Two defaults make the big ones grow fast:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the query profiler is on and writes stack samples of running queries into &lt;code&gt;trace_log&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;text_log&lt;/code&gt; stores the server log at &lt;code&gt;trace&lt;/code&gt; level.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Public examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Langfuse &lt;a href="https://github.com/langfuse/langfuse/issues/13123" rel="noopener noreferrer"&gt;#13123&lt;/a&gt;:&lt;/strong&gt; &lt;code&gt;system.trace_log&lt;/code&gt; was 66.86 GiB, while the Langfuse tables listed there (&lt;code&gt;traces&lt;/code&gt;, &lt;code&gt;observations&lt;/code&gt;, &lt;code&gt;scores&lt;/code&gt;) took about 51 MiB together. The maintainers decided not to override ClickHouse's defaults and added a &lt;a href="https://langfuse.com/faq/all/reduce-clickhouse-disk-size" rel="noopener noreferrer"&gt;FAQ page&lt;/a&gt; instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SigNoz &lt;a href="https://github.com/SigNoz/signoz/issues/12050" rel="noopener noreferrer"&gt;#12050&lt;/a&gt;:&lt;/strong&gt; more than 80 GB of system logs (including a &lt;code&gt;trace_log_0&lt;/code&gt; copy) next to less than 500 MB of telemetry. Truncating the &lt;code&gt;system.*&lt;/code&gt; tables freed about 80 GB. SigNoz's new Foundry installer sets TTLs; older docker compose installs don't have them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ClickStack Helm chart &lt;a href="https://github.com/ClickHouse/ClickStack-helm-charts/pull/275" rel="noopener noreferrer"&gt;PR #275&lt;/a&gt;:&lt;/strong&gt; a 10 Gi volume hit 100% after 10 days, with 5.2 GB of trace-level server logs and a 3.7 GB &lt;code&gt;system.text_log&lt;/code&gt; against ~70 MB of telemetry. The PR, merged in September 2026, adds a 7-day TTL.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. How do I set a TTL on ClickHouse system logs?
&lt;/h2&gt;

&lt;p&gt;A TTL makes ClickHouse delete old log rows by itself. Set it &lt;strong&gt;after&lt;/strong&gt; the &lt;code&gt;TRUNCATE&lt;/code&gt; from section 2, because of how ClickHouse applies it.&lt;/p&gt;

&lt;p&gt;When you add a TTL and restart, ClickHouse doesn't shrink the old table. It renames it to &lt;code&gt;trace_log_0&lt;/code&gt; with all its rows and creates a new one with the TTL. If you truncated first, these copies are tiny, and step 4 drops them.&lt;/p&gt;

&lt;p&gt;Run the SQL below in the same write-access client as in section 2.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: which logs have no TTL?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;formatReadableSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="k"&gt;extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;engine_full&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'TTL [^S]*'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'system'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%MergeTree'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'_log(_[0-9]+)?$'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;total_bytes&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An empty &lt;code&gt;ttl&lt;/code&gt; means the log is kept forever. Names like &lt;code&gt;trace_log_0&lt;/code&gt; are old copies; step 4 removes them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: add a TTL file in config.d
&lt;/h3&gt;

&lt;p&gt;Save this as &lt;code&gt;clickhouse-ttl.xml&lt;/code&gt; next to &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;clickhouse&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Keep 7 days of ClickHouse's own logs. List only logs your server has. --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;query_log&amp;gt;&amp;lt;ttl&amp;gt;&lt;/span&gt;event_date + INTERVAL 7 DAY DELETE&lt;span class="nt"&gt;&amp;lt;/ttl&amp;gt;&amp;lt;/query_log&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;trace_log&amp;gt;&amp;lt;ttl&amp;gt;&lt;/span&gt;event_date + INTERVAL 7 DAY DELETE&lt;span class="nt"&gt;&amp;lt;/ttl&amp;gt;&amp;lt;/trace_log&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;text_log&amp;gt;&amp;lt;ttl&amp;gt;&lt;/span&gt;event_date + INTERVAL 7 DAY DELETE&lt;span class="nt"&gt;&amp;lt;/ttl&amp;gt;&amp;lt;/text_log&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;metric_log&amp;gt;&amp;lt;ttl&amp;gt;&lt;/span&gt;event_date + INTERVAL 7 DAY DELETE&lt;span class="nt"&gt;&amp;lt;/ttl&amp;gt;&amp;lt;/metric_log&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;asynchronous_metric_log&amp;gt;&amp;lt;ttl&amp;gt;&lt;/span&gt;event_date + INTERVAL 7 DAY DELETE&lt;span class="nt"&gt;&amp;lt;/ttl&amp;gt;&amp;lt;/asynchronous_metric_log&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;part_log&amp;gt;&amp;lt;ttl&amp;gt;&lt;/span&gt;event_date + INTERVAL 7 DAY DELETE&lt;span class="nt"&gt;&amp;lt;/ttl&amp;gt;&amp;lt;/part_log&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- The stock config defines this log with &amp;lt;engine&amp;gt;, so a separate &amp;lt;ttl&amp;gt;
         stops the server from starting (ClickHouse#88366). TTL goes inside. --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;opentelemetry_span_log&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;engine&amp;gt;&lt;/span&gt;ENGINE = MergeTree PARTITION BY toYYYYMM(finish_date) ORDER BY (finish_date, finish_time_us) TTL finish_date + INTERVAL 7 DAY DELETE&lt;span class="nt"&gt;&amp;lt;/engine&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/opentelemetry_span_log&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/clickhouse&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mount it into the &lt;code&gt;clickhouse&lt;/code&gt; service (keep your existing volume lines):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;clickhouse&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./clickhouse-ttl.xml:/etc/clickhouse-server/config.d/clickhouse-ttl.xml:ro&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three traps in this step:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;opentelemetry_span_log&lt;/code&gt; takes its TTL inside &lt;code&gt;&amp;lt;engine&amp;gt;&lt;/code&gt;.&lt;/strong&gt; The stock config defines this log with &lt;code&gt;&amp;lt;engine&amp;gt;&lt;/code&gt;, and per the &lt;a href="https://clickhouse.com/docs/reference/system-tables/overview" rel="noopener noreferrer"&gt;docs&lt;/a&gt; that conflicts with &lt;code&gt;&amp;lt;ttl&amp;gt;&lt;/code&gt;. The server then exits with &lt;code&gt;If 'engine' is specified for system table, TTL parameters should be specified directly inside 'engine'&lt;/code&gt; (&lt;a href="https://github.com/ClickHouse/ClickHouse/issues/88366" rel="noopener noreferrer"&gt;ClickHouse #88366&lt;/a&gt;). Copy &lt;code&gt;PARTITION BY&lt;/code&gt; and &lt;code&gt;ORDER BY&lt;/code&gt; from &lt;code&gt;SELECT engine_full FROM system.tables WHERE database = 'system' AND name = 'opentelemetry_span_log'&lt;/code&gt; (SigNoz's config also sorts by &lt;code&gt;trace_id&lt;/code&gt;). Or switch the log off with &lt;code&gt;&amp;lt;opentelemetry_span_log remove="1"/&amp;gt;&lt;/code&gt; (Langfuse's &lt;a href="https://langfuse.com/self-hosting/configuration/scaling" rel="noopener noreferrer"&gt;scaling docs&lt;/a&gt;). &lt;code&gt;remove&lt;/code&gt; doesn't delete the existing table, so drop it yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;List only the logs your server has.&lt;/strong&gt; A section in the config is what turns a log on (the stock &lt;code&gt;config.xml&lt;/code&gt; disables &lt;code&gt;session_log&lt;/code&gt; by commenting it out). SigNoz's own &lt;a href="https://github.com/SigNoz/signoz/blob/v0.129.0/deploy/common/clickhouse/config.xml" rel="noopener noreferrer"&gt;&lt;code&gt;config.xml&lt;/code&gt;&lt;/a&gt; doesn't enable &lt;code&gt;text_log&lt;/code&gt;, so delete that line there. Also add &lt;code&gt;processors_profile_log&lt;/code&gt;, which has no TTL in that file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Profile settings go into users.d, not config.d.&lt;/strong&gt; If you also turn off the query profiler (&lt;code&gt;query_profiler_real_time_period_ns&lt;/code&gt; and &lt;code&gt;query_profiler_cpu_time_period_ns&lt;/code&gt; = 0, as in Langfuse's scaling docs), put that &lt;code&gt;&amp;lt;profiles&amp;gt;&lt;/code&gt; block into &lt;code&gt;users.d/&lt;/code&gt;. In &lt;code&gt;config.d/&lt;/code&gt; it is silently ignored (&lt;a href="https://github.com/triggerdotdev/trigger.dev/issues/4343" rel="noopener noreferrer"&gt;trigger.dev #4343&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: restart ClickHouse
&lt;/h3&gt;

&lt;p&gt;The TTL from &lt;code&gt;config.d&lt;/code&gt; takes effect only when the server restarts (&lt;a href="https://kb.altinity.com/altinity-kb-setup-and-maintenance/altinity-kb-system-tables-eat-my-disk/" rel="noopener noreferrer"&gt;Altinity KB&lt;/a&gt;). A config reload is not enough. With compose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt; clickhouse     &lt;span class="c"&gt;# recreates the container with the new mount&lt;/span&gt;
docker compose ps clickhouse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If ClickHouse keeps restarting, the reason is in its error log inside the container. &lt;code&gt;docker compose logs&lt;/code&gt; may not show it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker compose ps &lt;span class="nt"&gt;-aq&lt;/span&gt; clickhouse&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:/var/log/clickhouse-server/clickhouse-server.err.log &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 20 clickhouse-server.err.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: drop the old trace_log_0 copies
&lt;/h3&gt;

&lt;p&gt;After the restart, every changed log has a copy like &lt;code&gt;trace_log_0&lt;/code&gt; (then &lt;code&gt;_1&lt;/code&gt;, &lt;code&gt;_2&lt;/code&gt; after later changes). The copy keeps all the old rows and has no TTL (ClickStack PR #275 upgrade note, &lt;a href="https://github.com/getsentry/snuba/issues/7311" rel="noopener noreferrer"&gt;Sentry snuba #7311&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This query writes the &lt;code&gt;DROP&lt;/code&gt; statements for you. Read them, then paste them into the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="s1"&gt;'DROP TABLE system.'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;' SETTINGS max_table_size_to_drop = 0;'&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'system'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'_log_[0-9]+$'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ClickHouse no longer writes to these tables. &lt;code&gt;DROP&lt;/code&gt; can't be undone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: check the result
&lt;/h3&gt;

&lt;p&gt;Run the step 1 query again. Every big log should show a TTL, and no &lt;code&gt;_N&lt;/code&gt; names should be left.&lt;/p&gt;

&lt;p&gt;ClickHouse removes expired rows during merges, by default at most every 4 hours (&lt;a href="https://clickhouse.com/docs/reference/settings/merge-tree-settings/merge-with#merge_with_ttl_timeout" rel="noopener noreferrer"&gt;&lt;code&gt;merge_with_ttl_timeout&lt;/code&gt;&lt;/a&gt;), so the size shrinks over the next hours.&lt;/p&gt;

&lt;p&gt;A script can do the checking for you: &lt;a href="https://github.com/Protemir/diskvet" rel="noopener noreferrer"&gt;diskvet&lt;/a&gt; (my project) runs the step 1 check read-only and prints the &lt;code&gt;TRUNCATE&lt;/code&gt; commands, the TTL file and the &lt;code&gt;DROP&lt;/code&gt; list for your tables, with the flag for tables over the drop limit (see section 7).&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Disk still full? Where else the space goes
&lt;/h2&gt;

&lt;p&gt;Compare &lt;code&gt;df -h&lt;/code&gt; with the total size of all parts (&lt;code&gt;SELECT formatReadableSize(sum(bytes_on_disk)) FROM system.parts&lt;/code&gt;). If the disk holds much more than that, the space is outside ClickHouse's tables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are Docker container logs filling the disk?
&lt;/h3&gt;

&lt;p&gt;Docker's default &lt;code&gt;json-file&lt;/code&gt; log driver has no size limit (&lt;code&gt;max-size&lt;/code&gt; defaults to &lt;code&gt;-1&lt;/code&gt;, see the &lt;a href="https://docs.docker.com/engine/logging/drivers/json-file/" rel="noopener noreferrer"&gt;Docker docs&lt;/a&gt;). In &lt;a href="https://github.com/langfuse/langfuse/issues/16339" rel="noopener noreferrer"&gt;Langfuse #16339&lt;/a&gt; the ClickHouse container's log reached 89.1 GiB and filled a 244 GiB root disk.&lt;/p&gt;

&lt;p&gt;Find the biggest ones:&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;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'du -h /var/lib/docker/containers/*/*-json.log | sort -h | tail -5'&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--no-trunc&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.ID}} {{.Names}}'&lt;/span&gt;   &lt;span class="c"&gt;# the folder name is the container ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To fix it, add a &lt;code&gt;logging:&lt;/code&gt; block to every service in &lt;code&gt;docker-compose.yml&lt;/code&gt;. In #16339 the Langfuse maintainers recommended doing it for all containers, not only ClickHouse. SigNoz's &lt;a href="https://github.com/SigNoz/signoz/blob/v0.129.0/deploy/docker/docker-compose.yaml" rel="noopener noreferrer"&gt;compose file&lt;/a&gt; already sets &lt;code&gt;50m&lt;/code&gt; × 3.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;json-file&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;50m"&lt;/span&gt;
        &lt;span class="na"&gt;max-file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run &lt;code&gt;docker compose up -d --force-recreate&lt;/code&gt;: log options apply only to newly created containers. Recreating also removes the old log file; in #16339 that freed 81 GiB.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;If your Docker daemon uses another logging driver, set retention there instead, because &lt;code&gt;driver: json-file&lt;/code&gt; overrides it.&lt;/li&gt;
&lt;li&gt;Langfuse's README (&lt;a href="https://github.com/langfuse/langfuse/pull/16363" rel="noopener noreferrer"&gt;PR #16363&lt;/a&gt;) describes the daemon-wide way: set &lt;code&gt;max-size&lt;/code&gt; and &lt;code&gt;max-file&lt;/code&gt; as daemon defaults, then restart Docker and recreate the containers. It also advises against truncating or rotating these files with external tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Are ClickHouse's own log files big?
&lt;/h3&gt;

&lt;p&gt;ClickHouse also writes plain log files to &lt;code&gt;/var/log/clickhouse-server&lt;/code&gt; (a volume in Langfuse's compose). By default they are at &lt;code&gt;trace&lt;/code&gt; level, rotated at 1000M, with up to 10 old files kept (&lt;a href="https://clickhouse.com/docs/reference/settings/server-settings/settings/other#logger" rel="noopener noreferrer"&gt;&lt;code&gt;logger&lt;/code&gt; docs&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;ClickStack PR #275 uses the values below. Save them as another &lt;code&gt;config.d&lt;/code&gt; file, mount it like the TTL file and restart ClickHouse (section 4, step 3):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;clickhouse&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;logger&amp;gt;&amp;lt;level&amp;gt;&lt;/span&gt;information&lt;span class="nt"&gt;&amp;lt;/level&amp;gt;&amp;lt;size&amp;gt;&lt;/span&gt;100M&lt;span class="nt"&gt;&amp;lt;/size&amp;gt;&amp;lt;count&amp;gt;&lt;/span&gt;10&lt;span class="nt"&gt;&amp;lt;/count&amp;gt;&amp;lt;/logger&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/clickhouse&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why do deleted rows still take space?
&lt;/h3&gt;

&lt;p&gt;Langfuse's data retention removes old rows with a &lt;a href="https://clickhouse.com/docs/reference/statements/delete" rel="noopener noreferrer"&gt;lightweight &lt;code&gt;DELETE&lt;/code&gt;&lt;/a&gt;. ClickHouse only marks such rows; the space comes back when the part is merged.&lt;/p&gt;

&lt;p&gt;Background merges skip parts over &lt;a href="https://clickhouse.com/docs/reference/settings/merge-tree-settings/max-bytes#max_bytes_to_merge_at_max_space_in_pool" rel="noopener noreferrer"&gt;&lt;code&gt;max_bytes_to_merge_at_max_space_in_pool&lt;/code&gt;&lt;/a&gt; (150 GiB), and old monthly partitions are rarely merged. In &lt;a href="https://github.com/orgs/langfuse/discussions/13969" rel="noopener noreferrer"&gt;Langfuse discussion #13969&lt;/a&gt; a May partition of ~802 GiB was ~85% deleted rows.&lt;/p&gt;

&lt;p&gt;Find the partitions with deleted rows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;partition_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;formatReadableSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes_on_disk&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;has_lightweight_delete&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;partition_id&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes_on_disk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then apply the delete mask, one partition at a time (&lt;a href="https://clickhouse.com/docs/reference/statements/alter/apply-deleted-mask" rel="noopener noreferrer"&gt;APPLY DELETED MASK&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;observations&lt;/span&gt; &lt;span class="n"&gt;APPLY&lt;/span&gt; &lt;span class="n"&gt;DELETED&lt;/span&gt; &lt;span class="n"&gt;MASK&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt; &lt;span class="s1"&gt;'202605'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Careful:&lt;/strong&gt; this is a heavyweight mutation. It rewrites the affected parts, so it needs free space for the new copies and loads the disk. Run it off-peak and watch it with &lt;code&gt;SELECT * FROM system.mutations WHERE NOT is_done&lt;/code&gt;. In #13969 it freed ~360 GiB per replica in ~17 minutes.&lt;/p&gt;

&lt;p&gt;Since v3.179.0 the Langfuse worker can do this on a schedule: &lt;code&gt;LANGFUSE_CLICKHOUSE_DELETED_MASK_CLEANER_ENABLED=true&lt;/code&gt; (off by default; see &lt;code&gt;.env.prod.example&lt;/code&gt;, &lt;a href="https://github.com/langfuse/langfuse/pull/14035" rel="noopener noreferrer"&gt;PR #14035&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Are old parts stuck on disk? (inactive and detached parts)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;formatReadableSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes_on_disk&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="k"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;remove_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;oldest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;refcount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;max_refcount&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes_on_disk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inactive parts are left over from merges. ClickHouse deletes them after &lt;a href="https://clickhouse.com/docs/reference/settings/merge-tree-settings/other#old_parts_lifetime" rel="noopener noreferrer"&gt;&lt;code&gt;old_parts_lifetime&lt;/code&gt;&lt;/a&gt; (480 s), once no query uses them (&lt;code&gt;refcount&lt;/code&gt; = 1).&lt;/p&gt;

&lt;p&gt;If they pile up (112.55 GiB of them in Langfuse discussion #15024) and &lt;code&gt;oldest&lt;/code&gt; is hours ago, look for long queries in &lt;code&gt;system.processes&lt;/code&gt; and stop them with &lt;code&gt;KILL QUERY WHERE query_id = '...'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Detached parts stay on disk until you remove them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;formatReadableSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes_on_disk&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;detached_parts&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check &lt;code&gt;reason&lt;/code&gt; first. Remove a part with &lt;code&gt;ALTER TABLE &amp;lt;db&amp;gt;.&amp;lt;table&amp;gt; DROP DETACHED PART '&amp;lt;name&amp;gt;' SETTINGS allow_drop_detached = 1&lt;/code&gt;, not by deleting folders.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Upgrading ClickHouse? Check this first
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Langfuse + ClickHouse 26.8 or newer: update Langfuse first.&lt;/strong&gt; ClickHouse 26.8 changed the default of &lt;code&gt;input_format_read_datetime_number_as_raw_value&lt;/code&gt; from 1 to 0. Older Langfuse sends &lt;code&gt;DateTime64&lt;/code&gt; values as millisecond numbers, and on 26.8+ they end up stored as &lt;code&gt;9999-12-31 23:59:59&lt;/code&gt; (&lt;a href="https://github.com/langfuse/langfuse/issues/16858" rel="noopener noreferrer"&gt;Langfuse #16858&lt;/a&gt;: 0.59% to 21.43% of writes per project). The fix is &lt;a href="https://github.com/langfuse/langfuse/pull/16892" rel="noopener noreferrer"&gt;PR #16892&lt;/a&gt; (backported to v3 in &lt;a href="https://github.com/langfuse/langfuse/pull/16957" rel="noopener noreferrer"&gt;PR #16957&lt;/a&gt;), released in Langfuse v4.28.0 and v3.225.7. Langfuse partitions by month, so such rows land in partition &lt;code&gt;999912&lt;/code&gt;. Check your version and look for them:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;version&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;partition_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;partition_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'9999'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;partition_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;After any upgrade, run the step 1 query from section 4 again.&lt;/strong&gt; When a release changes a log's schema, ClickHouse renames the current table and creates a new one, so new &lt;code&gt;_N&lt;/code&gt; copies appear. Drop them as in step 4.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Check everything at once
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Protemir/diskvet" rel="noopener noreferrer"&gt;diskvet&lt;/a&gt; (my project) is an open-source (Apache-2.0) read-only script that runs most of the checks above and prints a report with the fix commands. It can't see Docker's log files, but it shows how much of the disk is outside ClickHouse's parts.&lt;/p&gt;

&lt;p&gt;On the machine with Langfuse's &lt;code&gt;docker-compose.yml&lt;/code&gt; (read &lt;code&gt;checks.sql&lt;/code&gt; before you run it):&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;-fsSLO&lt;/span&gt; https://github.com/Protemir/diskvet/releases/latest/download/diskvet.sh
curl &lt;span class="nt"&gt;-fsSLO&lt;/span&gt; https://github.com/Protemir/diskvet/releases/latest/download/checks.sql
sh diskvet.sh report &lt;span class="nt"&gt;--docker&lt;/span&gt; auto &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; report.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For SigNoz, use &lt;code&gt;--docker signoz-clickhouse&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It reads only metadata from &lt;code&gt;system.*&lt;/code&gt; tables (&lt;code&gt;system.tables&lt;/code&gt;, &lt;code&gt;system.parts&lt;/code&gt;, &lt;code&gt;system.disks&lt;/code&gt;, &lt;code&gt;system.detached_parts&lt;/code&gt;, &lt;code&gt;system.mutations&lt;/code&gt;, &lt;code&gt;system.part_log&lt;/code&gt; and a few more), with &lt;code&gt;readonly=2&lt;/code&gt; and resource limits. It never reads rows of your tables, &lt;code&gt;system.query_log&lt;/code&gt; or query texts, and sends nothing anywhere. Nothing runs by itself: you read each fix and run it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;ClickHouse documentation and source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System tables overview (unlimited growth, &lt;code&gt;&amp;lt;engine&amp;gt;&lt;/code&gt; vs &lt;code&gt;&amp;lt;ttl&amp;gt;&lt;/code&gt;, renaming on schema change): &lt;a href="https://clickhouse.com/docs/reference/system-tables/overview" rel="noopener noreferrer"&gt;https://clickhouse.com/docs/reference/system-tables/overview&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Default &lt;code&gt;config.xml&lt;/code&gt; (logs without TTL, &lt;code&gt;opentelemetry_span_log&lt;/code&gt; engine, &lt;code&gt;text_log&lt;/code&gt; and logger at &lt;code&gt;trace&lt;/code&gt;, &lt;code&gt;session_log&lt;/code&gt; commented out): &lt;a href="https://github.com/ClickHouse/ClickHouse/blob/master/programs/server/config.xml" rel="noopener noreferrer"&gt;https://github.com/ClickHouse/ClickHouse/blob/master/programs/server/config.xml&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max_table_size_to_drop&lt;/code&gt;, server setting and &lt;code&gt;force_drop_table&lt;/code&gt;: &lt;a href="https://clickhouse.com/docs/reference/settings/server-settings/settings/max-table#max_table_size_to_drop" rel="noopener noreferrer"&gt;https://clickhouse.com/docs/reference/settings/server-settings/settings/max-table#max_table_size_to_drop&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max_table_size_to_drop&lt;/code&gt;, query setting: &lt;a href="https://clickhouse.com/docs/reference/settings/session-settings/max#max_table_size_to_drop" rel="noopener noreferrer"&gt;https://clickhouse.com/docs/reference/settings/session-settings/max#max_table_size_to_drop&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Query-level override since 23.12: &lt;a href="https://github.com/ClickHouse/ClickHouse/pull/57452" rel="noopener noreferrer"&gt;https://github.com/ClickHouse/ClickHouse/pull/57452&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The flag is removed after use (&lt;code&gt;checkCanBeDropped&lt;/code&gt;): &lt;a href="https://github.com/ClickHouse/ClickHouse/blob/master/src/Interpreters/Context.cpp" rel="noopener noreferrer"&gt;https://github.com/ClickHouse/ClickHouse/blob/master/src/Interpreters/Context.cpp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;TRUNCATE: &lt;a href="https://clickhouse.com/docs/reference/statements/truncate" rel="noopener noreferrer"&gt;https://clickhouse.com/docs/reference/statements/truncate&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;opentelemetry_span_log&lt;/code&gt; TTL error: &lt;a href="https://github.com/ClickHouse/ClickHouse/issues/88366" rel="noopener noreferrer"&gt;https://github.com/ClickHouse/ClickHouse/issues/88366&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MergeTree settings: &lt;code&gt;merge_with_ttl_timeout&lt;/code&gt; (14400 s) &lt;a href="https://clickhouse.com/docs/reference/settings/merge-tree-settings/merge-with#merge_with_ttl_timeout" rel="noopener noreferrer"&gt;https://clickhouse.com/docs/reference/settings/merge-tree-settings/merge-with#merge_with_ttl_timeout&lt;/a&gt;, &lt;code&gt;old_parts_lifetime&lt;/code&gt; (480 s) &lt;a href="https://clickhouse.com/docs/reference/settings/merge-tree-settings/other#old_parts_lifetime" rel="noopener noreferrer"&gt;https://clickhouse.com/docs/reference/settings/merge-tree-settings/other#old_parts_lifetime&lt;/a&gt;, &lt;code&gt;max_bytes_to_merge_at_max_space_in_pool&lt;/code&gt; (150 GiB) &lt;a href="https://clickhouse.com/docs/reference/settings/merge-tree-settings/max-bytes#max_bytes_to_merge_at_max_space_in_pool" rel="noopener noreferrer"&gt;https://clickhouse.com/docs/reference/settings/merge-tree-settings/max-bytes#max_bytes_to_merge_at_max_space_in_pool&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Server log rotation (&lt;code&gt;logger&lt;/code&gt;: &lt;code&gt;level&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt;, &lt;code&gt;count&lt;/code&gt;): &lt;a href="https://clickhouse.com/docs/reference/settings/server-settings/settings/other#logger" rel="noopener noreferrer"&gt;https://clickhouse.com/docs/reference/settings/server-settings/settings/other#logger&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Lightweight DELETE: &lt;a href="https://clickhouse.com/docs/reference/statements/delete" rel="noopener noreferrer"&gt;https://clickhouse.com/docs/reference/statements/delete&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;APPLY DELETED MASK: &lt;a href="https://clickhouse.com/docs/reference/statements/alter/apply-deleted-mask" rel="noopener noreferrer"&gt;https://clickhouse.com/docs/reference/statements/alter/apply-deleted-mask&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;system.parts: &lt;a href="https://clickhouse.com/docs/reference/system-tables/parts" rel="noopener noreferrer"&gt;https://clickhouse.com/docs/reference/system-tables/parts&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;system.detached_parts: &lt;a href="https://clickhouse.com/docs/reference/system-tables/detached_parts" rel="noopener noreferrer"&gt;https://clickhouse.com/docs/reference/system-tables/detached_parts&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DROP DETACHED PART: &lt;a href="https://clickhouse.com/docs/reference/statements/alter/partition" rel="noopener noreferrer"&gt;https://clickhouse.com/docs/reference/statements/alter/partition&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;allow_drop_detached&lt;/code&gt;: &lt;a href="https://clickhouse.com/docs/reference/settings/session-settings/allow#allow_drop_detached" rel="noopener noreferrer"&gt;https://clickhouse.com/docs/reference/settings/session-settings/allow#allow_drop_detached&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;system.mutations: &lt;a href="https://clickhouse.com/docs/reference/system-tables/mutations" rel="noopener noreferrer"&gt;https://clickhouse.com/docs/reference/system-tables/mutations&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;KILL QUERY: &lt;a href="https://clickhouse.com/docs/reference/statements/kill" rel="noopener noreferrer"&gt;https://clickhouse.com/docs/reference/statements/kill&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Altinity KB, "System tables ate my disk" (restart needed): &lt;a href="https://kb.altinity.com/altinity-kb-setup-and-maintenance/altinity-kb-system-tables-eat-my-disk/" rel="noopener noreferrer"&gt;https://kb.altinity.com/altinity-kb-setup-and-maintenance/altinity-kb-system-tables-eat-my-disk/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Docker:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;json-file logging driver (&lt;code&gt;max-size&lt;/code&gt; defaults to -1; existing containers keep old settings): &lt;a href="https://docs.docker.com/engine/logging/drivers/json-file/" rel="noopener noreferrer"&gt;https://docs.docker.com/engine/logging/drivers/json-file/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Langfuse:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FAQ, reduce ClickHouse disk size: &lt;a href="https://langfuse.com/faq/all/reduce-clickhouse-disk-size" rel="noopener noreferrer"&gt;https://langfuse.com/faq/all/reduce-clickhouse-disk-size&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Scaling docs, ClickHouse system log tables: &lt;a href="https://langfuse.com/self-hosting/configuration/scaling" rel="noopener noreferrer"&gt;https://langfuse.com/self-hosting/configuration/scaling&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;#13123, system tables grow unbounded: &lt;a href="https://github.com/langfuse/langfuse/issues/13123" rel="noopener noreferrer"&gt;https://github.com/langfuse/langfuse/issues/13123&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;#16339, unbounded Docker log: &lt;a href="https://github.com/langfuse/langfuse/issues/16339" rel="noopener noreferrer"&gt;https://github.com/langfuse/langfuse/issues/16339&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PR #16363, Docker log rotation in the README (daemon defaults, no external truncation): &lt;a href="https://github.com/langfuse/langfuse/pull/16363" rel="noopener noreferrer"&gt;https://github.com/langfuse/langfuse/pull/16363&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Discussion #13969, lightweight-deleted rows: &lt;a href="https://github.com/orgs/langfuse/discussions/13969" rel="noopener noreferrer"&gt;https://github.com/orgs/langfuse/discussions/13969&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Discussion #15024, inactive parts and 59 GiB text_log: &lt;a href="https://github.com/orgs/langfuse/discussions/15024" rel="noopener noreferrer"&gt;https://github.com/orgs/langfuse/discussions/15024&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PR #14035, deleted-mask cleaner (released in v3.179.0): &lt;a href="https://github.com/langfuse/langfuse/pull/14035" rel="noopener noreferrer"&gt;https://github.com/langfuse/langfuse/pull/14035&lt;/a&gt;, &lt;a href="https://github.com/langfuse/langfuse/releases/tag/v3.179.0" rel="noopener noreferrer"&gt;https://github.com/langfuse/langfuse/releases/tag/v3.179.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;#16858, DateTime64 on ClickHouse 26.8+: &lt;a href="https://github.com/langfuse/langfuse/issues/16858" rel="noopener noreferrer"&gt;https://github.com/langfuse/langfuse/issues/16858&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PR #16892 and v3 backport PR #16957: &lt;a href="https://github.com/langfuse/langfuse/pull/16892" rel="noopener noreferrer"&gt;https://github.com/langfuse/langfuse/pull/16892&lt;/a&gt;, &lt;a href="https://github.com/langfuse/langfuse/pull/16957" rel="noopener noreferrer"&gt;https://github.com/langfuse/langfuse/pull/16957&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Releases with the fix: &lt;a href="https://github.com/langfuse/langfuse/releases/tag/v4.28.0" rel="noopener noreferrer"&gt;https://github.com/langfuse/langfuse/releases/tag/v4.28.0&lt;/a&gt;, &lt;a href="https://github.com/langfuse/langfuse/releases/tag/v3.225.7" rel="noopener noreferrer"&gt;https://github.com/langfuse/langfuse/releases/tag/v3.225.7&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SigNoz, ClickStack and others:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SigNoz #12050, 80+ GB of system logs: &lt;a href="https://github.com/SigNoz/signoz/issues/12050" rel="noopener noreferrer"&gt;https://github.com/SigNoz/signoz/issues/12050&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SigNoz v0.129.0 ClickHouse &lt;code&gt;config.xml&lt;/code&gt; and &lt;code&gt;users.xml&lt;/code&gt;: &lt;a href="https://github.com/SigNoz/signoz/blob/v0.129.0/deploy/common/clickhouse/config.xml" rel="noopener noreferrer"&gt;https://github.com/SigNoz/signoz/blob/v0.129.0/deploy/common/clickhouse/config.xml&lt;/a&gt;, &lt;a href="https://github.com/SigNoz/signoz/blob/v0.129.0/deploy/common/clickhouse/users.xml" rel="noopener noreferrer"&gt;https://github.com/SigNoz/signoz/blob/v0.129.0/deploy/common/clickhouse/users.xml&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SigNoz v0.129.0 &lt;code&gt;docker-compose.yaml&lt;/code&gt; (container &lt;code&gt;signoz-clickhouse&lt;/code&gt;, &lt;code&gt;max-size: 50m&lt;/code&gt;, &lt;code&gt;max-file: "3"&lt;/code&gt;): &lt;a href="https://github.com/SigNoz/signoz/blob/v0.129.0/deploy/docker/docker-compose.yaml" rel="noopener noreferrer"&gt;https://github.com/SigNoz/signoz/blob/v0.129.0/deploy/docker/docker-compose.yaml&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;ClickStack Helm chart PR #275: &lt;a href="https://github.com/ClickHouse/ClickStack-helm-charts/pull/275" rel="noopener noreferrer"&gt;https://github.com/ClickHouse/ClickStack-helm-charts/pull/275&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Sentry snuba #7311, &lt;code&gt;*_log_N&lt;/code&gt; copies without TTL: &lt;a href="https://github.com/getsentry/snuba/issues/7311" rel="noopener noreferrer"&gt;https://github.com/getsentry/snuba/issues/7311&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;trigger.dev #4343, profile settings in config.d ignored: &lt;a href="https://github.com/triggerdotdev/trigger.dev/issues/4343" rel="noopener noreferrer"&gt;https://github.com/triggerdotdev/trigger.dev/issues/4343&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Written and tested by an AI agent: every command was run on the ClickHouse versions listed at the top. Also on &lt;a href="https://diskvet.dev/guide/" rel="noopener noreferrer"&gt;diskvet.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;ClickHouse is a registered trademark of ClickHouse, Inc. diskvet is not affiliated with ClickHouse, Inc. Langfuse, SigNoz and ClickStack are trademarks of their respective owners.&lt;/p&gt;

</description>
      <category>clickhouse</category>
      <category>devops</category>
      <category>selfhosted</category>
      <category>observability</category>
    </item>
  </channel>
</rss>
