fixed site
This commit is contained in:
+619
@@ -0,0 +1,619 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="de-DE"
|
||||
dir="ltr"
|
||||
class="h-full">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="language" content="de-DE">
|
||||
<script>
|
||||
|
||||
(function () {
|
||||
const storedTheme = localStorage.getItem("theme");
|
||||
const systemPrefersLight = window.matchMedia("(prefers-color-scheme: light)").matches;
|
||||
const theme = storedTheme || (systemPrefersLight ? "light" : "dark");
|
||||
document.documentElement.setAttribute("data-theme", theme);
|
||||
})();
|
||||
</script>
|
||||
<title>Blocking Invalid Recipients Before They Reach Your Exchange Server | Demians Blog</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="
|
||||
Recently, I had to deal with a serious problem: backscatter.
|
||||
One of our mail gateways ended up listed on the backscatter.org blacklist for sending bounce …
|
||||
|
||||
">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="canonical" href="https://pyte.dev/posts/blocking-invalid-rcpt-postfix/">
|
||||
|
||||
<meta name="robots" content="index, follow">
|
||||
|
||||
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:title" content="Blocking Invalid Recipients Before They Reach Your Exchange Server | Demians Blog">
|
||||
<meta property="og:description" content="Recently, I had to deal with a serious problem: backscatter.
|
||||
One of our mail gateways ended up listed on the backscatter.org blacklist for sending bounce …">
|
||||
<meta property="og:url" content="https://pyte.dev/posts/blocking-invalid-rcpt-postfix/">
|
||||
<meta property="og:site_name" content="Demians Blog"><meta property="og:image" content="https://pyte.dev/assets/patrick.png">
|
||||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="630"><meta property="article:published_time" content="2025-08-01T10:03:15+02:00">
|
||||
<meta
|
||||
property="article:modified_time"
|
||||
content="2025-08-01T10:03:15+02:00">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Blocking Invalid Recipients Before They Reach Your Exchange Server | Demians Blog">
|
||||
<meta name="twitter:description" content="Recently, I had to deal with a serious problem: backscatter.
|
||||
One of our mail gateways ended up listed on the backscatter.org blacklist for sending bounce …"><meta name="twitter:image" content="https://pyte.dev/assets/patrick.png">
|
||||
|
||||
<script type="application/ld+json">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"{\"@context\":\"https://schema.org\",\"@type\":\"BlogPosting\",\"author\":{\"@type\":\"Person\",\"email\":\"demian (at) pyte (dot) dev\",\"name\":\"Demians Blog\"},\"dateModified\":\"2025-08-01T10:03:15+02:00\",\"datePublished\":\"2025-08-01T10:03:15+02:00\",\"description\":\"Recently, I had to deal with a serious problem: backscatter.\\nOne of our mail gateways ended up listed on the backscatter.org blacklist for sending bounce …\",\"headline\":\"Blocking Invalid Recipients Before They Reach Your Exchange Server\",\"image\":\"https://pyte.dev/assets/patrick.png\",\"mainEntityOfPage\":{\"@id\":\"https://pyte.dev/posts/blocking-invalid-rcpt-postfix/\",\"@type\":\"WebPage\"},\"publisher\":{\"@type\":\"Organization\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://pyte.dev/assets/patrick.png\"},\"name\":\"Demians Blog\"}}"</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="application/ld+json">"{\"@context\":\"https://schema.org\",\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/\",\"name\":\"Demians Blog\",\"position\":1},{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/posts/\",\"name\":\"Posts\",\"position\":2},{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/posts/blocking-invalid-rcpt-postfix/\",\"name\":\"Blocking Invalid Recipients Before They Reach Your Exchange Server\",\"position\":3}]}"</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Outfit:wght@400;500;600;700;800;900&family=Space+Grotesk:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet">
|
||||
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/css/theme.min.86a32b729f656fc6c119ed69193950db815a3ad9fdc967b32c76d602f11449a4.css"
|
||||
integrity="sha256-hqMrcp9lb8bBGe1pGTlQ24FaOtn9yWezLHbWAvEUSaQ=">
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/css/syntax-dark.min.3e403a03e3af837b3829e9b6f01fc7792bda7cb7f5056f5e9786109545c6b2e1.css"
|
||||
integrity="sha256-PkA6A+Ovg3s4Kem28B/HeSvafLf1BW9el4YQlUXGsuE="
|
||||
id="syntax-dark-theme"
|
||||
class="syntax-theme"><link
|
||||
rel="stylesheet"
|
||||
href="/css/syntax-light.min.d0d33b879698595e6b2c0f75f0cea95a8517fb0150570cd0ee4dc42e25c8d147.css"
|
||||
integrity="sha256-0NM7h5aYWV5rLA918M6pWoUX+wFQVwzQ7k3ELiXI0Uc="
|
||||
id="syntax-light-theme"
|
||||
class="syntax-theme"
|
||||
disabled><script>
|
||||
|
||||
(function () {
|
||||
const storedTheme = localStorage.getItem("theme");
|
||||
const systemPrefersLight = window.matchMedia("(prefers-color-scheme: light)").matches;
|
||||
const theme = storedTheme || (systemPrefersLight ? "light" : "dark");
|
||||
|
||||
const syntaxDark = document.getElementById("syntax-dark-theme");
|
||||
const syntaxLight = document.getElementById("syntax-light-theme");
|
||||
|
||||
if (theme === "light") {
|
||||
if (syntaxDark) syntaxDark.disabled = true;
|
||||
if (syntaxLight) syntaxLight.disabled = false;
|
||||
} else {
|
||||
if (syntaxDark) syntaxDark.disabled = false;
|
||||
if (syntaxLight) syntaxLight.disabled = true;
|
||||
}
|
||||
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
const currentTheme = document.documentElement.getAttribute("data-theme");
|
||||
if (currentTheme === "light") {
|
||||
if (syntaxDark) syntaxDark.disabled = true;
|
||||
if (syntaxLight) syntaxLight.disabled = false;
|
||||
} else {
|
||||
if (syntaxDark) syntaxDark.disabled = false;
|
||||
if (syntaxLight) syntaxLight.disabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-theme"],
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/css/bundle.min.783a79746a859af9be598cbc33fba2a1087434b23768524277b07ef28336f113.css"
|
||||
integrity="sha256-eDp5dGqFmvm+WYy8M/uioQh0NLI3aFJCd7B+8oM28RM=">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
|
||||
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/logo-transparent/favicon-32x32.png">
|
||||
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/logo-transparent/favicon-16x16.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/logo-transparent/apple-touch-icon.png">
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="192x192"
|
||||
href="/favicon/logo-transparent/android-chrome-192x192.png">
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="512x512"
|
||||
href="/favicon/logo-transparent/android-chrome-512x512.png">
|
||||
<link rel="manifest" href="/favicon/logo-transparent/site.webmanifest">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
<body class="flex flex-col min-h-screen">
|
||||
<a href="#main-content" class="skip-to-main" aria-label="Skip to main content"
|
||||
>Skip to main content</a
|
||||
>
|
||||
<header class="sticky-header">
|
||||
<div class="header-container">
|
||||
<nav class="header-nav" role="navigation" aria-label="Main navigation">
|
||||
<div class="header-content">
|
||||
<div class="header-logo">
|
||||
<a href="/" class="logo-link" aria-label="Home - Demians Blog">
|
||||
Demians Blog
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="header-menu">
|
||||
<ul class="menu-list">
|
||||
|
||||
<li class="menu-item">
|
||||
<a
|
||||
href="/"
|
||||
class="menu-link ">
|
||||
Home
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="menu-item">
|
||||
<a
|
||||
href="/posts/"
|
||||
class="menu-link ">
|
||||
Blog
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<button id="search-toggle" class="search-toggle" aria-label="Search" type="button">
|
||||
<svg class="search-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button id="theme-toggle" class="theme-toggle" aria-label="Toggle theme" type="button">
|
||||
<svg class="icon-sun" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"></path>
|
||||
</svg>
|
||||
<svg class="icon-moon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="main-content" class="flex-1" role="main">
|
||||
|
||||
<div class="single-post-wrapper">
|
||||
<article class="single-post">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="post-header">
|
||||
<h1 class="post-title-main">Blocking Invalid Recipients Before They Reach Your Exchange Server</h1>
|
||||
|
||||
|
||||
|
||||
<div class="post-meta">
|
||||
<div class="post-meta-info">
|
||||
<time datetime="2025-08-01T10:03:15+02:00" class="post-date">
|
||||
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
August 1, 2025
|
||||
</time>
|
||||
|
||||
<span class="meta-separator">•</span>
|
||||
|
||||
|
||||
<span class="post-word-count">
|
||||
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
||||
</svg>
|
||||
762 words
|
||||
</span>
|
||||
|
||||
|
||||
|
||||
<span class="meta-separator">•</span>
|
||||
<span class="post-reading-time">
|
||||
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
4 min
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="post-content-main">
|
||||
<p>Recently, I had to deal with a serious problem: <strong>backscatter</strong>.<br>
|
||||
One of our mail gateways ended up listed on the <strong>backscatter.org</strong> blacklist for sending bounce messages to forged senders.</p>
|
||||
<p>After checking the logs, I quickly realized that our system wasn’t actually protected against backscatter attacks, so I had to do something about it.</p>
|
||||
<h2 id="what-even-is-backscatter">What Even Is Backscatter?</h2>
|
||||
<p>Backscatter is unwanted email that your mail server sends <strong>after</strong> receiving a message, usually in the form of a <strong>non-delivery report (NDR)</strong> or <strong>bounce</strong> to a <strong>forged sender address</strong>.</p>
|
||||
<p>It happens when:</p>
|
||||
<ul>
|
||||
<li>A spammer sends email with a <strong>fake “From” address</strong> (often an innocent third party).</li>
|
||||
<li>Your mail server <strong>accepts</strong> the message first, but later discovers it’s undeliverable.</li>
|
||||
<li>Your server sends a <strong>bounce</strong> to the forged address, hitting an innocent person instead of the spammer.</li>
|
||||
</ul>
|
||||
<p>This makes your server appear to be sending spam, even though you’re just bouncing bad mail.</p>
|
||||
<h2 id="the-problem-in-our-setup">The Problem in Our Setup</h2>
|
||||
<p>Our mail gateway mostly relays mail to multiple internal Exchange Server clusters.<br>
|
||||
We have a list of valid domains configured, so Postfix will only accept mail for those domains.</p>
|
||||
<p><strong>The problem?</strong><br>
|
||||
Postfix didn’t know which individual recipients were valid on the downstream Exchange servers.<br>
|
||||
That meant it would happily accept messages for <strong>nonexistent users</strong>, only to later bounce them, classic backscatter behavior.</p>
|
||||
<h2 id="the-goal">The Goal</h2>
|
||||
<p>We needed to reject mail <strong>during the SMTP session</strong>, ideally right after the <code>RCPT TO</code> command, if the recipient didn’t exist on the Exchange servers.</p>
|
||||
<p>That way, the sending server would get the rejection immediately, and we would never have to generate a bounce message.</p>
|
||||
<h2 id="possible-solutions-i-considered">Possible Solutions I Considered</h2>
|
||||
<h3 id="1-relay_"><strong>1. relay_recipient_maps</strong></h3>
|
||||
<p>This would require maintaining a <strong>full list of valid recipients</strong> in a Postfix lookup table.<br>
|
||||
It works well, but means writing and maintaining a script to <strong>sync the list from Active Directory</strong> on a regular basis.</p>
|
||||
<p>For us, this was too much custom scripting, too fragile, and too messy to maintain.</p>
|
||||
<h3 id="2-virtual_"><strong>2. virtual_mailbox_maps with LDAP</strong></h3>
|
||||
<p>Another approach would be using <strong>LDAP lookups</strong> directly against Active Directory to verify recipients in real-time.</p>
|
||||
<p>This can work in some setups, but:</p>
|
||||
<ul>
|
||||
<li>It adds complexity and dependencies.</li>
|
||||
<li>It can introduce security concerns.</li>
|
||||
<li>It didn’t fit well with our environment.</li>
|
||||
</ul>
|
||||
<p>So I ruled it out.</p>
|
||||
<h2 id="the-solution-reject_unverified_recipient">The Solution: <code>reject_unverified_recipient</code></h2>
|
||||
<p>While reading through the <strong>Postfix Address Verification Howto</strong>, I came across the <code>reject_unverified_recipient</code> option - bling! - exactly what I needed.</p>
|
||||
<h3 id="how-it-works"><strong>How It Works</strong></h3>
|
||||
<p>When an incoming SMTP session reaches the <code>RCPT TO</code> stage:</p>
|
||||
<ol>
|
||||
<li>Postfix checks if <code>reject_unverified_recipient</code> is enabled for the recipient domain.</li>
|
||||
<li>If yes, it temporarily <strong>probes the downstream mail system</strong> to see if the recipient address exists.</li>
|
||||
<li>If the downstream system says:
|
||||
<ul>
|
||||
<li><strong>User exists</strong> → Postfix continues processing.</li>
|
||||
<li><strong>User does not exist</strong> → Postfix <strong>rejects immediately</strong> with:
|
||||
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">550 5.1.1 <user@example.de>: Recipient address rejected: User unknown
|
||||
</span></span></code></pre></div></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
<p>Because the rejection happens <strong>during SMTP</strong>, no bounce is generated, and backscatter is avoided entirely.</p>
|
||||
<h2 id="implementation-in-ispconfig">Implementation in ISPConfig</h2>
|
||||
<p>In our case, the mail gateways run <strong>Postfix</strong> with <strong>ISPConfig</strong> as the management interface.<br>
|
||||
I implemented a new configuration option in ISPConfig for <strong>per-domain control</strong> of <code>reject_unverified_recipient</code>, along with a <strong>validation server</strong> to specify for the downstream validation server.</p>
|
||||
<h3 id="specifying-a-validation-server-for-recipient-verification">Specifying a Validation Server for Recipient Verification</h3>
|
||||
<p>One important detail is that Exchange servers cannot validate recipients over the default SMTP transport on port <code>25</code>. To enable recipient validation, you need to activate it on the Exchange server and use the Hub Transport service, which by default runs on port <code>2525</code>.</p>
|
||||
<p>Make sure to restrict access to this port, as it requires anonymous login specifically for recipient validation, you don’t want that exposed broadly.</p>
|
||||
|
||||
|
||||
|
||||
<blockquote class="blockquote-regular">
|
||||
<p><strong>Note:</strong> This setup does not affect your regular mail flow. Only the SMTP probes used for verifying recipients are sent to this validation server.<br>
|
||||
Postfix enables this behavior with the <code>address_verify_transport_maps</code> option.</p>
|
||||
|
||||
</blockquote>
|
||||
|
||||
<p>To optimize performance and reduce unnecessary verification probes for addresses that have already been checked, I configured a local cache on the mail gateway using the <code>address_verify_map</code> option. This way, repeated probes for the same recipient are avoided.</p>
|
||||
<p>Now, for domains that need recipient verification, we can enable it in the panel and point Postfix to the appropriate <strong>validation transport</strong>.</p>
|
||||
<h2 id="the-result">The Result</h2>
|
||||
<p>After enabling <code>reject_unverified_recipient</code> and pointing it at the Exchange clusters, the backscatter stopped completely.<br>
|
||||
We were no longer accepting messages for invalid recipients.</p>
|
||||
<h2 id="conclusion">Conclusion</h2>
|
||||
<p>If your Postfix server is acting as a relay for Exchange (or any downstream mail system) and you’re struggling with <strong>backscatter spam</strong>, enabling <code>reject_unverified_recipient</code> can be a clean and effective fix.</p>
|
||||
<p>It avoids maintaining large static recipient maps, works dynamically, and ensures that invalid mail is rejected <strong>before</strong> it ever gets into your system.</p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<nav class="post-navigation" aria-label="Post navigation">
|
||||
|
||||
<a
|
||||
href="/posts/dovecot-index-cache-issues/"
|
||||
class="nav-link nav-prev"
|
||||
aria-label="Previous post: Dovecot Index Cache Issues">
|
||||
<span class="nav-label">Previous</span>
|
||||
<span class="nav-title">Dovecot Index Cache Issues</span>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a
|
||||
href="/posts/google-groups-spam/"
|
||||
class="nav-link nav-next"
|
||||
aria-label="Next post: Google Groups Spam">
|
||||
<span class="nav-label">Next</span>
|
||||
<span class="nav-title">Google Groups Spam</span>
|
||||
</a>
|
||||
|
||||
</nav>
|
||||
</article>
|
||||
|
||||
|
||||
<aside class="post-toc" id="post-toc" aria-label="Table of contents">
|
||||
<button
|
||||
class="toc-toggle"
|
||||
id="toc-toggle"
|
||||
aria-expanded="true"
|
||||
aria-controls="toc-content"
|
||||
aria-label="Toggle table of contents">
|
||||
<svg
|
||||
class="toc-burger-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
<span class="toc-toggle-text">Table of Contents</span>
|
||||
<svg
|
||||
class="toc-chevron-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="toc-content" id="toc-content">
|
||||
<nav class="toc-nav" aria-label="Table of contents">
|
||||
<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#what-even-is-backscatter">What Even Is Backscatter?</a></li>
|
||||
<li><a href="#the-problem-in-our-setup">The Problem in Our Setup</a></li>
|
||||
<li><a href="#the-goal">The Goal</a></li>
|
||||
<li><a href="#possible-solutions-i-considered">Possible Solutions I Considered</a>
|
||||
<ul>
|
||||
<li><a href="#1-relay_"><strong>1. relay_recipient_maps</strong></a></li>
|
||||
<li><a href="#2-virtual_"><strong>2. virtual_mailbox_maps with LDAP</strong></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#the-solution-reject_unverified_recipient">The Solution: <code>reject_unverified_recipient</code></a>
|
||||
<ul>
|
||||
<li><a href="#how-it-works"><strong>How It Works</strong></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#implementation-in-ispconfig">Implementation in ISPConfig</a>
|
||||
<ul>
|
||||
<li><a href="#specifying-a-validation-server-for-recipient-verification">Specifying a Validation Server for Recipient Verification</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#the-result">The Result</a></li>
|
||||
<li><a href="#conclusion">Conclusion</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<footer class="site-footer">
|
||||
<div class="footer-content">
|
||||
<p class="footer-text">
|
||||
©
|
||||
|
||||
2026
|
||||
|
||||
Demians Blog.
|
||||
|
||||
Built with Hugo and Mana ❤️
|
||||
|
||||
</p>
|
||||
|
||||
<div class="footer-social">
|
||||
<div class="social-links">
|
||||
|
||||
<a
|
||||
href="https://github.com/pyte1"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="social-link"
|
||||
aria-label="GitHub">
|
||||
<svg fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="mailto:demian%20%28at%29%20pyte%20%28dot%29%20dev" class="social-link" aria-label="Email">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</footer>
|
||||
|
||||
<div id="search-modal" class="search-modal" aria-hidden="true" role="dialog" aria-label="Search">
|
||||
<div class="search-modal-backdrop" id="search-modal-backdrop"></div>
|
||||
<div class="search-modal-container">
|
||||
<div class="search-input-wrapper">
|
||||
<svg class="search-input-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
<input
|
||||
type="search"
|
||||
id="search-input"
|
||||
class="search-input"
|
||||
placeholder="Search..."
|
||||
autocomplete="off"
|
||||
aria-label="Search input">
|
||||
<button class="search-input-clear" id="search-input-clear" aria-label="Clear search">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="search-modal-close" id="search-modal-close" aria-label="Close search">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div id="search-results" class="search-results"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<button id="scroll-to-top" class="scroll-to-top" aria-label="Scroll to top" type="button">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M5 10l7-7m0 0l7 7m-7-7v18"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="/js/main.min.e60ab79dca7b920b4dc5cf3163ad5ce8794839b60f27778db65782f087be3e27.js" defer></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+569
@@ -0,0 +1,569 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="de-DE"
|
||||
dir="ltr"
|
||||
class="h-full">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="language" content="de-DE">
|
||||
<script>
|
||||
|
||||
(function () {
|
||||
const storedTheme = localStorage.getItem("theme");
|
||||
const systemPrefersLight = window.matchMedia("(prefers-color-scheme: light)").matches;
|
||||
const theme = storedTheme || (systemPrefersLight ? "light" : "dark");
|
||||
document.documentElement.setAttribute("data-theme", theme);
|
||||
})();
|
||||
</script>
|
||||
<title>Dovecot Index Cache Issues | Demians Blog</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="
|
||||
Understanding dovecot.index.cache I recently migrated an old mail server system into its new home. After the migration, I checked the logs and noticed some …
|
||||
|
||||
">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="canonical" href="https://pyte.dev/posts/dovecot-index-cache-issues/">
|
||||
|
||||
<meta name="robots" content="index, follow">
|
||||
|
||||
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:title" content="Dovecot Index Cache Issues | Demians Blog">
|
||||
<meta property="og:description" content="Understanding dovecot.index.cache I recently migrated an old mail server system into its new home. After the migration, I checked the logs and noticed some …">
|
||||
<meta property="og:url" content="https://pyte.dev/posts/dovecot-index-cache-issues/">
|
||||
<meta property="og:site_name" content="Demians Blog"><meta property="og:image" content="https://pyte.dev/assets/patrick.png">
|
||||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="630"><meta property="article:published_time" content="2025-05-18T11:34:09+02:00">
|
||||
<meta
|
||||
property="article:modified_time"
|
||||
content="2025-05-18T11:34:09+02:00">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Dovecot Index Cache Issues | Demians Blog">
|
||||
<meta name="twitter:description" content="Understanding dovecot.index.cache I recently migrated an old mail server system into its new home. After the migration, I checked the logs and noticed some …"><meta name="twitter:image" content="https://pyte.dev/assets/patrick.png">
|
||||
|
||||
<script type="application/ld+json">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"{\"@context\":\"https://schema.org\",\"@type\":\"BlogPosting\",\"author\":{\"@type\":\"Person\",\"email\":\"demian (at) pyte (dot) dev\",\"name\":\"Demians Blog\"},\"dateModified\":\"2025-05-18T11:34:09+02:00\",\"datePublished\":\"2025-05-18T11:34:09+02:00\",\"description\":\"Understanding dovecot.index.cache I recently migrated an old mail server system into its new home. After the migration, I checked the logs and noticed some …\",\"headline\":\"Dovecot Index Cache Issues\",\"image\":\"https://pyte.dev/assets/patrick.png\",\"mainEntityOfPage\":{\"@id\":\"https://pyte.dev/posts/dovecot-index-cache-issues/\",\"@type\":\"WebPage\"},\"publisher\":{\"@type\":\"Organization\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://pyte.dev/assets/patrick.png\"},\"name\":\"Demians Blog\"}}"</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="application/ld+json">"{\"@context\":\"https://schema.org\",\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/\",\"name\":\"Demians Blog\",\"position\":1},{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/posts/\",\"name\":\"Posts\",\"position\":2},{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/posts/dovecot-index-cache-issues/\",\"name\":\"Dovecot Index Cache Issues\",\"position\":3}]}"</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Outfit:wght@400;500;600;700;800;900&family=Space+Grotesk:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet">
|
||||
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/css/theme.min.86a32b729f656fc6c119ed69193950db815a3ad9fdc967b32c76d602f11449a4.css"
|
||||
integrity="sha256-hqMrcp9lb8bBGe1pGTlQ24FaOtn9yWezLHbWAvEUSaQ=">
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/css/syntax-dark.min.3e403a03e3af837b3829e9b6f01fc7792bda7cb7f5056f5e9786109545c6b2e1.css"
|
||||
integrity="sha256-PkA6A+Ovg3s4Kem28B/HeSvafLf1BW9el4YQlUXGsuE="
|
||||
id="syntax-dark-theme"
|
||||
class="syntax-theme"><link
|
||||
rel="stylesheet"
|
||||
href="/css/syntax-light.min.d0d33b879698595e6b2c0f75f0cea95a8517fb0150570cd0ee4dc42e25c8d147.css"
|
||||
integrity="sha256-0NM7h5aYWV5rLA918M6pWoUX+wFQVwzQ7k3ELiXI0Uc="
|
||||
id="syntax-light-theme"
|
||||
class="syntax-theme"
|
||||
disabled><script>
|
||||
|
||||
(function () {
|
||||
const storedTheme = localStorage.getItem("theme");
|
||||
const systemPrefersLight = window.matchMedia("(prefers-color-scheme: light)").matches;
|
||||
const theme = storedTheme || (systemPrefersLight ? "light" : "dark");
|
||||
|
||||
const syntaxDark = document.getElementById("syntax-dark-theme");
|
||||
const syntaxLight = document.getElementById("syntax-light-theme");
|
||||
|
||||
if (theme === "light") {
|
||||
if (syntaxDark) syntaxDark.disabled = true;
|
||||
if (syntaxLight) syntaxLight.disabled = false;
|
||||
} else {
|
||||
if (syntaxDark) syntaxDark.disabled = false;
|
||||
if (syntaxLight) syntaxLight.disabled = true;
|
||||
}
|
||||
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
const currentTheme = document.documentElement.getAttribute("data-theme");
|
||||
if (currentTheme === "light") {
|
||||
if (syntaxDark) syntaxDark.disabled = true;
|
||||
if (syntaxLight) syntaxLight.disabled = false;
|
||||
} else {
|
||||
if (syntaxDark) syntaxDark.disabled = false;
|
||||
if (syntaxLight) syntaxLight.disabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-theme"],
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/css/bundle.min.783a79746a859af9be598cbc33fba2a1087434b23768524277b07ef28336f113.css"
|
||||
integrity="sha256-eDp5dGqFmvm+WYy8M/uioQh0NLI3aFJCd7B+8oM28RM=">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
|
||||
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/logo-transparent/favicon-32x32.png">
|
||||
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/logo-transparent/favicon-16x16.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/logo-transparent/apple-touch-icon.png">
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="192x192"
|
||||
href="/favicon/logo-transparent/android-chrome-192x192.png">
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="512x512"
|
||||
href="/favicon/logo-transparent/android-chrome-512x512.png">
|
||||
<link rel="manifest" href="/favicon/logo-transparent/site.webmanifest">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
<body class="flex flex-col min-h-screen">
|
||||
<a href="#main-content" class="skip-to-main" aria-label="Skip to main content"
|
||||
>Skip to main content</a
|
||||
>
|
||||
<header class="sticky-header">
|
||||
<div class="header-container">
|
||||
<nav class="header-nav" role="navigation" aria-label="Main navigation">
|
||||
<div class="header-content">
|
||||
<div class="header-logo">
|
||||
<a href="/" class="logo-link" aria-label="Home - Demians Blog">
|
||||
Demians Blog
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="header-menu">
|
||||
<ul class="menu-list">
|
||||
|
||||
<li class="menu-item">
|
||||
<a
|
||||
href="/"
|
||||
class="menu-link ">
|
||||
Home
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="menu-item">
|
||||
<a
|
||||
href="/posts/"
|
||||
class="menu-link ">
|
||||
Blog
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<button id="search-toggle" class="search-toggle" aria-label="Search" type="button">
|
||||
<svg class="search-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button id="theme-toggle" class="theme-toggle" aria-label="Toggle theme" type="button">
|
||||
<svg class="icon-sun" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"></path>
|
||||
</svg>
|
||||
<svg class="icon-moon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="main-content" class="flex-1" role="main">
|
||||
|
||||
<div class="single-post-wrapper">
|
||||
<article class="single-post">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="post-header">
|
||||
<h1 class="post-title-main">Dovecot Index Cache Issues</h1>
|
||||
|
||||
|
||||
|
||||
<div class="post-meta">
|
||||
<div class="post-meta-info">
|
||||
<time datetime="2025-05-18T11:34:09+02:00" class="post-date">
|
||||
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
May 18, 2025
|
||||
</time>
|
||||
|
||||
<span class="meta-separator">•</span>
|
||||
|
||||
|
||||
<span class="post-word-count">
|
||||
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
||||
</svg>
|
||||
592 words
|
||||
</span>
|
||||
|
||||
|
||||
|
||||
<span class="meta-separator">•</span>
|
||||
<span class="post-reading-time">
|
||||
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
3 min
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="post-content-main">
|
||||
<h1 id="understanding-dovecotindexcache">Understanding <code>dovecot.index.cache</code></h1>
|
||||
<p>I recently migrated an old mail server system into its new home. After the migration, I checked the logs and noticed some warnings that looked like this:</p>
|
||||
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">May</span> <span class="mi">17</span> <span class="mi">11</span><span class="p">:</span><span class="mi">23</span><span class="p">:</span><span class="mi">13</span> <span class="n">server1</span> <span class="n">dovecot</span><span class="p">:</span> <span class="n">dsync</span><span class="o">-</span><span class="n">local</span><span class="p">(</span><span class="n">user</span><span class="err">@</span><span class="n">domain</span><span class="o">.</span><span class="n">tld</span><span class="p">)</span><span class="o"><</span><span class="n">cRjZCwGnKWiIvicA2dm5Tw</span><span class="o">></span><span class="p">:</span> <span class="n">Error</span><span class="p">:</span> <span class="n">Mailbox</span> <span class="n">INBOX</span><span class="p">:</span> <span class="n">mmap</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mi">511310568</span><span class="p">)</span> <span class="n">failed</span> <span class="n">with</span> <span class="n">file</span> <span class="o">/</span><span class="k">var</span><span class="o">/</span><span class="n">vmail</span><span class="o">/</span><span class="n">domain</span><span class="o">.</span><span class="n">tld</span><span class="o">/</span><span class="n">user</span><span class="o">/</span><span class="n">Maildir</span><span class="o">/</span><span class="n">dovecot</span><span class="o">.</span><span class="n">index</span><span class="o">.</span><span class="n">cache</span><span class="p">:</span> <span class="n">Cannot</span> <span class="n">allocate</span> <span class="n">memory</span>
|
||||
</span></span></code></pre></div><p>The error indicates that the <code>dovecot.index.cache</code> file is too big to process, and Dovecot cannot allocate enough memory to handle it.</p>
|
||||
<h1 id="what-are-dovecotindexcache-files">What Are <code>dovecot.index.cache</code> Files?</h1>
|
||||
<p>Dovecot, the most popular IMAP server, uses a set of index files (<code>dovecot.index</code>, <code>dovecot.index.cache</code>, <code>dovecot.index.log</code>, etc.) to speed up mailbox access. The file I had to deal with stores cached message metadata (headers, flags, and preview text) so Dovecot does not have to read each message file in the mailbox every time.</p>
|
||||
<p>Over time, the <code>dovecot.index.cache</code> file can grow very large or become outdated. Here are a few reasons why this happens:</p>
|
||||
<h3 id="during-normal-operation">During Normal Operation</h3>
|
||||
<ul>
|
||||
<li>Deleted or moved messages may leave behind unused metadata.</li>
|
||||
<li>Corrupt or unreferenced entries might accumulate if a process is interrupted.</li>
|
||||
<li>Stale data can hang around for years if not explicitly purged.</li>
|
||||
</ul>
|
||||
<h3 id="after-a-migration">After a Migration</h3>
|
||||
<p>Migrations are particularly prone to creating out-of-sync or bloated index files because:</p>
|
||||
<ul>
|
||||
<li><strong>File timestamps and UIDs change</strong>: Dovecot’s cache is based on assumptions about message state. A migration (e.g. via <code>rsync</code> or <code>imapsync</code>) may change those assumptions, causing the cache to mismatch the actual message data.</li>
|
||||
<li><strong>Index format/version mismatch</strong>: If you switch Dovecot versions between servers, the format of <code>.index.cache</code> might be incompatible or inefficient.</li>
|
||||
<li><strong>Partial cache rebuilds</strong>: After a migration, Dovecot may try to reuse old cache files that no longer reflect the real contents of the mailbox, leading to odd behavior or performance issues.</li>
|
||||
</ul>
|
||||
<h1 id="how-to-deal-with-these-files">How to Deal with These Files</h1>
|
||||
<p>After migrating to the new servers, I encountered a few of these messages in the log, so I had to search through and find all users affected by this. Since these large files not only consume unnecessary disk space but also affect performance and cause <code>dsync</code> issues, I had to do something about it.</p>
|
||||
<h2 id="is-it-safe-to-delete-these-files">Is It Safe to Delete These Files?</h2>
|
||||
<p>Short answer: <strong>Yes</strong> — it’s safe to delete all <code>dovecot.index*</code> files inside a user’s Maildir.</p>
|
||||
<p>Dovecot will automatically regenerate the indexes when needed. This will result in a small delay the first time a user accesses their mailbox after deleting these files, but performance will return to normal quickly.</p>
|
||||
<h2 id="how-to-find-and-clean-up-large-index-files">How to Find and Clean Up Large Index Files</h2>
|
||||
<p>First, you should check what “large” means for your setup. I checked for files that are larger than 100 MB in size using the following command:</p>
|
||||
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">find /var/vmail -type f -name <span class="s2">"dovecot.index.cache"</span> -size +100M -exec ls -lh <span class="o">{}</span> <span class="se">\;</span>
|
||||
</span></span></code></pre></div><p>After validating these files and users, I then moved into the user’s Maildir and removed the files:</p>
|
||||
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">cd</span> <span class="o">/</span><span class="k">var</span><span class="o">/</span><span class="n">vmail</span><span class="o">/</span><span class="n">domain</span><span class="o">.</span><span class="n">tld</span><span class="o">/</span><span class="n">affecteduser</span><span class="o">/</span><span class="n">Maildir</span><span class="o">/</span>
|
||||
</span></span><span class="line"><span class="cl"><span class="n">rm</span> <span class="n">dovecot</span><span class="o">.</span><span class="n">index</span><span class="o">*</span>
|
||||
</span></span></code></pre></div><h2 id="what-else-can-i-do">What Else Can I Do?</h2>
|
||||
<p>There’s another way to deal with large index files: you could increase the virtual memory limit that Dovecot is allowed to use, which is controlled by the <code>default_vsz_limit</code> in Dovecot’s configuration files.</p>
|
||||
<p>Another option to prevent future bloat of these files would be adjusting the <code>mail_cache_fields</code> and <code>mail_never_cache_fields</code> in the configuration.</p>
|
||||
<p>I decided to delete these files for affected users because these files had grown so large before migrating to the new setup. After regenerating, the files are much smaller, and time will tell if this becomes an issue again.</p>
|
||||
<h1 id="conclusion">Conclusion</h1>
|
||||
<p>If you encounter these warnings in your logs, it’s worth checking the <code>dovecot.index.cache</code> files and dealing with them. Cleaning them up is safe and easy — Dovecot will take care of rebuilding what it needs.</p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<nav class="post-navigation" aria-label="Post navigation">
|
||||
|
||||
<a
|
||||
href="/posts/my-first-post/"
|
||||
class="nav-link nav-prev"
|
||||
aria-label="Previous post: Introduction">
|
||||
<span class="nav-label">Previous</span>
|
||||
<span class="nav-title">Introduction</span>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a
|
||||
href="/posts/blocking-invalid-rcpt-postfix/"
|
||||
class="nav-link nav-next"
|
||||
aria-label="Next post: Blocking Invalid Recipients Before They Reach Your Exchange Server">
|
||||
<span class="nav-label">Next</span>
|
||||
<span class="nav-title">Blocking Invalid Recipients Before They Reach Your Exchange Server</span>
|
||||
</a>
|
||||
|
||||
</nav>
|
||||
</article>
|
||||
|
||||
|
||||
<aside class="post-toc" id="post-toc" aria-label="Table of contents">
|
||||
<button
|
||||
class="toc-toggle"
|
||||
id="toc-toggle"
|
||||
aria-expanded="true"
|
||||
aria-controls="toc-content"
|
||||
aria-label="Toggle table of contents">
|
||||
<svg
|
||||
class="toc-burger-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
<span class="toc-toggle-text">Table of Contents</span>
|
||||
<svg
|
||||
class="toc-chevron-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="toc-content" id="toc-content">
|
||||
<nav class="toc-nav" aria-label="Table of contents">
|
||||
<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li>
|
||||
<ul>
|
||||
<li><a href="#during-normal-operation">During Normal Operation</a></li>
|
||||
<li><a href="#after-a-migration">After a Migration</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul>
|
||||
<li><a href="#is-it-safe-to-delete-these-files">Is It Safe to Delete These Files?</a></li>
|
||||
<li><a href="#how-to-find-and-clean-up-large-index-files">How to Find and Clean Up Large Index Files</a></li>
|
||||
<li><a href="#what-else-can-i-do">What Else Can I Do?</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<footer class="site-footer">
|
||||
<div class="footer-content">
|
||||
<p class="footer-text">
|
||||
©
|
||||
|
||||
2026
|
||||
|
||||
Demians Blog.
|
||||
|
||||
Built with Hugo and Mana ❤️
|
||||
|
||||
</p>
|
||||
|
||||
<div class="footer-social">
|
||||
<div class="social-links">
|
||||
|
||||
<a
|
||||
href="https://github.com/pyte1"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="social-link"
|
||||
aria-label="GitHub">
|
||||
<svg fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="mailto:demian%20%28at%29%20pyte%20%28dot%29%20dev" class="social-link" aria-label="Email">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</footer>
|
||||
|
||||
<div id="search-modal" class="search-modal" aria-hidden="true" role="dialog" aria-label="Search">
|
||||
<div class="search-modal-backdrop" id="search-modal-backdrop"></div>
|
||||
<div class="search-modal-container">
|
||||
<div class="search-input-wrapper">
|
||||
<svg class="search-input-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
<input
|
||||
type="search"
|
||||
id="search-input"
|
||||
class="search-input"
|
||||
placeholder="Search..."
|
||||
autocomplete="off"
|
||||
aria-label="Search input">
|
||||
<button class="search-input-clear" id="search-input-clear" aria-label="Clear search">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="search-modal-close" id="search-modal-close" aria-label="Close search">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div id="search-results" class="search-results"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<button id="scroll-to-top" class="scroll-to-top" aria-label="Scroll to top" type="button">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M5 10l7-7m0 0l7 7m-7-7v18"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="/js/main.min.e60ab79dca7b920b4dc5cf3163ad5ce8794839b60f27778db65782f087be3e27.js" defer></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Executable
+618
@@ -0,0 +1,618 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="de-DE"
|
||||
dir="ltr"
|
||||
class="h-full">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="language" content="de-DE">
|
||||
<script>
|
||||
|
||||
(function () {
|
||||
const storedTheme = localStorage.getItem("theme");
|
||||
const systemPrefersLight = window.matchMedia("(prefers-color-scheme: light)").matches;
|
||||
const theme = storedTheme || (systemPrefersLight ? "light" : "dark");
|
||||
document.documentElement.setAttribute("data-theme", theme);
|
||||
})();
|
||||
</script>
|
||||
<title>Google Groups Spam | Demians Blog</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="
|
||||
Understanding the Google Groups Spam Problem Over the past year, I’ve noticed a significant increase in spam messages originating from Google Groups. This …
|
||||
|
||||
">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="canonical" href="https://pyte.dev/posts/google-groups-spam/">
|
||||
|
||||
<meta name="robots" content="index, follow">
|
||||
|
||||
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:title" content="Google Groups Spam | Demians Blog">
|
||||
<meta property="og:description" content="Understanding the Google Groups Spam Problem Over the past year, I’ve noticed a significant increase in spam messages originating from Google Groups. This …">
|
||||
<meta property="og:url" content="https://pyte.dev/posts/google-groups-spam/">
|
||||
<meta property="og:site_name" content="Demians Blog"><meta property="og:image" content="https://pyte.dev/assets/patrick.png">
|
||||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="630"><meta property="article:published_time" content="2025-10-10T09:26:56+02:00">
|
||||
<meta
|
||||
property="article:modified_time"
|
||||
content="2025-10-10T09:26:56+02:00">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Google Groups Spam | Demians Blog">
|
||||
<meta name="twitter:description" content="Understanding the Google Groups Spam Problem Over the past year, I’ve noticed a significant increase in spam messages originating from Google Groups. This …"><meta name="twitter:image" content="https://pyte.dev/assets/patrick.png">
|
||||
|
||||
<script type="application/ld+json">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"{\"@context\":\"https://schema.org\",\"@type\":\"BlogPosting\",\"author\":{\"@type\":\"Person\",\"email\":\"demian (at) pyte (dot) dev\",\"name\":\"Demians Blog\"},\"dateModified\":\"2025-10-10T09:26:56+02:00\",\"datePublished\":\"2025-10-10T09:26:56+02:00\",\"description\":\"Understanding the Google Groups Spam Problem Over the past year, I\\u0026rsquo;ve noticed a significant increase in spam messages originating from Google Groups. This …\",\"headline\":\"Google Groups Spam\",\"image\":\"https://pyte.dev/assets/patrick.png\",\"mainEntityOfPage\":{\"@id\":\"https://pyte.dev/posts/google-groups-spam/\",\"@type\":\"WebPage\"},\"publisher\":{\"@type\":\"Organization\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://pyte.dev/assets/patrick.png\"},\"name\":\"Demians Blog\"}}"</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="application/ld+json">"{\"@context\":\"https://schema.org\",\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/\",\"name\":\"Demians Blog\",\"position\":1},{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/posts/\",\"name\":\"Posts\",\"position\":2},{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/posts/google-groups-spam/\",\"name\":\"Google Groups Spam\",\"position\":3}]}"</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Outfit:wght@400;500;600;700;800;900&family=Space+Grotesk:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet">
|
||||
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/css/theme.min.86a32b729f656fc6c119ed69193950db815a3ad9fdc967b32c76d602f11449a4.css"
|
||||
integrity="sha256-hqMrcp9lb8bBGe1pGTlQ24FaOtn9yWezLHbWAvEUSaQ=">
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/css/syntax-dark.min.3e403a03e3af837b3829e9b6f01fc7792bda7cb7f5056f5e9786109545c6b2e1.css"
|
||||
integrity="sha256-PkA6A+Ovg3s4Kem28B/HeSvafLf1BW9el4YQlUXGsuE="
|
||||
id="syntax-dark-theme"
|
||||
class="syntax-theme"><link
|
||||
rel="stylesheet"
|
||||
href="/css/syntax-light.min.d0d33b879698595e6b2c0f75f0cea95a8517fb0150570cd0ee4dc42e25c8d147.css"
|
||||
integrity="sha256-0NM7h5aYWV5rLA918M6pWoUX+wFQVwzQ7k3ELiXI0Uc="
|
||||
id="syntax-light-theme"
|
||||
class="syntax-theme"
|
||||
disabled><script>
|
||||
|
||||
(function () {
|
||||
const storedTheme = localStorage.getItem("theme");
|
||||
const systemPrefersLight = window.matchMedia("(prefers-color-scheme: light)").matches;
|
||||
const theme = storedTheme || (systemPrefersLight ? "light" : "dark");
|
||||
|
||||
const syntaxDark = document.getElementById("syntax-dark-theme");
|
||||
const syntaxLight = document.getElementById("syntax-light-theme");
|
||||
|
||||
if (theme === "light") {
|
||||
if (syntaxDark) syntaxDark.disabled = true;
|
||||
if (syntaxLight) syntaxLight.disabled = false;
|
||||
} else {
|
||||
if (syntaxDark) syntaxDark.disabled = false;
|
||||
if (syntaxLight) syntaxLight.disabled = true;
|
||||
}
|
||||
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
const currentTheme = document.documentElement.getAttribute("data-theme");
|
||||
if (currentTheme === "light") {
|
||||
if (syntaxDark) syntaxDark.disabled = true;
|
||||
if (syntaxLight) syntaxLight.disabled = false;
|
||||
} else {
|
||||
if (syntaxDark) syntaxDark.disabled = false;
|
||||
if (syntaxLight) syntaxLight.disabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-theme"],
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/css/bundle.min.783a79746a859af9be598cbc33fba2a1087434b23768524277b07ef28336f113.css"
|
||||
integrity="sha256-eDp5dGqFmvm+WYy8M/uioQh0NLI3aFJCd7B+8oM28RM=">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
|
||||
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/logo-transparent/favicon-32x32.png">
|
||||
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/logo-transparent/favicon-16x16.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/logo-transparent/apple-touch-icon.png">
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="192x192"
|
||||
href="/favicon/logo-transparent/android-chrome-192x192.png">
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="512x512"
|
||||
href="/favicon/logo-transparent/android-chrome-512x512.png">
|
||||
<link rel="manifest" href="/favicon/logo-transparent/site.webmanifest">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
<body class="flex flex-col min-h-screen">
|
||||
<a href="#main-content" class="skip-to-main" aria-label="Skip to main content"
|
||||
>Skip to main content</a
|
||||
>
|
||||
<header class="sticky-header">
|
||||
<div class="header-container">
|
||||
<nav class="header-nav" role="navigation" aria-label="Main navigation">
|
||||
<div class="header-content">
|
||||
<div class="header-logo">
|
||||
<a href="/" class="logo-link" aria-label="Home - Demians Blog">
|
||||
Demians Blog
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="header-menu">
|
||||
<ul class="menu-list">
|
||||
|
||||
<li class="menu-item">
|
||||
<a
|
||||
href="/"
|
||||
class="menu-link ">
|
||||
Home
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="menu-item">
|
||||
<a
|
||||
href="/posts/"
|
||||
class="menu-link ">
|
||||
Blog
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<button id="search-toggle" class="search-toggle" aria-label="Search" type="button">
|
||||
<svg class="search-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button id="theme-toggle" class="theme-toggle" aria-label="Toggle theme" type="button">
|
||||
<svg class="icon-sun" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"></path>
|
||||
</svg>
|
||||
<svg class="icon-moon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="main-content" class="flex-1" role="main">
|
||||
|
||||
<div class="single-post-wrapper">
|
||||
<article class="single-post">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="post-header">
|
||||
<h1 class="post-title-main">Google Groups Spam</h1>
|
||||
|
||||
|
||||
|
||||
<div class="post-meta">
|
||||
<div class="post-meta-info">
|
||||
<time datetime="2025-10-10T09:26:56+02:00" class="post-date">
|
||||
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
October 10, 2025
|
||||
</time>
|
||||
|
||||
<span class="meta-separator">•</span>
|
||||
|
||||
|
||||
<span class="post-word-count">
|
||||
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
||||
</svg>
|
||||
722 words
|
||||
</span>
|
||||
|
||||
|
||||
|
||||
<span class="meta-separator">•</span>
|
||||
<span class="post-reading-time">
|
||||
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
4 min
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="post-content-main">
|
||||
<h1 id="understanding-the-google-groups-spam-problem">Understanding the Google Groups Spam Problem</h1>
|
||||
<p>Over the past year, I’ve noticed a significant increase in spam messages originating from Google Groups. This issue stems from the way Google Groups is designed: It gives spammers an easy way to distribute large volumes of unwanted mail using legitimate Google mail servers, which makes filtering much harder.</p>
|
||||
<h2 id="why-it-happens">Why it Happens</h2>
|
||||
<p>There are a few fundamental problems with how Google Groups works that make it particularly attractive to spammers:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>No opt-in required</strong>: Spammers can freely add any email address to a Google Group without the recipient’s consent.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Unsubscribing is often impossible</strong>: Many spam groups are set to private, meaning that victims cannot even access the group page to unsubscribe.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Amplified spam through auto-responders</strong>: Some of these groups include automated mail systems (like ticketing or vacation responders). When these systems reply to the initial spam message, the responses are redistributed to all group members multiplying the traffic in unwanted mails.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="the-challenge-and-approach">The Challenge and Approach</h2>
|
||||
<p>While analyzing these messages, I discovered that several legitimate organizations also use Google Groups to distribute newsletters or announcements. That means simply blocking all mail coming from Google Groups would cause false positives and disrupt valid communication.</p>
|
||||
<p>To handle this more effectively, I developed a solution that integrates directly with Rspamd, our spam filtering system:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>Custom Lua plugin</strong>: Detects messages originating from Google Groups and assigns a custom symbol.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Composite rules</strong>: Use this symbol, combined with other spam indicators, to decide whether a message should be classified as spam.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>This approach allows us to target the abusive patterns specifically, without penalizing legitimate use of Google Groups.</p>
|
||||
<h2 id="technical-configuration">Technical Configuration</h2>
|
||||
<p>To tackle the Google Groups spam issue, I built a small custom Lua plugin for Rspamd that detects messages originating from Google Groups and assigns a custom symbol to them. Once tagged, we can use composite rules to decide whether a message should be treated as spam based on additional indicators.</p>
|
||||
<h3 id="step-1-create-the-custom-lua-plugin">Step 1: Create the Custom Lua Plugin</h3>
|
||||
<p>Start by creating a new file at <code>/etc/rspamd/plugins.d/kits_header_google_group.lua</code>:</p>
|
||||
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="n">rspamd_config</span><span class="p">:</span><span class="n">register_symbol</span><span class="p">{</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="s2">"KITS_HEADER_GOOGLE_GROUP"</span><span class="p">,</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="n">score</span> <span class="o">=</span> <span class="mf">0.1</span><span class="p">,</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="n">group</span> <span class="o">=</span> <span class="s2">"headers"</span><span class="p">,</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="n">description</span> <span class="o">=</span> <span class="s2">"Message contains X-Google-Group-Id header or List-Unsubscribe header with googlegroups"</span><span class="p">,</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="n">callback</span> <span class="o">=</span> <span class="kr">function</span><span class="p">(</span><span class="n">task</span><span class="p">)</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="c1">-- Check for X-Google-Group-Id header</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="kr">if</span> <span class="n">task</span><span class="p">:</span><span class="n">get_header</span><span class="p">(</span><span class="s1">'X-Google-Group-Id'</span><span class="p">)</span> <span class="kr">then</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="kr">return</span> <span class="kc">true</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="kr">end</span>
|
||||
</span></span><span class="line"><span class="cl">
|
||||
</span></span><span class="line"><span class="cl"> <span class="c1">-- Check for List-Unsubscribe header containing 'googlegroups'</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="kd">local</span> <span class="n">list_unsubscribe</span> <span class="o">=</span> <span class="n">task</span><span class="p">:</span><span class="n">get_header</span><span class="p">(</span><span class="s1">'List-Unsubscribe'</span><span class="p">)</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="kr">if</span> <span class="n">list_unsubscribe</span> <span class="ow">and</span> <span class="n">string.find</span><span class="p">(</span><span class="n">list_unsubscribe</span><span class="p">:</span><span class="n">lower</span><span class="p">(),</span> <span class="s1">'googlegroups'</span><span class="p">)</span> <span class="kr">then</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="kr">return</span> <span class="kc">true</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="kr">end</span>
|
||||
</span></span><span class="line"><span class="cl">
|
||||
</span></span><span class="line"><span class="cl"> <span class="kr">return</span> <span class="kc">false</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="kr">end</span>
|
||||
</span></span><span class="line"><span class="cl"><span class="p">}</span>
|
||||
</span></span></code></pre></div><p>This plugin checks for either of the following headers:</p>
|
||||
<ul>
|
||||
<li><code>X-Google-Group-Id</code></li>
|
||||
<li><code>List-Unsubscribe</code> containing the string <code>googlegroups</code></li>
|
||||
</ul>
|
||||
<p>If either is present, the message is tagged with the symbol <code>KITS_HEADER_GOOGLE_GROUP</code>.</p>
|
||||
<h3 id="step-2-enable-the-plugin">Step 2: Enable the Plugin</h3>
|
||||
<p>Next, register a module with the same name by creating an empty configuration file at <code>/etc/rspamd/modules.d/kits_header_google_group.conf</code>:</p>
|
||||
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Empty config to enable lua plugin</span>
|
||||
</span></span><span class="line"><span class="cl">kits_header_google_group <span class="o">{</span> <span class="o">}</span>
|
||||
</span></span></code></pre></div><p>At this point, every email that originates from a Google Group will be tagged with the symbol <code>KITS_HEADER_GOOGLE_GROUP</code> and adds a score of 0.1.</p>
|
||||
<h3 id="step-3-create-composite-rules">Step 3: Create Composite Rules</h3>
|
||||
<p>The next step is to define composite rules that evaluate whether a tagged message is likely to be spam. Create the following entries in <code>/etc/rspamd/override.d/composite.conf</code>:</p>
|
||||
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Google Group origin with bulk or freemail origin</span>
|
||||
</span></span><span class="line"><span class="cl">KITS_GOOGLE_GROUP_BAD <span class="o">{</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="nv">expression</span> <span class="o">=</span> <span class="s2">"KITS_HEADER_GOOGLE_GROUP and (DCC_REJECT | FUZZY_BULK | FREEMAIL_FROM)"</span><span class="p">;</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="nv">score</span> <span class="o">=</span> 8.0<span class="p">;</span>
|
||||
</span></span><span class="line"><span class="cl"><span class="o">}</span>
|
||||
</span></span><span class="line"><span class="cl">
|
||||
</span></span><span class="line"><span class="cl"><span class="c1"># Google Group origin with bulk and freemail origin</span>
|
||||
</span></span><span class="line"><span class="cl">KITS_GOOGLE_GROUP_WORST <span class="o">{</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="nv">expression</span> <span class="o">=</span> <span class="s2">"KITS_HEADER_GOOGLE_GROUP and (DCC_REJECT | FUZZY_BULK ) and FREEMAIL_FROM"</span><span class="p">;</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="nv">score</span> <span class="o">=</span> 20.0<span class="p">;</span>
|
||||
</span></span><span class="line"><span class="cl"><span class="o">}</span>
|
||||
</span></span></code></pre></div><p>These rules assign higher scores to messages that originate from Google Groups and match known spam indicators such as <code>DCC_REJECT</code>, <code>FUZZY_BULK</code>, or <code>FREEMAIL_FROM</code>.</p>
|
||||
|
||||
|
||||
|
||||
<blockquote class="blockquote-regular">
|
||||
<p>The naming scheme, scores, and logic here are tailored to my environment. Always adjust the scoring and expressions to fit your setup and verify changes before applying them.</p>
|
||||
|
||||
</blockquote>
|
||||
|
||||
<h2 id="conclusion">Conclusion</h2>
|
||||
<p>After deploying this configuration, the amount of spam originating from Google Groups dropped noticeably. Legitimate messages from companies still passed through correctly, while unwanted group spam was effectively flagged or quarantined by Rspamd.</p>
|
||||
<p>If you’re running a mail server, fighting spam is one of the more tedious and ongoing challenges. Thankfully, with Rspamd, we have some of the best and most flexible spam-fighting tools available.</p>
|
||||
<p>Battling spam will always be a cat-and-mouse game, and I’m sure spammers will find new and clever ways to distribute unwanted mail sooner rather than later, but Rspamd will be here to help. I hope to share more useful posts in the future about keeping our mail systems clean with Rspamd.</p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<nav class="post-navigation" aria-label="Post navigation">
|
||||
|
||||
<a
|
||||
href="/posts/blocking-invalid-rcpt-postfix/"
|
||||
class="nav-link nav-prev"
|
||||
aria-label="Previous post: Blocking Invalid Recipients Before They Reach Your Exchange Server">
|
||||
<span class="nav-label">Previous</span>
|
||||
<span class="nav-title">Blocking Invalid Recipients Before They Reach Your Exchange Server</span>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a
|
||||
href="/posts/spaceship-distrobox/"
|
||||
class="nav-link nav-next"
|
||||
aria-label="Next post: Spaceship Distrobox">
|
||||
<span class="nav-label">Next</span>
|
||||
<span class="nav-title">Spaceship Distrobox</span>
|
||||
</a>
|
||||
|
||||
</nav>
|
||||
</article>
|
||||
|
||||
|
||||
<aside class="post-toc" id="post-toc" aria-label="Table of contents">
|
||||
<button
|
||||
class="toc-toggle"
|
||||
id="toc-toggle"
|
||||
aria-expanded="true"
|
||||
aria-controls="toc-content"
|
||||
aria-label="Toggle table of contents">
|
||||
<svg
|
||||
class="toc-burger-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
<span class="toc-toggle-text">Table of Contents</span>
|
||||
<svg
|
||||
class="toc-chevron-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="toc-content" id="toc-content">
|
||||
<nav class="toc-nav" aria-label="Table of contents">
|
||||
<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#why-it-happens">Why it Happens</a></li>
|
||||
<li><a href="#the-challenge-and-approach">The Challenge and Approach</a></li>
|
||||
<li><a href="#technical-configuration">Technical Configuration</a>
|
||||
<ul>
|
||||
<li><a href="#step-1-create-the-custom-lua-plugin">Step 1: Create the Custom Lua Plugin</a></li>
|
||||
<li><a href="#step-2-enable-the-plugin">Step 2: Enable the Plugin</a></li>
|
||||
<li><a href="#step-3-create-composite-rules">Step 3: Create Composite Rules</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#conclusion">Conclusion</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<footer class="site-footer">
|
||||
<div class="footer-content">
|
||||
<p class="footer-text">
|
||||
©
|
||||
|
||||
2026
|
||||
|
||||
Demians Blog.
|
||||
|
||||
Built with Hugo and Mana ❤️
|
||||
|
||||
</p>
|
||||
|
||||
<div class="footer-social">
|
||||
<div class="social-links">
|
||||
|
||||
<a
|
||||
href="https://github.com/pyte1"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="social-link"
|
||||
aria-label="GitHub">
|
||||
<svg fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="mailto:demian%20%28at%29%20pyte%20%28dot%29%20dev" class="social-link" aria-label="Email">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</footer>
|
||||
|
||||
<div id="search-modal" class="search-modal" aria-hidden="true" role="dialog" aria-label="Search">
|
||||
<div class="search-modal-backdrop" id="search-modal-backdrop"></div>
|
||||
<div class="search-modal-container">
|
||||
<div class="search-input-wrapper">
|
||||
<svg class="search-input-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
<input
|
||||
type="search"
|
||||
id="search-input"
|
||||
class="search-input"
|
||||
placeholder="Search..."
|
||||
autocomplete="off"
|
||||
aria-label="Search input">
|
||||
<button class="search-input-clear" id="search-input-clear" aria-label="Clear search">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="search-modal-close" id="search-modal-close" aria-label="Close search">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div id="search-results" class="search-results"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<button id="scroll-to-top" class="scroll-to-top" aria-label="Scroll to top" type="button">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M5 10l7-7m0 0l7 7m-7-7v18"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="/js/main.min.e60ab79dca7b920b4dc5cf3163ad5ce8794839b60f27778db65782f087be3e27.js" defer></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
+887
-223
File diff suppressed because it is too large
Load Diff
+35
-16
@@ -1,28 +1,47 @@
|
||||
<?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/">
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>Posts on PyteDev Blog</title>
|
||||
<title>Posts on Demians Blog</title>
|
||||
<link>https://pyte.dev/posts/</link>
|
||||
<description>Recent content in Posts on PyteDev Blog</description>
|
||||
<generator>Hugo -- 0.147.2</generator>
|
||||
<language>en-us</language>
|
||||
<lastBuildDate>Sun, 11 May 2025 20:13:49 +0200</lastBuildDate>
|
||||
<description>Recent content in Posts on Demians Blog</description>
|
||||
<generator>Hugo</generator>
|
||||
<language>de-DE</language>
|
||||
<lastBuildDate>Sun, 04 Jan 2026 20:33:45 +0100</lastBuildDate>
|
||||
<atom:link href="https://pyte.dev/posts/index.xml" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>Spaceship Distrobox</title>
|
||||
<link>https://pyte.dev/posts/spaceship-distrobox/</link>
|
||||
<pubDate>Sun, 04 Jan 2026 20:33:45 +0100</pubDate>
|
||||
<guid>https://pyte.dev/posts/spaceship-distrobox/</guid>
|
||||
<description><h1 id="how-i-solved-the-distrobox-confusion-in-my-terminal">How I Solved the Distrobox Confusion in My Terminal</h1>
<p>Recently, I found myself struggling with something that seemed like a simple problem: when I was working with multiple terminal windows, I could never remember which one was inside a Distrobox and which one was on my local system. If you&rsquo;ve ever juggled between different environments, you know exactly how annoying this can get.</p>
<p>So, I decided to create a quick plugin for my favorite terminal prompt, <strong>Spaceship</strong>, that would clearly indicate when I&rsquo;m inside a Distrobox container. The result is a simple, customizable section that appears right in your prompt, showing the name of the active container. This way, I never have to guess or dig deeper to figure out where I&rsquo;m working.</p></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Google Groups Spam</title>
|
||||
<link>https://pyte.dev/posts/google-groups-spam/</link>
|
||||
<pubDate>Fri, 10 Oct 2025 09:26:56 +0200</pubDate>
|
||||
<guid>https://pyte.dev/posts/google-groups-spam/</guid>
|
||||
<description><h1 id="understanding-the-google-groups-spam-problem">Understanding the Google Groups Spam Problem</h1>
<p>Over the past year, I&rsquo;ve noticed a significant increase in spam messages originating from Google Groups. This issue stems from the way Google Groups is designed: It gives spammers an easy way to distribute large volumes of unwanted mail using legitimate Google mail servers, which makes filtering much harder.</p>
<h2 id="why-it-happens">Why it Happens</h2>
<p>There are a few fundamental problems with how Google Groups works that make it particularly attractive to spammers:</p></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Blocking Invalid Recipients Before They Reach Your Exchange Server</title>
|
||||
<link>https://pyte.dev/posts/blocking-invalid-rcpt-postfix/</link>
|
||||
<pubDate>Fri, 01 Aug 2025 10:03:15 +0200</pubDate>
|
||||
<guid>https://pyte.dev/posts/blocking-invalid-rcpt-postfix/</guid>
|
||||
<description><p>Recently, I had to deal with a serious problem: <strong>backscatter</strong>.<br>
One of our mail gateways ended up listed on the <strong>backscatter.org</strong> blacklist for sending bounce messages to forged senders.</p>
<p>After checking the logs, I quickly realized that our system wasn&rsquo;t actually protected against backscatter attacks, so I had to do something about it.</p>
<h2 id="what-even-is-backscatter">What Even Is Backscatter?</h2>
<p>Backscatter is unwanted email that your mail server sends <strong>after</strong> receiving a message, usually in the form of a <strong>non-delivery report (NDR)</strong> or <strong>bounce</strong> to a <strong>forged sender address</strong>.</p></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Dovecot Index Cache Issues</title>
|
||||
<link>https://pyte.dev/posts/dovecot-index-cache-issues/</link>
|
||||
<pubDate>Sun, 18 May 2025 11:34:09 +0200</pubDate>
|
||||
<guid>https://pyte.dev/posts/dovecot-index-cache-issues/</guid>
|
||||
<description><h1 id="understanding-dovecotindexcache">Understanding <code>dovecot.index.cache</code></h1>
<p>I recently migrated an old mail server system into its new home. After the migration, I checked the logs and noticed some warnings that looked like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">May</span> <span class="mi">17</span> <span class="mi">11</span><span class="p">:</span><span class="mi">23</span><span class="p">:</span><span class="mi">13</span> <span class="n">server1</span> <span class="n">dovecot</span><span class="p">:</span> <span class="n">dsync</span><span class="o">-</span><span class="n">local</span><span class="p">(</span><span class="n">user</span><span class="err">@</span><span class="n">domain</span><span class="o">.</span><span class="n">tld</span><span class="p">)</span><span class="o">&lt;</span><span class="n">cRjZCwGnKWiIvicA2dm5Tw</span><span class="o">&gt;</span><span class="p">:</span> <span class="n">Error</span><span class="p">:</span> <span class="n">Mailbox</span> <span class="n">INBOX</span><span class="p">:</span> <span class="n">mmap</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mi">511310568</span><span class="p">)</span> <span class="n">failed</span> <span class="n">with</span> <span class="n">file</span> <span class="o">/</span><span class="k">var</span><span class="o">/</span><span class="n">vmail</span><span class="o">/</span><span class="n">domain</span><span class="o">.</span><span class="n">tld</span><span class="o">/</span><span class="n">user</span><span class="o">/</span><span class="n">Maildir</span><span class="o">/</span><span class="n">dovecot</span><span class="o">.</span><span class="n">index</span><span class="o">.</span><span class="n">cache</span><span class="p">:</span> <span class="n">Cannot</span> <span class="n">allocate</span> <span class="n">memory</span>
</span></span></code></pre></div><p>The error indicates that the <code>dovecot.index.cache</code> file is too big to process, and Dovecot cannot allocate enough memory to handle it.</p>
<h1 id="what-are-dovecotindexcache-files">What Are <code>dovecot.index.cache</code> Files?</h1>
<p>Dovecot, the most popular IMAP server, uses a set of index files (<code>dovecot.index</code>, <code>dovecot.index.cache</code>, <code>dovecot.index.log</code>, etc.) to speed up mailbox access. The file I had to deal with stores cached message metadata (headers, flags, and preview text) so Dovecot does not have to read each message file in the mailbox every time.</p></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Introduction</title>
|
||||
<link>https://pyte.dev/posts/my-first-post/</link>
|
||||
<pubDate>Sun, 11 May 2025 20:13:49 +0200</pubDate>
|
||||
<guid>https://pyte.dev/posts/my-first-post/</guid>
|
||||
<description><h1 id="welcome-to-my-blog">Welcome to My Blog!</h1>
|
||||
<p>I’m <strong>Demian</strong>, a <strong>Sysadmin</strong>, <strong>Email Infrastructure enthusiast</strong>, and a passionate <strong>Open Source contributor</strong>. This is where I’ll be sharing what I know about system administration, managing email infrastructure, and contributing to open source projects.</p>
|
||||
<h2 id="what-to-expect">What to Expect</h2>
|
||||
<p>On this blog, I’ll be writing about:</p>
|
||||
<ul>
|
||||
<li><strong>Sysadmin tips and tools</strong>: Everything I’ve learned managing servers, networks, and infrastructure.</li>
|
||||
<li><strong>Email Infrastructure</strong>: Best practices for setting up, securing, and managing email systems.</li>
|
||||
<li><strong>Open Source</strong>: How I contribute to open source projects, and tips for getting started if you want to do the same.</li>
|
||||
</ul>
|
||||
<p>This blog is a space to share knowledge, troubleshoot common issues, and explore new tools and techniques. Whether you’re just getting into system administration, looking for email setup guides, or interested in contributing to open source, you’ll find something useful here.</p></description>
|
||||
<description><h1 id="welcome-to-my-blog">Welcome to My Blog!</h1>
<p>I’m <strong>Demian</strong>, a <strong>Sysadmin</strong>, <strong>Email Infrastructure enthusiast</strong>, and a passionate <strong>Open Source contributor</strong>. This is where I’ll be sharing what I know about system administration, managing email infrastructure, and contributing to open source projects.</p>
<h2 id="what-to-expect">What to Expect</h2>
<p>On this blog, I’ll be writing about:</p>
<ul>
<li><strong>Sysadmin tips and tools</strong>: Everything I’ve learned managing servers, networks, and infrastructure.</li>
<li><strong>Email Infrastructure</strong>: Best practices for setting up, securing, and managing email systems.</li>
<li><strong>Open Source</strong>: How I contribute to open source projects, and tips for getting started if you want to do the same.</li>
</ul>
<p>This blog is a space to share knowledge, troubleshoot common issues, and explore new tools and techniques. Whether you’re just getting into system administration, looking for email setup guides, or interested in contributing to open source, you’ll find something useful here.</p></description>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
||||
|
||||
@@ -1,218 +1,325 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="auto">
|
||||
|
||||
<head><meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="robots" content="index, follow">
|
||||
<title>Introduction | PyteDev Blog</title>
|
||||
<meta name="keywords" content="">
|
||||
<meta name="description" content="Welcome to My Blog!
|
||||
I’m Demian, a Sysadmin, Email Infrastructure enthusiast, and a passionate Open Source contributor. This is where I’ll be sharing what I know about system administration, managing email infrastructure, and contributing to open source projects.
|
||||
What to Expect
|
||||
On this blog, I’ll be writing about:
|
||||
|
||||
Sysadmin tips and tools: Everything I’ve learned managing servers, networks, and infrastructure.
|
||||
Email Infrastructure: Best practices for setting up, securing, and managing email systems.
|
||||
Open Source: How I contribute to open source projects, and tips for getting started if you want to do the same.
|
||||
|
||||
This blog is a space to share knowledge, troubleshoot common issues, and explore new tools and techniques. Whether you’re just getting into system administration, looking for email setup guides, or interested in contributing to open source, you’ll find something useful here.">
|
||||
<meta name="author" content="">
|
||||
<link rel="canonical" href="https://pyte.dev/posts/my-first-post/">
|
||||
<link crossorigin="anonymous" href="/assets/css/stylesheet.f49d66caae9ea0fd43f21f29e71a8d3e284517ed770f2aa86fa012953ad3c9ef.css" integrity="sha256-9J1myq6eoP1D8h8p5xqNPihFF+13Dyqob6ASlTrTye8=" rel="preload stylesheet" as="style">
|
||||
<link rel="icon" href="https://pyte.dev/favicon.ico">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="https://pyte.dev/favicon-16x16.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="https://pyte.dev/favicon-32x32.png">
|
||||
<link rel="apple-touch-icon" href="https://pyte.dev/apple-touch-icon.png">
|
||||
<link rel="mask-icon" href="https://pyte.dev/safari-pinned-tab.svg">
|
||||
<meta name="theme-color" content="#2e2e33">
|
||||
<meta name="msapplication-TileColor" content="#2e2e33">
|
||||
<link rel="alternate" hreflang="en" href="https://pyte.dev/posts/my-first-post/">
|
||||
<noscript>
|
||||
<style>
|
||||
#theme-toggle,
|
||||
.top-link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<style>
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--theme: rgb(29, 30, 32);
|
||||
--entry: rgb(46, 46, 51);
|
||||
--primary: rgb(218, 218, 219);
|
||||
--secondary: rgb(155, 156, 157);
|
||||
--tertiary: rgb(65, 66, 68);
|
||||
--content: rgb(196, 196, 197);
|
||||
--code-block-bg: rgb(46, 46, 51);
|
||||
--code-bg: rgb(55, 56, 62);
|
||||
--border: rgb(51, 51, 51);
|
||||
}
|
||||
|
||||
.list {
|
||||
background: var(--theme);
|
||||
}
|
||||
|
||||
.list:not(.dark)::-webkit-scrollbar-track {
|
||||
background: 0 0;
|
||||
}
|
||||
|
||||
.list:not(.dark)::-webkit-scrollbar-thumb {
|
||||
border-color: var(--theme);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
</noscript><meta property="og:url" content="https://pyte.dev/posts/my-first-post/">
|
||||
<meta property="og:site_name" content="PyteDev Blog">
|
||||
<meta property="og:title" content="Introduction">
|
||||
<meta property="og:description" content="Welcome to My Blog! I’m Demian, a Sysadmin, Email Infrastructure enthusiast, and a passionate Open Source contributor. This is where I’ll be sharing what I know about system administration, managing email infrastructure, and contributing to open source projects.
|
||||
What to Expect On this blog, I’ll be writing about:
|
||||
Sysadmin tips and tools: Everything I’ve learned managing servers, networks, and infrastructure. Email Infrastructure: Best practices for setting up, securing, and managing email systems. Open Source: How I contribute to open source projects, and tips for getting started if you want to do the same. This blog is a space to share knowledge, troubleshoot common issues, and explore new tools and techniques. Whether you’re just getting into system administration, looking for email setup guides, or interested in contributing to open source, you’ll find something useful here.">
|
||||
<meta property="og:locale" content="en-us">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="article:section" content="posts">
|
||||
<meta property="article:published_time" content="2025-05-11T20:13:49+02:00">
|
||||
<meta property="article:modified_time" content="2025-05-11T20:13:49+02:00">
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:title" content="Introduction">
|
||||
<meta name="twitter:description" content="Welcome to My Blog!
|
||||
I’m Demian, a Sysadmin, Email Infrastructure enthusiast, and a passionate Open Source contributor. This is where I’ll be sharing what I know about system administration, managing email infrastructure, and contributing to open source projects.
|
||||
What to Expect
|
||||
On this blog, I’ll be writing about:
|
||||
|
||||
Sysadmin tips and tools: Everything I’ve learned managing servers, networks, and infrastructure.
|
||||
Email Infrastructure: Best practices for setting up, securing, and managing email systems.
|
||||
Open Source: How I contribute to open source projects, and tips for getting started if you want to do the same.
|
||||
|
||||
This blog is a space to share knowledge, troubleshoot common issues, and explore new tools and techniques. Whether you’re just getting into system administration, looking for email setup guides, or interested in contributing to open source, you’ll find something useful here.">
|
||||
|
||||
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BreadcrumbList",
|
||||
"itemListElement": [
|
||||
{
|
||||
"@type": "ListItem",
|
||||
"position": 1 ,
|
||||
"name": "Posts",
|
||||
"item": "https://pyte.dev/posts/"
|
||||
},
|
||||
{
|
||||
"@type": "ListItem",
|
||||
"position": 2 ,
|
||||
"name": "Introduction",
|
||||
"item": "https://pyte.dev/posts/my-first-post/"
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BlogPosting",
|
||||
"headline": "Introduction",
|
||||
"name": "Introduction",
|
||||
"description": "Welcome to My Blog! I’m Demian, a Sysadmin, Email Infrastructure enthusiast, and a passionate Open Source contributor. This is where I’ll be sharing what I know about system administration, managing email infrastructure, and contributing to open source projects.\nWhat to Expect On this blog, I’ll be writing about:\nSysadmin tips and tools: Everything I’ve learned managing servers, networks, and infrastructure. Email Infrastructure: Best practices for setting up, securing, and managing email systems. Open Source: How I contribute to open source projects, and tips for getting started if you want to do the same. This blog is a space to share knowledge, troubleshoot common issues, and explore new tools and techniques. Whether you’re just getting into system administration, looking for email setup guides, or interested in contributing to open source, you’ll find something useful here.\n",
|
||||
"keywords": [
|
||||
|
||||
],
|
||||
"articleBody": "Welcome to My Blog! I’m Demian, a Sysadmin, Email Infrastructure enthusiast, and a passionate Open Source contributor. This is where I’ll be sharing what I know about system administration, managing email infrastructure, and contributing to open source projects.\nWhat to Expect On this blog, I’ll be writing about:\nSysadmin tips and tools: Everything I’ve learned managing servers, networks, and infrastructure. Email Infrastructure: Best practices for setting up, securing, and managing email systems. Open Source: How I contribute to open source projects, and tips for getting started if you want to do the same. This blog is a space to share knowledge, troubleshoot common issues, and explore new tools and techniques. Whether you’re just getting into system administration, looking for email setup guides, or interested in contributing to open source, you’ll find something useful here.\nWhy I’m Here I’ve been working in IT for a while now, and I’ve learned a lot by trial and error. Writing about my experiences helps me remember the lessons I’ve learned and hopefully helps others along the way. I believe in learning by doing, and this blog is just a reflection of that approach.\nI’m excited to share what I’ve picked up, and I hope you’ll find these posts useful, whether you’re a fellow sysadmin or just someone interested in these topics.\nLet’s Connect Feel free to reach out. Thanks for stopping by, and I look forward to sharing more soon!\n",
|
||||
"wordCount" : "236",
|
||||
"inLanguage": "en",
|
||||
"datePublished": "2025-05-11T20:13:49+02:00",
|
||||
"dateModified": "2025-05-11T20:13:49+02:00",
|
||||
"mainEntityOfPage": {
|
||||
"@type": "WebPage",
|
||||
"@id": "https://pyte.dev/posts/my-first-post/"
|
||||
},
|
||||
"publisher": {
|
||||
"@type": "Organization",
|
||||
"name": "PyteDev Blog",
|
||||
"logo": {
|
||||
"@type": "ImageObject",
|
||||
"url": "https://pyte.dev/favicon.ico"
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body class="" id="top">
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="de-DE"
|
||||
dir="ltr"
|
||||
class="h-full">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="language" content="de-DE">
|
||||
<script>
|
||||
if (localStorage.getItem("pref-theme") === "dark") {
|
||||
document.body.classList.add('dark');
|
||||
} else if (localStorage.getItem("pref-theme") === "light") {
|
||||
document.body.classList.remove('dark')
|
||||
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
document.body.classList.add('dark');
|
||||
|
||||
(function () {
|
||||
const storedTheme = localStorage.getItem("theme");
|
||||
const systemPrefersLight = window.matchMedia("(prefers-color-scheme: light)").matches;
|
||||
const theme = storedTheme || (systemPrefersLight ? "light" : "dark");
|
||||
document.documentElement.setAttribute("data-theme", theme);
|
||||
})();
|
||||
</script>
|
||||
<title>Introduction | Demians Blog</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="
|
||||
Welcome to My Blog! I’m Demian, a Sysadmin, Email Infrastructure enthusiast, and a passionate Open Source contributor. This is where I’ll be sharing what I know …
|
||||
|
||||
">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="canonical" href="https://pyte.dev/posts/my-first-post/">
|
||||
|
||||
<meta name="robots" content="index, follow">
|
||||
|
||||
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:title" content="Introduction | Demians Blog">
|
||||
<meta property="og:description" content="Welcome to My Blog! I’m Demian, a Sysadmin, Email Infrastructure enthusiast, and a passionate Open Source contributor. This is where I’ll be sharing what I know …">
|
||||
<meta property="og:url" content="https://pyte.dev/posts/my-first-post/">
|
||||
<meta property="og:site_name" content="Demians Blog"><meta property="og:image" content="https://pyte.dev/assets/patrick.png">
|
||||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="630"><meta property="article:published_time" content="2025-05-11T20:13:49+02:00">
|
||||
<meta
|
||||
property="article:modified_time"
|
||||
content="2025-05-11T20:13:49+02:00">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Introduction | Demians Blog">
|
||||
<meta name="twitter:description" content="Welcome to My Blog! I’m Demian, a Sysadmin, Email Infrastructure enthusiast, and a passionate Open Source contributor. This is where I’ll be sharing what I know …"><meta name="twitter:image" content="https://pyte.dev/assets/patrick.png">
|
||||
|
||||
<script type="application/ld+json">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"{\"@context\":\"https://schema.org\",\"@type\":\"BlogPosting\",\"author\":{\"@type\":\"Person\",\"email\":\"demian (at) pyte (dot) dev\",\"name\":\"Demians Blog\"},\"dateModified\":\"2025-05-11T20:13:49+02:00\",\"datePublished\":\"2025-05-11T20:13:49+02:00\",\"description\":\"Welcome to My Blog! I’m Demian, a Sysadmin, Email Infrastructure enthusiast, and a passionate Open Source contributor. This is where I’ll be sharing what I know …\",\"headline\":\"Introduction\",\"image\":\"https://pyte.dev/assets/patrick.png\",\"mainEntityOfPage\":{\"@id\":\"https://pyte.dev/posts/my-first-post/\",\"@type\":\"WebPage\"},\"publisher\":{\"@type\":\"Organization\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://pyte.dev/assets/patrick.png\"},\"name\":\"Demians Blog\"}}"</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="application/ld+json">"{\"@context\":\"https://schema.org\",\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/\",\"name\":\"Demians Blog\",\"position\":1},{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/posts/\",\"name\":\"Posts\",\"position\":2},{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/posts/my-first-post/\",\"name\":\"Introduction\",\"position\":3}]}"</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Outfit:wght@400;500;600;700;800;900&family=Space+Grotesk:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet">
|
||||
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/css/theme.min.86a32b729f656fc6c119ed69193950db815a3ad9fdc967b32c76d602f11449a4.css"
|
||||
integrity="sha256-hqMrcp9lb8bBGe1pGTlQ24FaOtn9yWezLHbWAvEUSaQ=">
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/css/syntax-dark.min.3e403a03e3af837b3829e9b6f01fc7792bda7cb7f5056f5e9786109545c6b2e1.css"
|
||||
integrity="sha256-PkA6A+Ovg3s4Kem28B/HeSvafLf1BW9el4YQlUXGsuE="
|
||||
id="syntax-dark-theme"
|
||||
class="syntax-theme"><link
|
||||
rel="stylesheet"
|
||||
href="/css/syntax-light.min.d0d33b879698595e6b2c0f75f0cea95a8517fb0150570cd0ee4dc42e25c8d147.css"
|
||||
integrity="sha256-0NM7h5aYWV5rLA918M6pWoUX+wFQVwzQ7k3ELiXI0Uc="
|
||||
id="syntax-light-theme"
|
||||
class="syntax-theme"
|
||||
disabled><script>
|
||||
|
||||
(function () {
|
||||
const storedTheme = localStorage.getItem("theme");
|
||||
const systemPrefersLight = window.matchMedia("(prefers-color-scheme: light)").matches;
|
||||
const theme = storedTheme || (systemPrefersLight ? "light" : "dark");
|
||||
|
||||
const syntaxDark = document.getElementById("syntax-dark-theme");
|
||||
const syntaxLight = document.getElementById("syntax-light-theme");
|
||||
|
||||
if (theme === "light") {
|
||||
if (syntaxDark) syntaxDark.disabled = true;
|
||||
if (syntaxLight) syntaxLight.disabled = false;
|
||||
} else {
|
||||
if (syntaxDark) syntaxDark.disabled = false;
|
||||
if (syntaxLight) syntaxLight.disabled = true;
|
||||
}
|
||||
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
const currentTheme = document.documentElement.getAttribute("data-theme");
|
||||
if (currentTheme === "light") {
|
||||
if (syntaxDark) syntaxDark.disabled = true;
|
||||
if (syntaxLight) syntaxLight.disabled = false;
|
||||
} else {
|
||||
if (syntaxDark) syntaxDark.disabled = false;
|
||||
if (syntaxLight) syntaxLight.disabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-theme"],
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
<header class="header">
|
||||
<nav class="nav">
|
||||
<div class="logo">
|
||||
<a href="https://pyte.dev/" accesskey="h" title="PyteDev Blog (Alt + H)">PyteDev Blog</a>
|
||||
<div class="logo-switches">
|
||||
<button id="theme-toggle" accesskey="t" title="(Alt + T)" aria-label="Toggle theme">
|
||||
<svg id="moon" xmlns="http://www.w3.org/2000/svg" width="24" height="18" viewBox="0 0 24 24"
|
||||
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
<svg id="sun" xmlns="http://www.w3.org/2000/svg" width="24" height="18" viewBox="0 0 24 24"
|
||||
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<ul id="menu">
|
||||
<li>
|
||||
<a href="https://pyte.dev/" title="Home">
|
||||
<span>Home</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://pyte.dev/posts/" title="Blog">
|
||||
<span>Blog</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://pyte.dev/search/" title="Search">
|
||||
<span>Search</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
<main class="main">
|
||||
|
||||
<article class="post-single">
|
||||
<header class="post-header">
|
||||
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/css/bundle.min.783a79746a859af9be598cbc33fba2a1087434b23768524277b07ef28336f113.css"
|
||||
integrity="sha256-eDp5dGqFmvm+WYy8M/uioQh0NLI3aFJCd7B+8oM28RM=">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
|
||||
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/logo-transparent/favicon-32x32.png">
|
||||
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/logo-transparent/favicon-16x16.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/logo-transparent/apple-touch-icon.png">
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="192x192"
|
||||
href="/favicon/logo-transparent/android-chrome-192x192.png">
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="512x512"
|
||||
href="/favicon/logo-transparent/android-chrome-512x512.png">
|
||||
<link rel="manifest" href="/favicon/logo-transparent/site.webmanifest">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
<body class="flex flex-col min-h-screen">
|
||||
<a href="#main-content" class="skip-to-main" aria-label="Skip to main content"
|
||||
>Skip to main content</a
|
||||
>
|
||||
<header class="sticky-header">
|
||||
<div class="header-container">
|
||||
<nav class="header-nav" role="navigation" aria-label="Main navigation">
|
||||
<div class="header-content">
|
||||
<div class="header-logo">
|
||||
<a href="/" class="logo-link" aria-label="Home - Demians Blog">
|
||||
Demians Blog
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="header-menu">
|
||||
<ul class="menu-list">
|
||||
|
||||
<li class="menu-item">
|
||||
<a
|
||||
href="/"
|
||||
class="menu-link ">
|
||||
Home
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="menu-item">
|
||||
<a
|
||||
href="/posts/"
|
||||
class="menu-link ">
|
||||
Blog
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<button id="search-toggle" class="search-toggle" aria-label="Search" type="button">
|
||||
<svg class="search-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button id="theme-toggle" class="theme-toggle" aria-label="Toggle theme" type="button">
|
||||
<svg class="icon-sun" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"></path>
|
||||
</svg>
|
||||
<svg class="icon-moon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="main-content" class="flex-1" role="main">
|
||||
|
||||
<div class="single-post-wrapper">
|
||||
<article class="single-post">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="post-header">
|
||||
<h1 class="post-title-main">Introduction</h1>
|
||||
|
||||
|
||||
|
||||
<div class="post-meta">
|
||||
<div class="post-meta-info">
|
||||
<time datetime="2025-05-11T20:13:49+02:00" class="post-date">
|
||||
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
May 11, 2025
|
||||
</time>
|
||||
|
||||
<span class="meta-separator">•</span>
|
||||
|
||||
|
||||
<span class="post-word-count">
|
||||
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
||||
</svg>
|
||||
236 words
|
||||
</span>
|
||||
|
||||
<h1 class="post-title entry-hint-parent">
|
||||
Introduction
|
||||
</h1>
|
||||
<div class="post-meta"><span title='2025-05-11 20:13:49 +0200 CEST'>May 11, 2025</span>
|
||||
|
||||
|
||||
<span class="meta-separator">•</span>
|
||||
<span class="post-reading-time">
|
||||
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
2 min
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</header>
|
||||
<div class="post-content"><h1 id="welcome-to-my-blog">Welcome to My Blog!<a hidden class="anchor" aria-hidden="true" href="#welcome-to-my-blog">#</a></h1>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="post-content-main">
|
||||
<h1 id="welcome-to-my-blog">Welcome to My Blog!</h1>
|
||||
<p>I’m <strong>Demian</strong>, a <strong>Sysadmin</strong>, <strong>Email Infrastructure enthusiast</strong>, and a passionate <strong>Open Source contributor</strong>. This is where I’ll be sharing what I know about system administration, managing email infrastructure, and contributing to open source projects.</p>
|
||||
<h2 id="what-to-expect">What to Expect<a hidden class="anchor" aria-hidden="true" href="#what-to-expect">#</a></h2>
|
||||
<h2 id="what-to-expect">What to Expect</h2>
|
||||
<p>On this blog, I’ll be writing about:</p>
|
||||
<ul>
|
||||
<li><strong>Sysadmin tips and tools</strong>: Everything I’ve learned managing servers, networks, and infrastructure.</li>
|
||||
@@ -220,92 +327,205 @@ This blog is a space to share knowledge, troubleshoot common issues, and explore
|
||||
<li><strong>Open Source</strong>: How I contribute to open source projects, and tips for getting started if you want to do the same.</li>
|
||||
</ul>
|
||||
<p>This blog is a space to share knowledge, troubleshoot common issues, and explore new tools and techniques. Whether you’re just getting into system administration, looking for email setup guides, or interested in contributing to open source, you’ll find something useful here.</p>
|
||||
<h2 id="why-im-here">Why I’m Here<a hidden class="anchor" aria-hidden="true" href="#why-im-here">#</a></h2>
|
||||
<h2 id="why-im-here">Why I’m Here</h2>
|
||||
<p>I’ve been working in IT for a while now, and I’ve learned a lot by trial and error. Writing about my experiences helps me remember the lessons I’ve learned and hopefully helps others along the way. I believe in <strong>learning by doing</strong>, and this blog is just a reflection of that approach.</p>
|
||||
<p>I’m excited to share what I’ve picked up, and I hope you’ll find these posts useful, whether you’re a fellow sysadmin or just someone interested in these topics.</p>
|
||||
<hr>
|
||||
<h2 id="lets-connect">Let’s Connect<a hidden class="anchor" aria-hidden="true" href="#lets-connect">#</a></h2>
|
||||
<h2 id="lets-connect">Let’s Connect</h2>
|
||||
<p>Feel free to reach out. Thanks for stopping by, and I look forward to sharing more soon!</p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<nav class="post-navigation" aria-label="Post navigation">
|
||||
|
||||
|
||||
|
||||
<a
|
||||
href="/posts/dovecot-index-cache-issues/"
|
||||
class="nav-link nav-next"
|
||||
aria-label="Next post: Dovecot Index Cache Issues">
|
||||
<span class="nav-label">Next</span>
|
||||
<span class="nav-title">Dovecot Index Cache Issues</span>
|
||||
</a>
|
||||
|
||||
</nav>
|
||||
</article>
|
||||
|
||||
|
||||
<aside class="post-toc" id="post-toc" aria-label="Table of contents">
|
||||
<button
|
||||
class="toc-toggle"
|
||||
id="toc-toggle"
|
||||
aria-expanded="true"
|
||||
aria-controls="toc-content"
|
||||
aria-label="Toggle table of contents">
|
||||
<svg
|
||||
class="toc-burger-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
<span class="toc-toggle-text">Table of Contents</span>
|
||||
<svg
|
||||
class="toc-chevron-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="toc-content" id="toc-content">
|
||||
<nav class="toc-nav" aria-label="Table of contents">
|
||||
<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#what-to-expect">What to Expect</a></li>
|
||||
<li><a href="#why-im-here">Why I’m Here</a></li>
|
||||
<li><a href="#lets-connect">Let’s Connect</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<footer class="post-footer">
|
||||
<ul class="post-tags">
|
||||
</ul>
|
||||
</footer>
|
||||
</article>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<footer class="site-footer">
|
||||
<div class="footer-content">
|
||||
<p class="footer-text">
|
||||
©
|
||||
|
||||
2026
|
||||
|
||||
Demians Blog.
|
||||
|
||||
Built with Hugo and Mana ❤️
|
||||
|
||||
</p>
|
||||
|
||||
<footer class="footer">
|
||||
<span>© 2025 <a href="https://pyte.dev/">PyteDev Blog</a></span> ·
|
||||
<div class="footer-social">
|
||||
<div class="social-links">
|
||||
|
||||
<a
|
||||
href="https://github.com/pyte1"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="social-link"
|
||||
aria-label="GitHub">
|
||||
<svg fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
|
||||
<span>
|
||||
Powered by
|
||||
<a href="https://gohugo.io/" rel="noopener noreferrer" target="_blank">Hugo</a> &
|
||||
<a href="https://github.com/adityatelange/hugo-PaperMod/" rel="noopener" target="_blank">PaperMod</a>
|
||||
</span>
|
||||
|
||||
|
||||
|
||||
<a href="mailto:demian%20%28at%29%20pyte%20%28dot%29%20dev" class="social-link" aria-label="Email">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</footer>
|
||||
<a href="#top" aria-label="go to top" title="Go to Top (Alt + G)" class="top-link" id="top-link" accesskey="g">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 6" fill="currentColor">
|
||||
<path d="M12 6H0l6-6z" />
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<script>
|
||||
let menu = document.getElementById('menu')
|
||||
if (menu) {
|
||||
menu.scrollLeft = localStorage.getItem("menu-scroll-position");
|
||||
menu.onscroll = function () {
|
||||
localStorage.setItem("menu-scroll-position", menu.scrollLeft);
|
||||
}
|
||||
}
|
||||
</footer>
|
||||
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener("click", function (e) {
|
||||
e.preventDefault();
|
||||
var id = this.getAttribute("href").substr(1);
|
||||
if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
||||
document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView({
|
||||
behavior: "smooth"
|
||||
});
|
||||
} else {
|
||||
document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView();
|
||||
}
|
||||
if (id === "top") {
|
||||
history.replaceState(null, null, " ");
|
||||
} else {
|
||||
history.pushState(null, null, `#${id}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
<div id="search-modal" class="search-modal" aria-hidden="true" role="dialog" aria-label="Search">
|
||||
<div class="search-modal-backdrop" id="search-modal-backdrop"></div>
|
||||
<div class="search-modal-container">
|
||||
<div class="search-input-wrapper">
|
||||
<svg class="search-input-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
<input
|
||||
type="search"
|
||||
id="search-input"
|
||||
class="search-input"
|
||||
placeholder="Search..."
|
||||
autocomplete="off"
|
||||
aria-label="Search input">
|
||||
<button class="search-input-clear" id="search-input-clear" aria-label="Clear search">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="search-modal-close" id="search-modal-close" aria-label="Close search">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div id="search-results" class="search-results"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</script>
|
||||
<script>
|
||||
var mybutton = document.getElementById("top-link");
|
||||
window.onscroll = function () {
|
||||
if (document.body.scrollTop > 800 || document.documentElement.scrollTop > 800) {
|
||||
mybutton.style.visibility = "visible";
|
||||
mybutton.style.opacity = "1";
|
||||
} else {
|
||||
mybutton.style.visibility = "hidden";
|
||||
mybutton.style.opacity = "0";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
<script>
|
||||
document.getElementById("theme-toggle").addEventListener("click", () => {
|
||||
if (document.body.className.includes("dark")) {
|
||||
document.body.classList.remove('dark');
|
||||
localStorage.setItem("pref-theme", 'light');
|
||||
} else {
|
||||
document.body.classList.add('dark');
|
||||
localStorage.setItem("pref-theme", 'dark');
|
||||
}
|
||||
})
|
||||
<button id="scroll-to-top" class="scroll-to-top" aria-label="Scroll to top" type="button">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M5 10l7-7m0 0l7 7m-7-7v18"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="/js/main.min.e60ab79dca7b920b4dc5cf3163ad5ce8794839b60f27778db65782f087be3e27.js" defer></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-us">
|
||||
<html lang="de-DE">
|
||||
<head>
|
||||
<title>https://pyte.dev/posts/</title>
|
||||
<link rel="canonical" href="https://pyte.dev/posts/">
|
||||
<meta name="robots" content="noindex">
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="refresh" content="0; url=https://pyte.dev/posts/">
|
||||
</head>
|
||||
|
||||
Executable
+532
@@ -0,0 +1,532 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="de-DE"
|
||||
dir="ltr"
|
||||
class="h-full">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="language" content="de-DE">
|
||||
<script>
|
||||
|
||||
(function () {
|
||||
const storedTheme = localStorage.getItem("theme");
|
||||
const systemPrefersLight = window.matchMedia("(prefers-color-scheme: light)").matches;
|
||||
const theme = storedTheme || (systemPrefersLight ? "light" : "dark");
|
||||
document.documentElement.setAttribute("data-theme", theme);
|
||||
})();
|
||||
</script>
|
||||
<title>Spaceship Distrobox | Demians Blog</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="
|
||||
How I Solved the Distrobox Confusion in My Terminal Recently, I found myself struggling with something that seemed like a simple problem: when I was working …
|
||||
|
||||
">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="canonical" href="https://pyte.dev/posts/spaceship-distrobox/">
|
||||
|
||||
<meta name="robots" content="index, follow">
|
||||
|
||||
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:title" content="Spaceship Distrobox | Demians Blog">
|
||||
<meta property="og:description" content="How I Solved the Distrobox Confusion in My Terminal Recently, I found myself struggling with something that seemed like a simple problem: when I was working …">
|
||||
<meta property="og:url" content="https://pyte.dev/posts/spaceship-distrobox/">
|
||||
<meta property="og:site_name" content="Demians Blog"><meta property="og:image" content="https://pyte.dev/assets/patrick.png">
|
||||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="630"><meta property="article:published_time" content="2026-01-04T20:33:45+01:00">
|
||||
<meta
|
||||
property="article:modified_time"
|
||||
content="2026-01-04T20:33:45+01:00">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Spaceship Distrobox | Demians Blog">
|
||||
<meta name="twitter:description" content="How I Solved the Distrobox Confusion in My Terminal Recently, I found myself struggling with something that seemed like a simple problem: when I was working …"><meta name="twitter:image" content="https://pyte.dev/assets/patrick.png">
|
||||
|
||||
<script type="application/ld+json">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"{\"@context\":\"https://schema.org\",\"@type\":\"BlogPosting\",\"author\":{\"@type\":\"Person\",\"email\":\"demian (at) pyte (dot) dev\",\"name\":\"Demians Blog\"},\"dateModified\":\"2026-01-04T20:33:45+01:00\",\"datePublished\":\"2026-01-04T20:33:45+01:00\",\"description\":\"How I Solved the Distrobox Confusion in My Terminal Recently, I found myself struggling with something that seemed like a simple problem: when I was working …\",\"headline\":\"Spaceship Distrobox\",\"image\":\"https://pyte.dev/assets/patrick.png\",\"mainEntityOfPage\":{\"@id\":\"https://pyte.dev/posts/spaceship-distrobox/\",\"@type\":\"WebPage\"},\"publisher\":{\"@type\":\"Organization\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://pyte.dev/assets/patrick.png\"},\"name\":\"Demians Blog\"}}"</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="application/ld+json">"{\"@context\":\"https://schema.org\",\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/\",\"name\":\"Demians Blog\",\"position\":1},{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/posts/\",\"name\":\"Posts\",\"position\":2},{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/posts/spaceship-distrobox/\",\"name\":\"Spaceship Distrobox\",\"position\":3}]}"</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Outfit:wght@400;500;600;700;800;900&family=Space+Grotesk:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet">
|
||||
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/css/theme.min.86a32b729f656fc6c119ed69193950db815a3ad9fdc967b32c76d602f11449a4.css"
|
||||
integrity="sha256-hqMrcp9lb8bBGe1pGTlQ24FaOtn9yWezLHbWAvEUSaQ=">
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/css/syntax-dark.min.3e403a03e3af837b3829e9b6f01fc7792bda7cb7f5056f5e9786109545c6b2e1.css"
|
||||
integrity="sha256-PkA6A+Ovg3s4Kem28B/HeSvafLf1BW9el4YQlUXGsuE="
|
||||
id="syntax-dark-theme"
|
||||
class="syntax-theme"><link
|
||||
rel="stylesheet"
|
||||
href="/css/syntax-light.min.d0d33b879698595e6b2c0f75f0cea95a8517fb0150570cd0ee4dc42e25c8d147.css"
|
||||
integrity="sha256-0NM7h5aYWV5rLA918M6pWoUX+wFQVwzQ7k3ELiXI0Uc="
|
||||
id="syntax-light-theme"
|
||||
class="syntax-theme"
|
||||
disabled><script>
|
||||
|
||||
(function () {
|
||||
const storedTheme = localStorage.getItem("theme");
|
||||
const systemPrefersLight = window.matchMedia("(prefers-color-scheme: light)").matches;
|
||||
const theme = storedTheme || (systemPrefersLight ? "light" : "dark");
|
||||
|
||||
const syntaxDark = document.getElementById("syntax-dark-theme");
|
||||
const syntaxLight = document.getElementById("syntax-light-theme");
|
||||
|
||||
if (theme === "light") {
|
||||
if (syntaxDark) syntaxDark.disabled = true;
|
||||
if (syntaxLight) syntaxLight.disabled = false;
|
||||
} else {
|
||||
if (syntaxDark) syntaxDark.disabled = false;
|
||||
if (syntaxLight) syntaxLight.disabled = true;
|
||||
}
|
||||
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
const currentTheme = document.documentElement.getAttribute("data-theme");
|
||||
if (currentTheme === "light") {
|
||||
if (syntaxDark) syntaxDark.disabled = true;
|
||||
if (syntaxLight) syntaxLight.disabled = false;
|
||||
} else {
|
||||
if (syntaxDark) syntaxDark.disabled = false;
|
||||
if (syntaxLight) syntaxLight.disabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-theme"],
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/css/bundle.min.783a79746a859af9be598cbc33fba2a1087434b23768524277b07ef28336f113.css"
|
||||
integrity="sha256-eDp5dGqFmvm+WYy8M/uioQh0NLI3aFJCd7B+8oM28RM=">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
|
||||
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/logo-transparent/favicon-32x32.png">
|
||||
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/logo-transparent/favicon-16x16.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/logo-transparent/apple-touch-icon.png">
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="192x192"
|
||||
href="/favicon/logo-transparent/android-chrome-192x192.png">
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="512x512"
|
||||
href="/favicon/logo-transparent/android-chrome-512x512.png">
|
||||
<link rel="manifest" href="/favicon/logo-transparent/site.webmanifest">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
<body class="flex flex-col min-h-screen">
|
||||
<a href="#main-content" class="skip-to-main" aria-label="Skip to main content"
|
||||
>Skip to main content</a
|
||||
>
|
||||
<header class="sticky-header">
|
||||
<div class="header-container">
|
||||
<nav class="header-nav" role="navigation" aria-label="Main navigation">
|
||||
<div class="header-content">
|
||||
<div class="header-logo">
|
||||
<a href="/" class="logo-link" aria-label="Home - Demians Blog">
|
||||
Demians Blog
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="header-menu">
|
||||
<ul class="menu-list">
|
||||
|
||||
<li class="menu-item">
|
||||
<a
|
||||
href="/"
|
||||
class="menu-link ">
|
||||
Home
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="menu-item">
|
||||
<a
|
||||
href="/posts/"
|
||||
class="menu-link ">
|
||||
Blog
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<button id="search-toggle" class="search-toggle" aria-label="Search" type="button">
|
||||
<svg class="search-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button id="theme-toggle" class="theme-toggle" aria-label="Toggle theme" type="button">
|
||||
<svg class="icon-sun" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"></path>
|
||||
</svg>
|
||||
<svg class="icon-moon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="main-content" class="flex-1" role="main">
|
||||
|
||||
<div class="single-post-wrapper">
|
||||
<article class="single-post">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="post-header">
|
||||
<h1 class="post-title-main">Spaceship Distrobox</h1>
|
||||
|
||||
|
||||
|
||||
<div class="post-meta">
|
||||
<div class="post-meta-info">
|
||||
<time datetime="2026-01-04T20:33:45+01:00" class="post-date">
|
||||
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
January 4, 2026
|
||||
</time>
|
||||
|
||||
<span class="meta-separator">•</span>
|
||||
|
||||
|
||||
<span class="post-word-count">
|
||||
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
||||
</svg>
|
||||
291 words
|
||||
</span>
|
||||
|
||||
|
||||
|
||||
<span class="meta-separator">•</span>
|
||||
<span class="post-reading-time">
|
||||
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
2 min
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="post-content-main">
|
||||
<h1 id="how-i-solved-the-distrobox-confusion-in-my-terminal">How I Solved the Distrobox Confusion in My Terminal</h1>
|
||||
<p>Recently, I found myself struggling with something that seemed like a simple problem: when I was working with multiple terminal windows, I could never remember which one was inside a Distrobox and which one was on my local system. If you’ve ever juggled between different environments, you know exactly how annoying this can get.</p>
|
||||
<p>So, I decided to create a quick plugin for my favorite terminal prompt, <strong>Spaceship</strong>, that would clearly indicate when I’m inside a Distrobox container. The result is a simple, customizable section that appears right in your prompt, showing the name of the active container. This way, I never have to guess or dig deeper to figure out where I’m working.</p>
|
||||
<h2 id="why-this-plugin-exists">Why This Plugin Exists</h2>
|
||||
<p>While there are already tools and functions for <strong>Bash</strong> or even the <strong>Starship Prompt</strong> to detect and display if you’re inside a container, I couldn’t find anything that suited my needs for <strong>Spaceship Prompt</strong>. I wanted a minimal, easy to install plugin that would do exactly what I needed: show me when I’m in a Distrobox without cluttering up my prompt.</p>
|
||||
<p>That’s why I created this plugin. It’s simple, lightweight, and specifically designed for Spaceship users who want to easily track which environment they’re in without extra hassle.</p>
|
||||
<h3 id="key-features">Key Features:</h3>
|
||||
<ul>
|
||||
<li><strong>Instant Visual Cue</strong>: It’s super clear when I’m inside a Distrobox. No need to check <code>CONTAINER_ID</code> or other variables.</li>
|
||||
<li><strong>Customizable</strong>: I can change the symbol, the color, and the position in the prompt to match my setup.</li>
|
||||
<li><strong>Super Simple Setup</strong>: I’m already using the Spaceship prompt, so adding this feature took just a couple of lines of code.</li>
|
||||
</ul>
|
||||
<h3 id="where-to-get-it">Where to get it</h3>
|
||||
<p>For installation and configuration instructions, check out the <a href="https://github.com/pyte1/spaceship-distrobox">Spaceship Distrobox plugin Repository</a>.</p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<nav class="post-navigation" aria-label="Post navigation">
|
||||
|
||||
<a
|
||||
href="/posts/google-groups-spam/"
|
||||
class="nav-link nav-prev"
|
||||
aria-label="Previous post: Google Groups Spam">
|
||||
<span class="nav-label">Previous</span>
|
||||
<span class="nav-title">Google Groups Spam</span>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
</nav>
|
||||
</article>
|
||||
|
||||
|
||||
<aside class="post-toc" id="post-toc" aria-label="Table of contents">
|
||||
<button
|
||||
class="toc-toggle"
|
||||
id="toc-toggle"
|
||||
aria-expanded="true"
|
||||
aria-controls="toc-content"
|
||||
aria-label="Toggle table of contents">
|
||||
<svg
|
||||
class="toc-burger-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
<span class="toc-toggle-text">Table of Contents</span>
|
||||
<svg
|
||||
class="toc-chevron-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="toc-content" id="toc-content">
|
||||
<nav class="toc-nav" aria-label="Table of contents">
|
||||
<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#why-this-plugin-exists">Why This Plugin Exists</a>
|
||||
<ul>
|
||||
<li><a href="#key-features">Key Features:</a></li>
|
||||
<li><a href="#where-to-get-it">Where to get it</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<footer class="site-footer">
|
||||
<div class="footer-content">
|
||||
<p class="footer-text">
|
||||
©
|
||||
|
||||
2026
|
||||
|
||||
Demians Blog.
|
||||
|
||||
Built with Hugo and Mana ❤️
|
||||
|
||||
</p>
|
||||
|
||||
<div class="footer-social">
|
||||
<div class="social-links">
|
||||
|
||||
<a
|
||||
href="https://github.com/pyte1"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="social-link"
|
||||
aria-label="GitHub">
|
||||
<svg fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="mailto:demian%20%28at%29%20pyte%20%28dot%29%20dev" class="social-link" aria-label="Email">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</footer>
|
||||
|
||||
<div id="search-modal" class="search-modal" aria-hidden="true" role="dialog" aria-label="Search">
|
||||
<div class="search-modal-backdrop" id="search-modal-backdrop"></div>
|
||||
<div class="search-modal-container">
|
||||
<div class="search-input-wrapper">
|
||||
<svg class="search-input-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
<input
|
||||
type="search"
|
||||
id="search-input"
|
||||
class="search-input"
|
||||
placeholder="Search..."
|
||||
autocomplete="off"
|
||||
aria-label="Search input">
|
||||
<button class="search-input-clear" id="search-input-clear" aria-label="Clear search">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="search-modal-close" id="search-modal-close" aria-label="Close search">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div id="search-results" class="search-results"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<button id="scroll-to-top" class="scroll-to-top" aria-label="Scroll to top" type="button">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M5 10l7-7m0 0l7 7m-7-7v18"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="/js/main.min.e60ab79dca7b920b4dc5cf3163ad5ce8794839b60f27778db65782f087be3e27.js" defer></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user