<?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/"><channel><title>Posts on ilyess</title><link>https://ilye.ss/posts/</link><description>Recent content in Posts on ilyess</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>All work licensed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) unless otherwise stated.</copyright><lastBuildDate>Sun, 06 Sep 2026 04:00:00 -0400</lastBuildDate><atom:link href="https://ilye.ss/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>A New Home for This Blog</title><link>https://ilye.ss/posts/a-new-home-for-this-blog/</link><pubDate>Sun, 06 Sep 2026 04:00:00 -0400</pubDate><guid>https://ilye.ss/posts/a-new-home-for-this-blog/</guid><description>&lt;p&gt;When I first launched this blog, I wanted a short domain name for it. A name built around &amp;ldquo;ilyess&amp;rdquo;,&#10;which is the identity of this site. Obviously, &lt;code&gt;ilyess.com&lt;/code&gt; was already taken. So I turned to the&#10;cheapest top-level domain I could get started with. After some promo hunting, I settled on&#10;&lt;code&gt;ilyess.cc&lt;/code&gt;. It checked most of the boxes: short, centered around &amp;ldquo;ilyess&amp;rdquo;, and cheap. But this&#10;domain name is on an uncommon TLD and it&amp;rsquo;s not that &amp;ldquo;cool&amp;rdquo;. At least I didn&amp;rsquo;t think it was cool, and&#10;I still don&amp;rsquo;t.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>When I first launched this blog, I wanted a short domain name for it. A name built around &ldquo;ilyess&rdquo;,
which is the identity of this site. Obviously, <code>ilyess.com</code> was already taken. So I turned to the
cheapest top-level domain I could get started with. After some promo hunting, I settled on
<code>ilyess.cc</code>. It checked most of the boxes: short, centered around &ldquo;ilyess&rdquo;, and cheap. But this
domain name is on an uncommon TLD and it&rsquo;s not that &ldquo;cool&rdquo;. At least I didn&rsquo;t think it was cool, and
I still don&rsquo;t.</p>
<p>After some time, I noticed that I really like a specific kind of clever domain name, one where the
TLD is woven into the site&rsquo;s own name. I remember exactly where the trick first clicked: Aral
Balkan&rsquo;s <code>ar.al</code>. From there, I started seeing them everywhere: <code>personalsit.es</code> for &ldquo;personal
sites&rdquo;, <code>jessi.ca</code> for &ldquo;jessica&rdquo;, <code>selfh.st</code> for &ldquo;selfhost&rdquo; (whose owner took it up a notch and used
the dot, too, as a substitute for &ldquo;o&rdquo;)&hellip; you get the idea. Naturally, I started obsessing over the
<code>ilye.ss</code> domain name. <code>ilye</code> plus <code>.ss</code> <em>is</em> &ldquo;ilyess&rdquo;. The TLD didn&rsquo;t hang off the word; it was
the last two letters of it. That was the whole magic trick.</p>
<p>When I checked the first time, the <code>.ss</code> TLD wasn&rsquo;t supported by popular registrars. I managed to
find one that had it, but the asking price was exorbitant. I wasn&rsquo;t ready to pay an arm and a leg
for what could objectively be described as an aesthetic upgrade, so I gave up the hunt and accepted
the less-than-ideal domain I had.</p>
<p>Recently, my obsession resurfaced. Open source and selfhosting are a steady part of my reading, and
<code>selfh.st</code> is always in those rounds — a constant reminder of the trick. Check after check, the idea
kept coming back. Before I knew it, I was looking at domain names again. I discovered <code>tld-list.com</code>
where I was pleasantly surprised to see a registrar I&rsquo;d never heard of listing my domain of choice
at a fair price. It was still expensive by <code>.cc</code> standards, especially compared to what I
first paid, but reasonable enough for me to pull the trigger.</p>
<p>This blog now sports the shiny new domain <code>ilye.ss</code>, and all <code>ilyess.cc</code> links redirect to this new
one. The mission is complete. I will stop looking at domain names — at least for this blog.</p>
<p>I spent years coveting a six-letter domain, for a blog about not very much. We all have a vanity
project we didn&rsquo;t really need. What&rsquo;s yours, the <code>ilye.ss</code> of your projects? Drop it
in the comments, reply on <a href="https://mastodon.online/@ilyess/117226792519546536"  target="_blank" rel="noreferrer">Mastodon</a>, or reach out via <a 
  class="email-link"
  href="#"
  data-email="aGlAaWx5ZS5zcw=="
  data-subject="RE: A New Home for This Blog">email</a>. Thank you so much for tuning in. Until
next time, take care!</p>
<a href="https://ilye.ss/posts/a-new-home-for-this-blog/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>Services Worth Self-Hosting for Privacy</title><link>https://ilye.ss/posts/services-worth-self-hosting-for-privacy/</link><pubDate>Sun, 08 Mar 2026 10:00:00 -0400</pubDate><guid>https://ilye.ss/posts/services-worth-self-hosting-for-privacy/</guid><description>&lt;p&gt;A few years ago, I decided I wanted more control over my data. I was tired of wondering what Google&#10;knew about me, tired of cloud services getting breached, and tired of relying on companies that&#10;could shut down tomorrow and take my data with them. So I started self-hosting — not to be optimal,&#10;but to be in control.&lt;/p&gt;&#10;&lt;p&gt;If you&amp;rsquo;re here, you might be thinking about doing the same. Maybe you&amp;rsquo;ve heard about self-hosting&#10;but feel overwhelmed by where to start. Maybe you think it requires a server closet full of&#10;equipment and a networking degree. It doesn&amp;rsquo;t.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>A few years ago, I decided I wanted more control over my data. I was tired of wondering what Google
knew about me, tired of cloud services getting breached, and tired of relying on companies that
could shut down tomorrow and take my data with them. So I started self-hosting — not to be optimal,
but to be in control.</p>
<p>If you&rsquo;re here, you might be thinking about doing the same. Maybe you&rsquo;ve heard about self-hosting
but feel overwhelmed by where to start. Maybe you think it requires a server closet full of
equipment and a networking degree. It doesn&rsquo;t.</p>
<p>This post covers the services I recommend starting with if you care about privacy and want to own
your data. None of these are required. Pick what sounds useful and ignore the rest.</p>

<h3 class="relative group">Pi-hole
    <div id="pi-hole" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#pi-hole" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If you only self-host one thing, make it <a href="https://pi-hole.net/"  target="_blank" rel="noreferrer">Pi-hole</a>. It runs a DNS server on your network that blocks
ads and trackers at the source. Every device in your home connects through it, so you get
ad-blocking everywhere on your phone, your laptop, and your smart TV without installing anything on
each device individually. I&rsquo;ve <a href="https://ilye.ss/posts/pihole-dhcp-docker-bridge-network/" >written about my Pi-hole setup</a> before, so I won&rsquo;t rehash the details here. But the tl;dr is: it
works, it&rsquo;s easy to run on a Raspberry Pi, and the difference in browsing experience is noticeable.
Once you have Pi-hole running, you can also use it as your DHCP server, meaning it can hand out IP
addresses to your devices, replacing your router&rsquo;s DHCP function.</p>

<h3 class="relative group">Bitwarden
    <div id="bitwarden" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#bitwarden" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>We all know we should use a password manager. But most people default to the cloud-hosted version,
which means their passwords live on someone else&rsquo;s server. <a href="https://bitwarden.com/"  target="_blank" rel="noreferrer">Bitwarden</a> is an open-source password
manager with a Docker image you can run at home. <a href="https://ilye.ss/posts/self-host-bitwarden-using-docker/" >I host my own instance</a>,
and it&rsquo;s been rock-solid. The browser extensions work well, mobile apps are decent, and you get all
the features you&rsquo;d want: 2FA integration, secure notes, password sharing with family. The
self-hosted version is free, which is refreshing in a world where everything wants a subscription.</p>
<p>The trade-off with self-hosting is backups. If your server dies and you haven&rsquo;t backed up your
vault, you&rsquo;re locked out of everything. I solve this by encrypting my vault locally and syncing it
to a few public cloud providers, on a regular basis. The data is encrypted before it leaves my
machine, so the providers never see my actual passwords — but I get the peace of mind that comes
with high availability and knowing there&rsquo;s always a safe copy somewhere.</p>

<h3 class="relative group">Obsidian with Git
    <div id="obsidian-with-git" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#obsidian-with-git" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>My note-taking setup <a href="https://ilye.ss/posts/my-new-note-taking-setup/" >has evolved a lot over the years</a>. I&rsquo;ve
tried Evernote, Standard Notes, Joplin, and finally landed on Obsidian with Git sync. <a href="https://obsidian.md/"  target="_blank" rel="noreferrer">Obsidian</a> is a
markdown-based note-taking app that stores your notes locally as plain text files. It runs locally
on your machine, so it&rsquo;s not a self-hosted service itself. The magic is in how you sync it. I use
Git for that. Specifically, I have a private <a href="https://about.gitea.com/"  target="_blank" rel="noreferrer">Gitea</a> instance running on a home server, and my
Obsidian vault lives there. Every change gets pushed to Gitea, giving me version history, automatic
backups, and complete ownership of my notes. Since everything is just markdown, my notes aren&rsquo;t
trapped in a proprietary format. I own them completely. No subscription, no cloud dependency, no
vendor lock-in. If you&rsquo;re already running Gitea (or any Git server), adding your Obsidian vault to
it takes minutes. Even if you&rsquo;re not, it&rsquo;s a great reason to set one up.</p>

<h3 class="relative group">Jellyfin
    <div id="jellyfin" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#jellyfin" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>For media, <a href="https://jellyfin.org/"  target="_blank" rel="noreferrer">Jellyfin</a> is a free, open-source media server. Think of it as your own personal Netflix.
Rip your DVDs, dump them on a server, and stream to any device in your house. It&rsquo;s not as polished
as Plex — which has moved toward paid features — but it works well and respects your privacy. No
accounts, no data collection, no subscription. I run Jellyfin on a modest server and stream to my
TV. It&rsquo;s perfect for having a centralized media library without relying on streaming services.</p>

<h3 class="relative group">AudiobookShelf
    <div id="audiobookshelf" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#audiobookshelf" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>If you listen to audiobooks, <a href="https://www.audiobookshelf.org/"  target="_blank" rel="noreferrer">AudiobookShelf</a> is a self-hosted audiobooks server, similar to Jellyfin
but for books. You upload your audiobooks, organize them by series or author, and stream to your
phone or other devices.</p>
<p>You don&rsquo;t need much to get started. A Raspberry Pi is perfect for beginners. Even a Pi 4 with 4GB
RAM can handle Pi-hole, Bitwarden, and a few other services without breaking a sweat. An old laptop
or desktop works too if you have one lying around. For networking, a static IP helps, but it&rsquo;s not
required. Dynamic DNS services like DuckDNS work fine if your ISP doesn&rsquo;t give you a static address.</p>
<p>How long it takes depends on your technical background. If you&rsquo;re comfortable with the command line
and have fiddled with Docker before, you might get up and running in an afternoon. If everything is
new to you, expect to spend some time reading and troubleshooting. Either way, the first service is
always the hardest. After that, adding more becomes much easier.</p>

<h2 class="relative group">Wrap up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Self-hosting is a journey, not a destination. You don&rsquo;t need to set up everything at once. Pick one
service that solves a problem you actually have. Maybe it&rsquo;s ad-blocking with Pi-hole. Maybe it&rsquo;s
finally switching to a password manager. Maybe it&rsquo;s getting your notes out of a proprietary service.</p>
<p>The important part is starting — you&rsquo;ll learn as you go, and that&rsquo;s part of the fun.</p>
<p>How about you? What self-hosted services do you run, or are you planning to set up? I&rsquo;d love to hear
what&rsquo;s in your stack. Feel free to leave a comment with your setup, or any recommendations you think
might improve mine. You can also share your thoughts on <a href="https://mastodon.online/@ilyess/116194246034905775"  target="_blank" rel="noreferrer">Mastodon</a> or reply via email. Thank you for
tuning in. Until next time, take care!</p>
<a href="https://ilye.ss/posts/services-worth-self-hosting-for-privacy/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>Using Pi-hole Dns in Neighboring Docker Containers</title><link>https://ilye.ss/posts/using-pihole-dns-in-neighboring-docker-containers/</link><pubDate>Mon, 13 Oct 2025 04:00:00 -0400</pubDate><guid>https://ilye.ss/posts/using-pihole-dns-in-neighboring-docker-containers/</guid><description>&lt;p&gt;In a &lt;a href="https://ilye.ss/posts/pihole-dhcp-docker-bridge-network/" &gt;previous post&lt;/a&gt;, I explained how to run Pi-hole&#10;in Docker and use it to manage the Domain Name System (DNS) for an entire home network — for example&#10;at home, work, or school. It&amp;rsquo;s a great tool for taking control over your DNS and blocking trackers&#10;and ads across the whole network. Once the network&amp;rsquo;s DHCP&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; server is configured, every machine on&#10;the network will begin sending its DNS queries to the Pi-hole container. But what about Docker&#10;containers that share the same Pi-hole host — what I&amp;rsquo;m calling &amp;ldquo;neighboring Docker containers&amp;rdquo;? Can&#10;they also use the Pi-hole container on that host for DNS?&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>In a <a href="https://ilye.ss/posts/pihole-dhcp-docker-bridge-network/" >previous post</a>, I explained how to run Pi-hole
in Docker and use it to manage the Domain Name System (DNS) for an entire home network — for example
at home, work, or school. It&rsquo;s a great tool for taking control over your DNS and blocking trackers
and ads across the whole network. Once the network&rsquo;s DHCP<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> server is configured, every machine on
the network will begin sending its DNS queries to the Pi-hole container. But what about Docker
containers that share the same Pi-hole host — what I&rsquo;m calling &ldquo;neighboring Docker containers&rdquo;? Can
they also use the Pi-hole container on that host for DNS?</p>

<h2 class="relative group">Pi-hole DNS in the Host Machine
    <div id="pi-hole-dns-in-the-host-machine" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#pi-hole-dns-in-the-host-machine" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s start with the host machine itself. For it to use the Pi-hole container as a DNS resolver, the
loopback<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> IP address must be set as the DNS nameserver in the host&rsquo;s configuration. This is
usually done by adding the following line to <code>/etc/resolv.conf</code>:</p>
<div class="highlight-wrapper"><pre tabindex="0"><code class="language-conf" data-lang="conf">nameserver 127.0.0.1</code></pre></div>
<p>The Pi-hole Docker container publishes UDP port 53 on the host machine to handle DNS for the entire
network. Using the loopback address here tells the host to resolve DNS to &ldquo;itself&rdquo;, which routes
queries through that published port.</p>
<div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="warning">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon">
</span></div>
        <div class="grow">
          Warning
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>There are situations where this won&rsquo;t work — for example, if the Pi-hole container must go down
briefly for maintenance such as updating its Docker image. In those cases, a simple workaround is
to point the host temporarily to a public DNS resolver until the maintenance is finished.</p></div></div>
<h2 class="relative group">Pi-hole DNS in Neighboring Docker Containers
    <div id="pi-hole-dns-in-neighboring-docker-containers" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#pi-hole-dns-in-neighboring-docker-containers" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>By default, Docker uses the DNS servers configured on the host inside containers unless those
servers are invalid, in which case it falls back to Google&rsquo;s <code>8.8.8.8</code> resolver or to whatever
resolver is set in Docker&rsquo;s daemon configuration (typically in <code>/etc/docker/daemon.json</code>). If you
use the loopback IP address on the host, as shown in the previous section, Docker treats it as
invalid because it would point to the container&rsquo;s own loopback address, which is not the intended
effect. In other words, it would be telling every Docker container to use <strong>itself</strong> as the DNS
resolver — a setup that simply doesn&rsquo;t work. So, how can we use the Pi-hole container to serve DNS
for both the host machine <em>and</em> any neighboring Docker containers?</p>
<p>An option is to use the host&rsquo;s LAN<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> IP address (e.g., <code>10.10.0.10</code>) instead of the loopback
address. In my experience, this approach also failed. When I tested it, I could see DNS queries
arriving at the Pi-hole container from neighboring containers, but those requests never completed the
round-trip — they timed out from the container&rsquo;s perspective.</p>
<div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="note">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon">
</span></div>
        <div class="grow">
          Note
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>If you know how to resolve this timeout when using the LAN IP address as a DNS resolver in
neighboring containers, please let me know.</p></div></div><p>Another alternative is to use Docker networks so that all containers share the network used by
Pi-hole. This is the approach I&rsquo;ll detail below.</p>
<p>Running Pi-hole in Docker, as described in <a href="https://ilye.ss/posts/pihole-dhcp-docker-bridge-network/" >my previous post</a>, automatically creates a Docker network called
<code>pihole_backend</code> (assuming the folder that holds the <code>docker-compose.yml</code> file is named <code>pihole</code>).
The relevant section looks like this:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-docker" data-lang="docker"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1</span><span>networks:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2</span><span>  backend:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3</span><span>    ipam:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4</span><span>      config:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5</span><span>        - subnet: 172.31.0.0/16</span></span></code></pre></div></div>
<p>We want to attach any container that needs to use Pi-hole to this Docker network. To do that, update
the service definition in its <code>docker-compose.yml</code> file as follows:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-docker" data-lang="docker"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span>service:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span>  myservice:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span>    ...<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span>    networks:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span>      - pihole_backend<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span>    dns:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span>      - <span style="color:#e6db74">&#34;172.31.0.111&#34;</span> <span style="color:#75715e"># Pi-hole container&#39;s IP address on the pihole_backend Docker network</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span>networks:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span>  pihole_backend:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span>    external: true <span style="color:#75715e"># Important since this network is created in another docker-compose file (Pi-hole&#39;s)</span></span></span></code></pre></div></div>
<p>We&rsquo;re making three main changes:</p>
<ol>
<li>Declare Pi-hole&rsquo;s Docker network as external so that services defined in the compose file can use
it.</li>
<li>Attach the service to that network — this lets container communicate with Pi-hole.</li>
<li>Configure the service to use Pi-hole&rsquo;s IP address on that network as its DNS resolver.</li>
</ol>
<p>After saving these changes, the <code>myservice</code> container will start using Pi-hole for DNS the next time
it restarts.</p>

<h3 class="relative group">Caveats
    <div id="caveats" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#caveats" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Unfortunately, these changes have to be added to every container, which makes this approach
error-prone. It&rsquo;s up to you to remember to insert those lines in the <code>docker-compose.yml</code> file before
deploying a new service; otherwise the container will fall back to Docker&rsquo;s default public DNS
resolver.</p>
<p>Another drawback is that the Pi-hole stack must be deployed first so the <code>pihole_backend</code> network
exists for the other containers to join. That requirement makes sense — containers can&rsquo;t use Pi-hole
for DNS until it&rsquo;s running — but the dependency isn&rsquo;t expressed in a way that drives the deployment
sequence. Unless all services are defined in the same compose file and are explicitly configured to
depend on the Pi-hole service, you have to manually verify that the Pi-hole stack is online before
launching any dependent stacks.</p>
<p>If you know of a better approach that avoids these caveats, please let me know!</p>

<h2 class="relative group">Wrap Up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I explained how to use a Pi-hole Docker container as a DNS resolver on the host machine and discussed
some challenges of using it from neighboring Docker containers. I then presented a workaround and
highlighted its remaining shortcomings. I hope you found this article informative, or at least
enjoyable. I&rsquo;m always eager to hear from you, so feel free to drop a comment below, fire off a
<a href="https://mastodon.online/@ilyess/115368307830274533"  target="_blank" rel="noreferrer">Mastodon</a> reply, or send me <a 
  class="email-link"
  href="#"
  data-email="aGlAaWx5ZS5zcw=="
  data-subject="RE: Using Pi-hole Dns in Neighboring Docker Containers">an email</a>. Thanks again for tuning in. Until next time, take
care!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>The <a href="https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol"  target="_blank" rel="noreferrer">Dynamic Host Configuration Protocol</a> is a protocol used to automatically assign IP
addresses and other communication parameters to devices connected to the network.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>The <a href="https://en.wikipedia.org/wiki/Loopback"  target="_blank" rel="noreferrer">loopback</a> IP address is a non-routable address that points to the machine itself.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>A <a href="https://en.wikipedia.org/wiki/Local_area_network"  target="_blank" rel="noreferrer">Local Area Network</a> (LAN) is a computer network that interconnects computers within a limited
area such as a residence.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
<a href="https://ilye.ss/posts/using-pihole-dns-in-neighboring-docker-containers/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>Why I'm Adding Native Comments to My Blog</title><link>https://ilye.ss/posts/why-im-adding-native-comments-to-my-blog/</link><pubDate>Thu, 25 Sep 2025 04:00:00 -0400</pubDate><guid>https://ilye.ss/posts/why-im-adding-native-comments-to-my-blog/</guid><description>&lt;p&gt;One of the main reasons I love blogging is the lively discussions my posts generate. Exchanging&#10;ideas with like-minded people brings me joy, helps me broaden my knowledge, and lets me forge&#10;long-term connections. Those conversations can&amp;rsquo;t thrive without a welcoming space. Broadly speaking,&#10;I categorize this feedback into two groups: private channels such as email and direct messages, and&#10;public venues like social-media posts or forums.&lt;/p&gt;&#10;&lt;p&gt;In this article I&amp;rsquo;ll briefly outline the tools I&amp;rsquo;ve been using to manage public discussions on this&#10;blog, explore other comment-management solutions, and describe the changes I&amp;rsquo;m implementing in my&#10;current setup moving forward.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>One of the main reasons I love blogging is the lively discussions my posts generate. Exchanging
ideas with like-minded people brings me joy, helps me broaden my knowledge, and lets me forge
long-term connections. Those conversations can&rsquo;t thrive without a welcoming space. Broadly speaking,
I categorize this feedback into two groups: private channels such as email and direct messages, and
public venues like social-media posts or forums.</p>
<p>In this article I&rsquo;ll briefly outline the tools I&rsquo;ve been using to manage public discussions on this
blog, explore other comment-management solutions, and describe the changes I&rsquo;m implementing in my
current setup moving forward.</p>

<h2 class="relative group">How I Manage Comments Today
    <div id="how-i-manage-comments-today" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#how-i-manage-comments-today" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Whenever I publish a new post, I also share it on Mastodon hoping to spark a constructive
conversation. In this way, Mastodon serves as a rudimentary comment-management system: It lets me
announce a new article and exchange thoughts with people across the Fediverse, all from the comfort
of my own Mastodon account. Many fellow bloggers have adopted the same strategy, and that&rsquo;s
perfectly reasonable. The approach is extremely low-effort, requires virtually no maintenance, is
completely free, and leverages the Fediverse that those bloggers already belong to.</p>
<p>A major downside of using Mastodon — or any other social-media platform — in this way is that it pulls
the conversation away from the blog post being discussed. That disconnect can discourage readers
from sharing their thoughts, or cause them to miss the conversation entirely if they skip the
paragraph where I usually drop the Mastodon link. As a workaround, some developers have created
scripts that automatically fetch Mastodon replies and display them below the article. In my view,
while this improves the reader&rsquo;s experience somewhat, it&rsquo;s still suboptimal because participants
have to leave the blog and hop onto the Fediverse to join the discussion.</p>
<p>Another limitation of using Mastodon as a comment-management tool is that it excludes anyone without
a Fediverse account. This shortcoming isn&rsquo;t unique to Mastodon — most social-media platforms and any
comment system that requires an account share the same issue — but it remains a drawback in my view.</p>

<h2 class="relative group">Time for a Change
    <div id="time-for-a-change" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#time-for-a-change" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It&rsquo;s time to try something new. I&rsquo;ve been considering adding a more comprehensive comment-management
solution to this blog for a while, but I never fully committed until recently. Adding comments to a
static site is a contentious proposition — it forces you to pause and weigh the trade-offs before
making a decision.</p>
<p>On one hand, I completely understand the purist view that comments are inherently dynamic while
static sites are meant to stay static. Moreover, maintaining native user-registration and login just
to enable comments often isn&rsquo;t worth the effort for most personal blogs. And third-party comment
providers usually fall short on privacy, and they rarely blend seamlessly with a site&rsquo;s design.</p>
<p>On the flip side, comments can prompt dialogue and give the author a place to solicit reader input.
They&rsquo;re also more accessible because readers don&rsquo;t need to be on the same social-media platform as
the author to leave a remark.</p>
<div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="note">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon">
</span></div>
        <div class="grow">
          Note
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>Email is another way people can reach out, but it remains a one-on-one channel. I see it as a
private message to the author, unlike a comment that can be viewed, challenged, supported, or
corrected publicly.</p></div></div><p>I wanted to gauge what the Fediverse community thought about this, so I posted a <a href="https://mastodon.online/@ilyess/115012199194772730"  target="_blank" rel="noreferrer">question</a> about
comment management on blogs and received a range of responses. The question was:</p>
<blockquote><p>If you have a blog, what do you use for comment management?</p>
<ol>
<li>Third party comment provider like Disqus or Github</li>
<li>Native comment management</li>
<li>Webmentions</li>
<li>Other</li>
</ol>
</blockquote><p>To my surprise, most of the votes favored native comment management, with &ldquo;Other&rdquo; in second place
and Webmentions<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> third. Unsurprisingly, &ldquo;third-party comment providers&rdquo; ranked last.</p>
<p>Alongside the poll results, I received excellent suggestions and caught a glimpse of the tools
others use to solve the same problem. This reinforced the notion that some form of
commenting — whether minimal or extensive — is welcomed, if not encouraged, and that leveraging
Mastodon or the Fediverse in general remains a popular strategy among bloggers. A few brave
contributors even explained how they implement native comment systems and kindly shared valuable
resources for further learning. So I dove down the rabbit hole, searching for the perfect native
comment solution for my statically generated blog.</p>
<div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="note">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon">
</span></div>
        <div class="grow">
          Note
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>Webmentions are great, but they raise the participation bar even higher than Mastodon or the
broader Fediverse. To join the conversation, you&rsquo;d need to run your own blog!</p></div></div>
<h2 class="relative group">New Kid on the Blog
    <div id="new-kid-on-the-blog" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#new-kid-on-the-blog" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>In my quest for the perfect native comment-management system, I kept a short list of requirements:</p>
<ol>
<li>Open-source and self-hostable: I don&rsquo;t want to rely on a third party for comments.</li>
<li>Anonymous submissions: Lowering the barrier encourages participation.</li>
<li>Easy moderation: Preferably with an option to require admin approval for every submission.</li>
</ol>
<p>A few projects caught my eye during the search. The first was <a href="https://isso-comments.de/"  target="_blank" rel="noreferrer">Isso</a>, an open-source alternative
to Disqus with Markdown support. The second was <a href="https://docs.comentario.app/en/"  target="_blank" rel="noreferrer">Comentario</a>, which offers a very similar feature
set but with a slightly more refined UI and documentation. I chose the latter. Its sleek admin
interface and extensive comment-section customizability won me over instantly. Because I don&rsquo;t want
to manage user accounts or logins, I enabled the always-anonymous mode. Visitors can still supply a
name for display purposes — a name that doesn&rsquo;t have to be their real one.</p>
<div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="note">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon">
</span></div>
        <div class="grow">
          Note
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>Frankly, I could have gone with Isso just as easily. I happened to try Comentario first, so I
stuck with it. Let me know whether you&rsquo;ve tried both and which one you prefer.</p></div></div><p>After a while, I set up my Comentario instance and tweaked the UI to match my site&rsquo;s overall theme.
It&rsquo;s now ready to accept comment submissions. This post will be the first to enable native comments,
with plans to roll them out to the rest of the articles if they prove stable enough. I&rsquo;d really
appreciate it if you&rsquo;d drop a comment below to help me test the system — do it for science!</p>
<p>All other existing feedback and comment channels will stay untouched, so I&rsquo;ll keep posting my blog
entries on Mastodon as well. The goal of adding native comments is to expand the current comment
hub, not to replace it entirely. You can still email me or continue engaging on Mastodon if you
prefer.</p>

<h2 class="relative group">Wrap Up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I explained why I&rsquo;m shifting away from Fediverse-only chatter, outlined the basics I needed in a
comment system, and shared how I landed on Comentario. The new comment section is now live, but the
old channels (email and Mastodon) remain open.</p>
<p>Give the new comment box a spin, fire off a reply on <a href="https://mastodon.online/@ilyess/115265749904203890"  target="_blank" rel="noreferrer">Mastodon</a>, or shoot me <a 
  class="email-link"
  href="#"
  data-email="aGlAaWx5ZS5zcw=="
  data-subject="RE: Why I&#39;m Adding Native Comments to My Blog">an email</a>. Your
feedback keeps the conversation flowing, and I&rsquo;m excited to hear what you think. Until next time,
take care!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://en.wikipedia.org/wiki/Webmention"  target="_blank" rel="noreferrer">Webmention</a> is a simple protocol to notify any URL when a website links to it, and for web
pages to request notifications when somebody links to them.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
<a href="https://ilye.ss/posts/why-im-adding-native-comments-to-my-blog/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>How I Fixed My Linux Installation</title><link>https://ilye.ss/posts/how-i-fixed-my-linux-installation/</link><pubDate>Tue, 26 Aug 2025 04:00:00 -0400</pubDate><guid>https://ilye.ss/posts/how-i-fixed-my-linux-installation/</guid><description>&lt;p&gt;A few weeks ago, I &lt;a href="https://ilye.ss/posts/how-i-broke-my-linux-installation/" &gt;broke my Linux installation&lt;/a&gt;.&#10;After a lengthy investigation that involved messing with BIOS settings and flashing a new firmware&#10;version, I was now ready to load a live Arch ISO and start digging around in hopes of finding the&#10;root cause and deploying a fix.&lt;/p&gt;&#10;&lt;div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="note"&gt;&#10; &lt;div class="flex items-center gap-2 font-semibold text-inherit"&gt;&#10; &lt;div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"&gt;&lt;span class="relative block icon"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"&gt;&lt;path fill="currentColor" d="M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z"/&gt;&lt;/svg&gt;&#10;&lt;/span&gt;&lt;/div&gt;&#10; &lt;div class="grow"&gt;&#10; Note&#10; &lt;/div&gt;&#10; &lt;/div&gt;&lt;div class="admonition-content mt-3 text-base leading-relaxed text-inherit"&gt;&lt;p&gt;If you haven&amp;rsquo;t read &lt;a href="https://ilye.ss/posts/how-i-broke-my-linux-installation/" &gt;the post referenced above&lt;/a&gt;, I&#10;suggest giving it a read before this one.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>A few weeks ago, I <a href="https://ilye.ss/posts/how-i-broke-my-linux-installation/" >broke my Linux installation</a>.
After a lengthy investigation that involved messing with BIOS settings and flashing a new firmware
version, I was now ready to load a live Arch ISO and start digging around in hopes of finding the
root cause and deploying a fix.</p>
<div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="note">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon">
</span></div>
        <div class="grow">
          Note
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>If you haven&rsquo;t read <a href="https://ilye.ss/posts/how-i-broke-my-linux-installation/" >the post referenced above</a>, I
suggest giving it a read before this one.</p></div></div>
<h2 class="relative group">The Unexpected EFI Snag
    <div id="the-unexpected-efi-snag" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-unexpected-efi-snag" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I booted into the live Arch Linux ISO, and chrooted<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> into my Arch installation. As I inspected
drive partitions to mount them correctly, I noticed that the EFI partition was mounted on <code>/efi</code>.
That instantly caught my attention — I remembered from reading the Systemd-boot Arch wiki article
that <code>/efi</code> isn&rsquo;t mentioned as a supported EFI path. By default, <code>bootctl install</code> looks for <code>/boot</code>
and <code>/boot/efi</code>. Apparently, Systemd-boot doesn&rsquo;t support <code>/efi</code> as a mount point for the EFI
partition just yet, even though the Arch wiki discourages mounting the EFI partition under
<code>/boot/efi</code>. Hopefully this will be addressed in a future version. Separating the boot and EFI
partitions comes with plenty of benefits. The main ones are:</p>
<ul>
<li>Separation of concerns between the OS and UEFI files;</li>
<li>Ability to encrypt kernel images in <code>/boot</code>; and</li>
<li>On-demand EFI partition mounting during boot loader upgrades.</li>
</ul>
<div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="note">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon">
</span></div>
        <div class="grow">
          Editor&rsquo;s note
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>I just checked the Arch wiki article again, as a sanity check before publishing this post, and now
it says that Systemd-boot will try to locate the EFI system partition at <code>/efi</code><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. This change
must have been pushed in a recent update.</p></div></div><p>So, I changed the mount location to <code>/boot/efi</code> to see if it made any difference.</p>

<h2 class="relative group">Light at the End of the Tunnel
    <div id="light-at-the-end-of-the-tunnel" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#light-at-the-end-of-the-tunnel" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Now that the EFI partition was mounted at <code>/boot/efi</code>, I ran the <code>bootctl install</code> command again and
lo and behold, no EFI var input/output error! It seemed to work? I followed the Arch wiki to create
the loader configuration file alongside an entry file for my Arch installation. A quick reboot
brought up the Systemd-boot menu as expected, with my new Arch entry selected as default. Wonderful!</p>
<p>I&rsquo;m glad I took the time to carefully go over the Arch wiki&rsquo;s Systemd-boot article. Otherwise, I&rsquo;m
not sure I would&rsquo;ve guessed that the mount point of the EFI partition had anything to do with the
EFI vars error. Also, props to the Arch wiki maintainers — you all are heroes!</p>
<p>There was one small problem. I couldn&rsquo;t boot into my Arch Linux system through the Systemd-boot
menu. Specifically, the boot entry generated by the <code>bootctl</code> command wasn&rsquo;t able to properly load
my Arch installation. My system sits on a LUKS-encrypted<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> partition, so I can&rsquo;t use the default
boot entry configuration. Back to the hunt for the right option parameters that would enable
Systemd-boot to correctly boot my system.</p>
<p>The exact configuration wasn&rsquo;t straightforward, to be frank. I had to dig around in old forum posts,
go through the man page of <code>bootctl</code> — several times — and try out a bunch of option variations<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>.
Eventually, I managed to concoct just the right boot options for Systemd-boot to decrypt the
partition and load the system.</p>
<p>There was still one small problem<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>. My system still wasn&rsquo;t booting, and this time the problem
wasn&rsquo;t with Systemd-boot directly. Kernel and initramfs<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> image files live under <code>/boot</code> by
default, but that&rsquo;s not where Systemd-boot was looking. It checked <code>/boot/efi</code> since that&rsquo;s where my
EFI partition was mounted. So, I had to manually copy kernel and initramfs image files to
<code>/boot/efi</code> because I wasn&rsquo;t able to directly reference these files from <code>/boot</code>. What I mean is
that in the loader entry file, these image files are referenced as follows:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#ae81ff">linux	/vmlinuz-linux</span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">initrd	/initramfs-linux.img</span></span></span></code></pre></div></div>
<p>The <code>/</code> here references the EFI partition, <code>/boot/efi</code>. Ideally, I should be able to go up to the
parent folder and grab the image files like so:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#ae81ff">linux	../vmlinuz-linux</span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">initrd	../initramfs-linux.img</span></span></span></code></pre></div></div>
<p>Or maybe like:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#ae81ff">linux	/../vmlinuz-linux</span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">initrd	/../initramfs-linux.img</span></span></span></code></pre></div></div>
<p>Unfortunately, none of those attempts bore any fruit. I had no choice but to copy those files
manually from <code>/boot</code> to <code>/boot/efi</code> and see if it fixed the issue. I rebooted the machine,
speed-typed the decryption passphrase, hit Enter and stared at the screen in anticipation. As the
milliseconds rolled by, a wall of log messages washed over the black screen. With every new line my
hopes built up — until I was greeted with &ldquo;Welcome to Arch Linux!&rdquo;. I finally made it, people!</p>

<h2 class="relative group">Clean Up
    <div id="clean-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#clean-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The system was properly booting the current kernel, but it wouldn&rsquo;t boot the next kernel version
unless I manually copied over its images to <code>/boot/efi</code>. Frankly, I didn&rsquo;t feel like doing this by
hand after every kernel update — if I even remembered to do it at all. So, automating this process
was a mandatory next step.</p>
<p>There are different ways of achieving this. I opted for updating the mkinitcpio<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup> Linux preset to
target the correct location when building kernel and initramfs image files. It&rsquo;s a fairly simple
configuration update. Once ready, I tested it by rebuilding the current kernel images and ensured
they were indeed placed in the expected location.</p>
<p>Now that my boot sequence was properly configured with Systemd-boot for current and future kernel
versions, it was time for what I&rsquo;d been longing for all along:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>sudo pacman -Rns grub</span></span></code></pre></div></div>
<p>I got rid of GRUB and got my system that much closer to its ultimate lean state. Man, did it feel
good to kick those few dozen megabytes out of my system. I never thought one could draw so much
pleasure from running a single Linux command.</p>
<div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="note">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon">
</span></div>
        <div class="grow">
          Note
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>To be clear, I don&rsquo;t hate GRUB. As a matter of fact, I&rsquo;ve always used it with great success. It&rsquo;s
relatively simple to configure and it does the job. I&rsquo;m celebrating the fact that I can use
Systemd-boot, which comes preinstalled, more so than the fact that I&rsquo;m not using GRUB, if that
makes sense.</p></div></div>
<h2 class="relative group">Retrospective
    <div id="retrospective" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#retrospective" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You might be thinking &ldquo;That&rsquo;s an awful amount of time, effort, and energy put into deleting a single
package&rdquo;. And I&rsquo;d be the first to agree. Did I <em>have</em> to go through all this torture? Not really;
GRUB worked just fine and I never had to touch it since the first time I set it up. Would I do this
again now that I know how tumultuous the path to perfection can be? Hell yeah! Yes, it was
challenging — but that&rsquo;s where the fun lies. I learned so much from this little mission. By the time
I reached the final stages, I was decrypting my root partition from the live ISO TTY<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup> with the
confidence of a seasoned expert and chrooting in to mess around with Systemd-boot configuration
files that I knew exactly where to find. Hacker status: Unlocked! Yeah, that felt pretty good.</p>
<p>Yet, even as I gained proficiency in Systemd-boot and manipulating system configuration from a live
ISO, a few lingering questions remained unanswered. The first is the EFI vars error. The problem
from the <a href="https://ilye.ss/posts/dual-boot-struggles/" >dual boot article</a>, which instigated this whole endeavor
is unfortunately still here. Trying to update EFI vars still throws that input/output error, even
with Systemd-boot. For instance, any attempt to modify the boot order invariably results in this
error. As a consequence, my machine still launches the Windows bootloader, where I have to hit
Escape to switch to the Systemd-boot loader screen. I still don&rsquo;t know what the root cause could be,
and quite honestly, it bugs me. The one consolation in all this is the confirmation that the
bootloader is not to blame. At least, neither GRUB nor Systemd-boot is the culprit.</p>
<p>The second question concerns the EFI System Partition (ESP). Based on my understanding, a single ESP
is technically sufficient for UEFI to function, as it serves as the central location for boot files.
Consequently, in multi-boot configurations, it&rsquo;s possible to consolidate all operating systems
under a single ESP, using one bootloader to manage them collectively. Is this accurate and feasible?
I currently have multiple EFI partitions — one for each OS on my computer.</p>

<h2 class="relative group">Wrap Up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Recovering from a broken Linux installation proved to be a journey filled with unexpected challenges
and valuable lessons — from debugging EFI partition mounting issues and Systemd-boot configurations
to customizing the kernel update process and finally bidding farewell to GRUB. Along the way, I
learned a lot about the Linux boot process, the benefits of separating boot and EFI partitions, and
Systemd-boot in general. While the transition to Systemd-boot was ultimately successful, some
questions are still unresolved, as detailed in the previous section. If you have insights or
solutions to these puzzles, feel free to reach out on <a href="https://mastodon.online/@ilyess/115097456275368741"  target="_blank" rel="noreferrer">Mastodon</a> or via email.</p>
<p>I appreciate your time and interest and hope these experiences resonate with or assist your own
Linux adventures. Until next time, take care!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://en.wikipedia.org/wiki/Chroot"  target="_blank" rel="noreferrer">chroot</a> is command that changes the apparent root directory for the current running
process and its children.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Although the <a href="https://wiki.archlinux.org/title/EFI_system_partition"  target="_blank" rel="noreferrer">EFI System Partition</a> article still claims that only <code>GRUB</code> and <code>rEFInd</code> support
the /efi mount point for EFI partitions. I bet Systemd-boot introduced support for /efi only
recently and the update hasn&rsquo;t yet fully propagated through the Arch wiki.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://en.wikipedia.org/wiki/Linux_Unified_Key_Setup"  target="_blank" rel="noreferrer">LUKS</a> stands for Linux Unified Key Setup. It&rsquo;s a disk encryption specification for Linux that
provides a standard for encrypting entire storage devices.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Each variation requiring a full system reboot to test out.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>I know. This is starting a bit repetitive but what can I tell you? No one said this was going
to be easy.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p><a href="https://en.wikipedia.org/wiki/Initial_ramdisk"  target="_blank" rel="noreferrer">initramfs</a>, abbreviated from &ldquo;initial RAM file system&rdquo;, is a temporary root file system used
during the boot process of Linux systems, allowing the kernel to load necessary drivers and
modules before the actual root file system is mounted.&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p><a href="https://wiki.archlinux.org/title/Mkinitcpio"  target="_blank" rel="noreferrer">mkinitcpio</a> is a tool that creates an initial ramdisk environment, essential for booting
the Linux kernel.&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p><a href="https://en.wikipedia.org/wiki/Tty_%28Unix%29"  target="_blank" rel="noreferrer">TTY</a> stands for &ldquo;teletypewriter,&rdquo; but in computing it refers to the text-based computer
terminals.&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
<a href="https://ilye.ss/posts/how-i-fixed-my-linux-installation/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>My New Note Taking Setup</title><link>https://ilye.ss/posts/my-new-note-taking-setup/</link><pubDate>Thu, 07 Aug 2025 01:00:00 -0400</pubDate><guid>https://ilye.ss/posts/my-new-note-taking-setup/</guid><description>I spent so much time in the past exploring different note taking solutions in order to put together a system that covers all my needs. I tried multiple apps and services, &amp;hellip;</description><content:encoded>
<![CDATA[<p>I spent so much time in the past <a href="https://ilye.ss/posts/my-journey-with-note-apps/" >exploring different note taking solutions</a>,
in order to put together a system that covers all my needs. I tried multiple apps and services, and
finally ended up with 2 disconnected note systems: one on my desktop, and one on my phone. It was
not ideal since I was sacrificing the sync functionality, but it was simple, and it did the job.</p>
<p>I slowly built up the reflex of checking out both the desktop and the phone whenever I&rsquo;m looking for
a note. That worked surprisingly well; It didn&rsquo;t feel like I was missing out on much. There might
have been a handful of occasions where I needed a note copied over to the other system, but
transferring it manually wasn&rsquo;t that big of a hassle. The system was stupidly simple, just the way I
like it, so I stuck with it for some time.</p>

<h2 class="relative group">A Serendipitous Discovery
    <div id="a-serendipitous-discovery" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#a-serendipitous-discovery" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>One day, I found out about a new <a href="https://github.com/epwalsh/obsidian.nvim"  target="_blank" rel="noreferrer">Neovim plugin for Obsidian</a>. It allows you to interact with
your Obsidian<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> vault from the comfort of Neovim<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. This alone was enough of a reason for me to
take it for a ride, and let me tell you: I instantly fell in love! I do all my writing in Neovim
anyway, including taking notes, so this was right up my alley. The plugin has some nice features
like browsing tags, easy note linking, note templates, and more<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>. This pleasant experience led me to
revisit Obsidian as a dedicated note taking solution on both desktop and mobile.</p>
<p>After some digging, I discovered that Obsidian supports data synchronization through a Git server.
And the cherry on top is that it doesn&rsquo;t have to be Github or any specific Git platform; any Git
server will do. This was the last piece of the puzzle for a new note taking system.</p>

<h2 class="relative group">The New System
    <div id="the-new-system" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-new-system" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>With these new findings, I spun up a local Gitea<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> instance to carry out the sync; I installed
Obsidian on both my phone and desktop and configured them to connect to my Gitea server. I admit I
couldn&rsquo;t make it work on the first try, but after some fiddling the system was operational. Notes
are finally synchronized on both ends through the Gitea server.</p>
<div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="note">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon">
</span></div>
        <div class="grow">
          Note
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>I didn&rsquo;t have to install Obsidian on my computer since I&rsquo;m using Neovim. I did it to take
advantage of the Git community plugin that automatically pushes local changes after a set timeout
and periodically pulls new changes from the Git server. It&rsquo;s a nice convenience so I don&rsquo;t have to
worry about running Git commands to keep my note vault up to date on desktop.</p></div></div><p>The new system consists of 3 components:</p>
<ol>
<li>Neovim on the desktop with the Obsidian plugin</li>
<li>The Obsidian app on my phone</li>
<li>A local instance of Gitea</li>
</ol>
<p>Not only is this system superior to its predecessor, it also maintains the same freedom from vendor
lock-in and privacy guarantees. It:</p>
<ul>
<li>Uses an open file format: All note files are in Markdown; my favorite text format.</li>
<li>Is completely offline: I&rsquo;m hosting the Gitea sync server off the internet so it&rsquo;s only accessible
within the local network, for maximum privacy;</li>
<li>Is private and ad-free: Neither Obsidian nor Gitea has any telemetry or ads.</li>
<li>Is totally free but partially open source: Both Obsidian and Gitea are free, but only Gitea is
open source.</li>
</ul>
<p>This setup basically checks most of the boxes; I couldn&rsquo;t be happier. I think I finally cracked the
code and put together a system that can last. So much so that I haven&rsquo;t felt the need to explore
other note taking options in a long time now<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>. Unless my current use cases change dramatically,
I&rsquo;m going to call this one &ldquo;problem solved.&rdquo; Given my picky nature and perfectionist tendencies, I
don&rsquo;t get to do this that often.</p>
<div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="note">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon">
</span></div>
        <div class="grow">
          Note
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>If I&rsquo;m being honest, using closed-source software goes against my values and doesn&rsquo;t sit very well
with me. Moving forward, I suspect I&rsquo;ll keep an eye out for any open source contenders to replace
Obsidian, unless the Obsidian team comes to their senses and publishes their code base under an
open-source license.</p></div></div>
<h3 class="relative group">Caveats
    <div id="caveats" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#caveats" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>One limitation of the system is the fact that synchronization only takes place when I&rsquo;m connected to
my local network. This is by design for increased privacy, but it comes with a slight inconvenience.
At times, I make changes to a note offline, say on my phone, when I&rsquo;m away from my local network but
forget to trigger a sync when I&rsquo;m back. If I happen to update the same note on the desktop and sync
the changes, it will cause a Git conflict the next time my phone attempts to sync. This doesn&rsquo;t
happen often enough to be annoying, however. It&rsquo;s just an edge-case that I need to be mindful of.
Besides, I&rsquo;ll take this limitation over having my precious notes out on the internet any day of the
week.</p>

<h2 class="relative group">Wrap Up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>It&rsquo;s been a long journey trying to customize my note taking process. My preference for simplicity
and privacy kept me on the hunt for the perfect system. I finally landed on Obsidian, despite having
discarded it in the past, with a custom Git server for synchronization. How about you? What&rsquo;s your
note taking process like? Feel free to share your setup, or any recommendations you think might
improve mine, on <a href="https://mastodon.online/@ilyess/114988744276241726"  target="_blank" rel="noreferrer">Mastodon</a>. You can also reply to this post via <a 
  class="email-link"
  href="#"
  data-email="aGlAaWx5ZS5zcw=="
  data-subject="RE: My New Note Taking Setup">email</a>. Thank you very much for tuning
in. Until next time, take care!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Obsidian is a free note taking app. Find out more about it <a href="https://obsidian.md/"  target="_blank" rel="noreferrer">here</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Neovim is a free and open-source, screen-based text editor program that runs on the terminal.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Let me know if you&rsquo;d be interested in a deep-dive into the Neovim Obsidian plugin.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Gitea is a free and open source Git server. Here&rsquo;s their <a href="https://about.gitea.com/"  target="_blank" rel="noreferrer">about page</a> for more details.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>I&rsquo;ve been using this setup for months now.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
<a href="https://ilye.ss/posts/my-new-note-taking-setup/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>Cookie Consent Popups Were a Mistake</title><link>https://ilye.ss/posts/cookie-consent-popups-were-a-mistake/</link><pubDate>Sat, 26 Jul 2025 04:00:00 -0400</pubDate><guid>https://ilye.ss/posts/cookie-consent-popups-were-a-mistake/</guid><description>&lt;p&gt;If you spent any amount of time online in the past few years, you probably noticed the prevalence of&#10;cookie consent modals. You know, those Popup-looking dialog boxes that jump at you, obfuscating most&#10;of the screen, if not all of it, just to tell you:&lt;/p&gt;&#10;&lt;blockquote&gt;&lt;p&gt;&amp;ldquo;Hey, we collect a bunch of data about your usage of this website and share it with 998723487 of&#10;our partners. Tell us you agree by clicking the only button below&amp;rdquo;.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>If you spent any amount of time online in the past few years, you probably noticed the prevalence of
cookie consent modals. You know, those Popup-looking dialog boxes that jump at you, obfuscating most
of the screen, if not all of it, just to tell you:</p>
<blockquote><p>&ldquo;Hey, we collect a bunch of data about your usage of this website and share it with 998723487 of
our partners. Tell us you agree by clicking the only button below&rdquo;.</p>
</blockquote><p>If you&rsquo;ve grown annoyed by this practice, you&rsquo;re not alone. No one asked for this, yet we all have
to do this dance whenever we want to visit a website like some sort of ritual jig at the entrance of
a religious site. How did we get here? Why did the entire internet all of a sudden get plagued by
these Popup windows, and is there a better way to accomplish what they were designed for without all
the dark patterns?</p>
<div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="tip">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon">
</span></div>
        <div class="grow">
          Tip
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>Use <a href="https://ublockorigin.com/"  target="_blank" rel="noreferrer">uBlockOrigin</a>. Not only does it block a lot of tracking by default, it also removes most cookie
consent Popup windows automatically. This means that you won&rsquo;t be tracked nor asked to be tracked.
The best of both worlds!</p></div></div>
<h2 class="relative group">How Did We Get Here?
    <div id="how-did-we-get-here" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#how-did-we-get-here" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>In 2016, the European Union (EU) published the <a href="https://en.wikipedia.org/wiki/General_Data_Protection_Regulation"  target="_blank" rel="noreferrer">General Data Protection Regulation (GDPR)</a> which is a
set of rules that govern website practices operating inside the EU. GDPR&rsquo;s main goal is to protect
users&rsquo; online privacy. This was a long time coming and I&rsquo;m glad the EU took this step. The practices
some websites adopted have gotten way out of control and turned them into pervasive data mining
devices. Without going in too much detail and drowning in lawyer jargon, the GDPR stipulates that
websites must inform their users of any data stored on their device (this obviously includes
cookies) along with the purpose of doing so, and collect their consent. This sounds like a great
idea  —  it gives the user control over their computer: They get to decide whether any external
entity is allowed to store and access data on their machine. Notice that there&rsquo;s no mention of Popup
windows; the implementation was left up to website operators to define and that&rsquo;s where the mandate
fell short in my opinion.</p>
<div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="warning">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon">
</span></div>
        <div class="grow">
          Disclaimer
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>I&rsquo;m not a lawyer or a privacy regulator, so take everything I say here with a grain of salt.</p></div></div><p>To comply with these regulations, companies introduced a dialog box on their website asking visitors
for permission to use cookies. Most of them don&rsquo;t allow any user action on the website until the
cookie Popup window is addressed. Some even deny users access if they don&rsquo;t accept, so is it really
a choice after all? It gets worse. Some websites default to collecting your data and using cookies
<em>until</em> you click the &ldquo;Decline&rdquo; button. So the choice boils down to:</p>
<ol>
<li>Accept, otherwise you can&rsquo;t do anything; or</li>
<li>Deny, so you can&rsquo;t do anything; or</li>
<li>Ignore, which is effectively the same as &ldquo;Accept&rdquo;.</li>
</ol>
<p>What a mess!</p>

<h2 class="relative group">Can We Do Better?
    <div id="can-we-do-better" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#can-we-do-better" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>GDPR merely talks about providing information around data stored on users&rsquo; devices and collecting
their consent, without dictating <em>how</em> to do so. A better approach to achieve this, in my opinion,
is to have a browser setting to indicate consent. In the absence of this consent indicator, websites
must refrain from storing and/or accessing browser cookies, in order to comply with GDPR. The good
news is we already have a browser setting that was designed specifically for use cases like this
one. It&rsquo;s called the <a href="https://en.wikipedia.org/wiki/Global_Privacy_Control"  target="_blank" rel="noreferrer">Global Privacy Control (GPC)</a>. It&rsquo;s a simple HTTP header that signals the
user&rsquo;s opting out of tracking or having their data collected and/or sold.</p>
<blockquote><p>GPC is a valid do-not-sell-my-personal-information signal according to the California Consumer
Privacy Act (CCPA), which stipulates that websites are legally required to respect a signal sent
by users who want to opt-out of having their personal data sold.  —  Wikipedia<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
</blockquote><p>I don&rsquo;t know about you but to me, this sounds like exactly what we need for the job. Users could
install a browser extension that automatically attaches this HTTP header to all outgoing requests. I
haven&rsquo;t looked but I bet there are multiple extensions meant for this or that could be used for this
purpose. Or better yet, browsers could natively support this feature through a setting toggle. Once
configured, all visited websites would receive this header and therefore be informed of the user&rsquo;s
opt-out decision. No Popups needed  —  totally transparent for the end user.</p>
<p>If such a simple solution exists, why did the industry rush to implement cookie consent Popups? Why
build a UI element with text, links and buttons, worry about scaling and placement, bloat the page
DOM<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, and potentially increase load time? While I can&rsquo;t claim to have a definite answer to these
questions, I can sure take a guess.</p>
<p>See? With a simple HTTP header that the browser broadcasts online, there&rsquo;s no wiggle room. The
signal is clear: either the header exists with a value of <code>1</code> or it doesn&rsquo;t. It&rsquo;s binary, and not up
for debate. In the case of a Popup, however, websites still have a chance to wheedle the user into
opting in to data collection. With a Popup, they&rsquo;re free to deploy all the dark patterns in the book
to get you to act in a certain way: to surrender your consent, in this case. They get to design the
Popup so that it covers the entire page for instance, to maximize friction; make the &ldquo;Accept&rdquo; button
so atrociously flashy that you&rsquo;re blinded to everything else around it; make it so effortless to
click, or tap, that it almost sucks in your cursor, or thumb, like a powerful vacuum cleaner in face
of an empty plastic bag. They get to make the &ldquo;Decline&rdquo; button so minuscule that it&rsquo;s destined to be
missed.</p>
<ul>
<li><em>Wait! Does it have to be a button? Let&rsquo;s make that a link&hellip; And make the font color merely a
couple bits off the background color so it&rsquo;s only visible under a narrow wave length band of UV
light.</em></li>
<li><em>Hold on! Should we even bother with a &ldquo;Decline&rdquo; button or link? Yeah, let&rsquo;s just leave it out.</em></li>
</ul>
<p>I recognize that I&rsquo;m generalizing here. There are probably websites out there that already honor the
GPC header, and others that found ways to do without cookies altogether. However, it still feels
like the vast majority of websites fall in the cookie Popup camp, unfortunately.</p>
<div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="question">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon">
</span></div>
        <div class="grow">
          Question
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>Do you know of any large-scale analysis that looked into GPC support versus the use of cookie
Popups?</p></div></div>
<h2 class="relative group">Where Do We Go From Here?
    <div id="where-do-we-go-from-here" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#where-do-we-go-from-here" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>In my opinion, all regulations in place today must be revisited to explicitly prohibit these dark
patterns and clearly endorse, if not require, the use of GPC for the purpose of tracking consent
collection, like the CCPA. In the meantime, I suggest you equip your browser with a good ad/tracking
blocking add-on like uBlockOrigin, and configure GPC.</p>

<h2 class="relative group">Wrap Up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>And that&rsquo;s why I believe cookie Popups were a big mistake. I hope I managed to get my points across
and that my lousy humor didn&rsquo;t put you off too much. Now, I&rsquo;m curious to know how you deal with the
annoying cookie Popups. Do you mindlessly hit &ldquo;Accept&rdquo; every time just to get through? Do you use
some sort of browser add-on to block or bypass this step? Let me know by replying to this post on
<a href="https://mastodon.online/@ilyess/114921657659707012"  target="_blank" rel="noreferrer">Mastodon</a> or reaching out via <a 
  class="email-link"
  href="#"
  data-email="aGlAaWx5ZS5zcw=="
  data-subject="RE: Cookie Consent Popups Were a Mistake">email</a>. Thank you so much for tuning in. Until next time, take care!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://en.wikipedia.org/wiki/Global_Privacy_Control"  target="_blank" rel="noreferrer">https://en.wikipedia.org/wiki/Global_Privacy_Control</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://en.wikipedia.org/wiki/Document_Object_Model"  target="_blank" rel="noreferrer">The Document Object Model (DOM)</a> is a cross-platform and language-independent interface that
treats an HTML or XML document as a tree structure.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
<a href="https://ilye.ss/posts/cookie-consent-popups-were-a-mistake/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>How I Broke My Linux Installation</title><link>https://ilye.ss/posts/how-i-broke-my-linux-installation/</link><pubDate>Sat, 19 Jul 2025 04:00:00 -0400</pubDate><guid>https://ilye.ss/posts/how-i-broke-my-linux-installation/</guid><description>&lt;p&gt;The other day, I was chilling on my computer, reading the &lt;a href="https://wiki.archlinux.org/title/Arch_boot_process" target="_blank" rel="noreferrer"&gt;Arch boot process&lt;/a&gt; on the Arch wiki,&#10;like one does. I&amp;rsquo;m familiar with the high-level boot sequence but the exact details were blurry in&#10;my head so I was due for a refresher. Maybe around half-way through the article, I remembered&#10;&lt;a href="https://ilye.ss/posts/dual-boot-struggles/" &gt;my recent troubles&lt;/a&gt; with EFI variables&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; and wondered if my current bootloader was somehow&#10;faulty after all. Would simply moving to a different bootloader resolve the issue&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;? I heard&#10;positive things about Systemd-boot&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; so I figured it&amp;rsquo;s a good opportunity to give it a try. This&#10;has been on my mind for a while now, since I&amp;rsquo;m constantly striving for a lean Linux installation.&#10;With that decision made, I set out to go over the Systemd-boot &lt;a href="https://wiki.archlinux.org/title/Systemd-boot" target="_blank" rel="noreferrer"&gt;Arch wiki article&lt;/a&gt; right after I&amp;rsquo;m&#10;done with the boot process one.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>The other day, I was chilling on my computer, reading the <a href="https://wiki.archlinux.org/title/Arch_boot_process"  target="_blank" rel="noreferrer">Arch boot process</a> on the Arch wiki,
like one does. I&rsquo;m familiar with the high-level boot sequence but the exact details were blurry in
my head so I was due for a refresher. Maybe around half-way through the article, I remembered
<a href="/posts/dual-boot-struggles/" >my recent troubles</a> with EFI variables<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and wondered if my current bootloader was somehow
faulty after all. Would simply moving to a different bootloader resolve the issue<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>? I heard
positive things about Systemd-boot<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> so I figured it&rsquo;s a good opportunity to give it a try. This
has been on my mind for a while now, since I&rsquo;m constantly striving for a lean Linux installation.
With that decision made, I set out to go over the Systemd-boot <a href="https://wiki.archlinux.org/title/Systemd-boot"  target="_blank" rel="noreferrer">Arch wiki article</a> right after I&rsquo;m
done with the boot process one.</p>
<div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="info">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon">
</span></div>
        <div class="grow">
          Info
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>It&rsquo;s generally recommended to use Systemd-boot if you&rsquo;re using Systemd on your system and
don&rsquo;t need advanced boot loading customization.</p></div></div>
<h2 class="relative group">Configuring Systemd-boot
    <div id="configuring-systemd-boot" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#configuring-systemd-boot" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Given the sensitive nature of the boot process, I took some time to prepare a bootable USB drive
with the Arch Linux ISO<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>, before changing anything. I picked Ventoy<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> for the job. It&rsquo;s a nice
piece of software that I&rsquo;ve been wanting to try out anyway.</p>
<p>On the surface, setting up Systemd-boot is fairly simple. All you have to do is run the <code>bootctl install</code> command and that should take care of everything. Unfortunately, that didn&rsquo;t go so smoothly
for me. The command resulted in an EFI vars input/output error - an error I&rsquo;m starting to get
<del>annoyed by</del> familiar with at this point. I ensured that my system was booted through UEFI<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>,
and that EFI vars were all loaded as they should. I went through <code>bootctl</code>&rsquo;s man page and ran the
command a bunch of time with different flags, but it kept falling flat. It copied some files into
the boot location but always ended in that EFI error. Despite my enthusiasm to get Systemd-boot
working, I had to shift gears and focus on my EFI situation before I can make any meaningful
progress.</p>

<h2 class="relative group">Stubborn EFI
    <div id="stubborn-efi" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#stubborn-efi" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I started by resetting the BIOS to its default configuration. That only made matters worse because
it enabled Secure Boot which blocked the bootloader, so I had to turn it off<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>. After that, I
cleared the NVRAM<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup> state that holds EFI vars. It didn&rsquo;t seem to make a difference, except that it
nuked the GRUB loader. Now, I&rsquo;m getting a Systemd-boot loader instead with a single entry to reboot
into BIOS. I&rsquo;m not sure if this was the result of wiping the NVRAM or the <code>bootctl install</code>
command. Perhaps the command did actually work halfway despite the EFI input/output error? Either
way, now that the bootloader is corrupted, there&rsquo;s no giving up. I must sort this out if I want to
get my system back up and running.</p>
<p>Next, I flushed the BIOS with the latest firmware. This part took way more time than I&rsquo;m
willing to admit. I didn&rsquo;t have a spare USB stick on hand to use for the firmware update. So I
needed to find a way to use my Ventoy USB drive without having to remove Ventoy, because I might
need it to boot into a live ISO for debugging. By default, Ventoy creates 2 partitions on the USB
drive: One bootable partition for Ventoy itself, and another exFAT<sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup> partition to host ISO files.
I figured I could just drop the firmware file in the exFAT partition and load it from the BIOS. Not
so fast there, Stacy! The BIOS native file browser couldn&rsquo;t find the Ventoy partition for some
reason. I kept looking and browsing other disks from the BIOS in search for the firmware file, but
to no avail.</p>
<p>Well, it turns out my BIOS doesn&rsquo;t support exFAT partitions. Or so it appeared - all I know for sure
is that I wasn&rsquo;t able to get to the Ventoy partition from the BIOS. Next step was to format that
partition using a more widely supported filesystem, like FAT32. Luckily, that did the trick. I was
able to read the now-FAT32-formatted Ventoy partition, find the firmware file, and flush it to BIOS.
Once this side quest completed, I could go back to the main task: figuring out the EFI vars IO error
and installing Systemd-boot.</p>
<div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="note">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon">
</span></div>
        <div class="grow">
          Note
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>Keep in mind that I&rsquo;m doing all this without my usual tools. My main computer is the very machine
I&rsquo;m troubleshooting, since my bootloader is gone. So I&rsquo;m making do with whatever old,
half-working, potato machine I can get my hands on, hoping it, too, doesn&rsquo;t break on me. The
seemingly simple maneuver of downloading the latest firmware from the motherboard&rsquo;s vendor
website, throwing it in a flash drive, and loading it in the BIOS for a firmware update ended up
being quite challenging.</p></div></div><p>Now that the BIOS is running a shiny new firmware version, I can finally get down to business. I
grabbed a copy of the Arch Linux ISO and placed it in the Ventoy partition. Fortunately, formatting
the Ventoy USB drive only formatted the partition where ISO files are stored and didn&rsquo;t touch the
Ventoy bootable partition, so Ventoy was still intact. On top of that, Ventoy supports FAT32
partitions which means I didn&rsquo;t have to reformat that ISO storage partition. I&rsquo;m telling you, Ventoy
is a piece of art!</p>
<p>I&rsquo;m now at a point where I can simply boot into the Arch Linux ISO, chroot into my system, and
install Systemd-boot. If you&rsquo;re thinking &ldquo;yeah, right. Easier said than done.&rdquo; you&rsquo;d be totally
right. But this article is getting long already, so I&rsquo;m going to have to cut it here. Stay tuned for
what happens next.</p>

<h2 class="relative group">Wrap Up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>You saw how I managed to totally obliterate my boot loader thereby rendering the whole system
unbootable. I&rsquo;ve gone through the steps I took to troubleshoot the problem, and touched on some
findings like the lack of exFAT support in my BIOS for firmware updates, and the discovery of the
amazing bootable USB drive tool called Ventoy. How about you? When was the last time you broke your
Linux system and what was the issue? Also, if you have any ideas on what could be wrong with my EFI
vars, I&rsquo;m all ears! Feel free to reach out on <a href="https://mastodon.online/@ilyess/114880059807946365"  target="_blank" rel="noreferrer">Mastodon</a> or via <a 
  class="email-link"
  href="#"
  data-email="aGlAaWx5ZS5zcw=="
  data-subject="RE: How I Broke My Linux Installation">email</a>. Until next time, take care.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>EFI variables are key/value pairs used in the UEFI system to store non-volatile data, such as
boot configuration settings.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>The problem is an input/output error I get whenever I try to change the boot order. More on
this in my <a href="/posts/dual-boot-struggles/" >Dual Boot Struggles</a> article.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Systemd-boot is a simple boot manager that comes pre-installed with Systemd.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>An ISO file, or live image, is a bootable version of an operating system that can run directly
from a removable medium like a USB stick without needing to be installed on a hard drive.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Ventoy is an open-source tool that allows you to create bootable USB drives for various
operating systems using ISO files without needing to format the drive each time.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>Unified Extensible Firmware Interface.&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p>It is possible to boot Arch Linux with Secure Boot enabled but I&rsquo;ll park this for another day.&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p>Non-Volatile Random Access memory. It&rsquo;s a type of memory that retains data even when the power
is turned off.&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9">
<p>exFAT is a file system optimized for flash memory such as USB flash drives and SD cards.&#160;<a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
<a href="https://ilye.ss/posts/how-i-broke-my-linux-installation/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>Dual Boot Struggles</title><link>https://ilye.ss/posts/dual-boot-struggles/</link><pubDate>Mon, 02 Jun 2025 03:00:00 -0400</pubDate><guid>https://ilye.ss/posts/dual-boot-struggles/</guid><description>&lt;p&gt;On a beautiful day, I was minding my own business, tinkering around on my computer and suddenly&amp;hellip;&#10;Pitch black! Power outage. All electronics go dark, and I have to resort to pre-digital-era&#10;distractions until the power&amp;rsquo;s back. No big deal. Some time passes and the power is finally&#10;restored. Back in my comfy seat, ready to pick up my digital wandering where it got interrupted, I&#10;press the power button and eagerly wait. Few seconds later, I&amp;rsquo;m greeted with an unfamiliar boot&#10;menu. It definitely isn&amp;rsquo;t &lt;a href="https://en.wikipedia.org/wiki/GNU_GRUB" target="_blank" rel="noreferrer"&gt;Grub&lt;/a&gt;, to my surprise. It&amp;rsquo;s the Windows bootloader&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&amp;hellip; How? Why?&#10;Where did my beloved Arch installation go? And just like that, down the rabbit hole I dove in&#10;pursuit of a plausible explanation for this mystery. But before I take you down with me through this&#10;tortuous investigation, I think I need to lay down some foundational context first.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>On a beautiful day, I was minding my own business, tinkering around on my computer and suddenly&hellip;
Pitch black! Power outage. All electronics go dark, and I have to resort to pre-digital-era
distractions until the power&rsquo;s back. No big deal. Some time passes and the power is finally
restored. Back in my comfy seat, ready to pick up my digital wandering where it got interrupted, I
press the power button and eagerly wait. Few seconds later, I&rsquo;m greeted with an unfamiliar boot
menu. It definitely isn&rsquo;t <a href="https://en.wikipedia.org/wiki/GNU_GRUB"  target="_blank" rel="noreferrer">Grub</a>, to my surprise. It&rsquo;s the Windows bootloader<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>&hellip; How? Why?
Where did my beloved Arch installation go? And just like that, down the rabbit hole I dove in
pursuit of a plausible explanation for this mystery. But before I take you down with me through this
tortuous investigation, I think I need to lay down some foundational context first.</p>

<h2 class="relative group">My Dual Boot Setup
    <div id="my-dual-boot-setup" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#my-dual-boot-setup" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I currently have a dual-boot setup with Windows and Arch Linux on my computer. Wait! Before you
leave, let me explain. I don&rsquo;t use Windows, and haven&rsquo;t in many years now. When I first got my
computer back in the day, it came with Windows installed. So when it was time for me to completely
switch to Linux, I just created a second partition to host my new favorite OS and left the Windows
partition there until I get around to backing up any data I might have left behind. Needless to
say I never got to do that. This hasn&rsquo;t really bothered me, to be honest, since the Windows
partition is small and I have way more disk space than I need on the Linux partition. When I
installed my current Linux distro<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, I had configured Grub to default to it, so I don&rsquo;t even
see the Windows entry most of the time and I&rsquo;ve kind of forgotten it was there. To recap, I have
Grub as my bootloader that supports both my Arch and Windows installations, and the Windows
bootloader is completely out of the picture.</p>

<h2 class="relative group">The Investigation
    <div id="the-investigation" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-investigation" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Now, back to the power outage. I was confused by the Windows bootloader and didn&rsquo;t know where to go
from there. Hitting the escape key took me to the BIOS menu. Nothing seemed to trigger Grub to load.
Since I was on the BIOS menu, I went to check the boot order. That seemed fine, except the item for
booting from the hard drive now had &ldquo;Windows&rdquo; in the name, instead of &ldquo;Grub&rdquo;. I was getting a bit
stuck, and slowly coming to terms with the possibility that it might be a hardware failure.</p>
<p>I figured the boot partition got corrupted by the power outage, so I whipped out my trusty Linux
flash drive and plugged it in. Once in the live environment, I tried to mount the boot partition and
inspect it. Everything looked normal. I was able to mount all partitions and read their content. The
problem had to come from somewhere else.</p>
<p>Next, I started poking around a little deeper in the BIOS menu, since I kept being taken there
whenever I tried to skip the Windows bootloader. I noticed that Secure Boot was enabled despite
having a vague memory of having to disable it in the past. I decided to disable it and see what
happens. I rebooted the computer and to my disappointment, the Windows bootloader was drawn on the
screen. &ldquo;I have to keep digging!&rdquo;, I thought to myself. I hit the escape key, ready to peel off even
deeper layers of the BIOS menu, except it didn&rsquo;t show up! I was instead taken to the Grub loader.
Finally! I can boot my Arch installation once again. It seemed as if my computer reset the Secure
Boot to &ldquo;enabled&rdquo; in the BIOS after the power outage. I have no idea why this would happen, though.
Perhaps the motherboard battery was drained so the firmware got reset to its factory settings when
the power came back? Unclear.</p>
<p>Now that I have a workaround to load my beloved Linux installation, I started troubleshooting the
Grub configuration and EFI boot setup. Grub looked fine. I even reloaded the configuration, which
had no noticeable effect. I used the <code>efibootmgr</code> command to examine the EFI boot configuration and
noticed that the boot order was off indeed. The Windows Boot Manager was the one set to launch
first, which explains the problem I was seeing. But when I tried to update the boot order with
<code>efibootmgr -o</code> in order to promote the Grub loader to the top of the list, I kept getting a weird
error that read:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>Could not set BootOrder: Input/output error
</span></span><span style="display:flex;"><span>error trace:
</span></span><span style="display:flex;"><span> efivarfs.c:441 efivarfs_set_variable<span style="color:#f92672">()</span>: writing to fd <span style="color:#ae81ff">5</span> failed: Input/output error
</span></span><span style="display:flex;"><span> lib.c:78 _efi_set_variable_mode<span style="color:#f92672">()</span>: ops-&gt;set_variable<span style="color:#f92672">()</span> failed: Input/output error</span></span></code></pre></div></div>
<p>I did some research to understand what this was all about but without any conclusive findings. So, I
decided to call it a win and not invest any more time on this issue, especially since I at least
have a reasonable workaround. It&rsquo;s just an extra keystroke<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> during boot time.
Furthermore, I have been planning to nuke the Windows partition for the longest time but haven&rsquo;t had
the chance to get to it. Maybe this is yet another reason to bump that up in my to-do list.</p>

<h2 class="relative group">Wrap Up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Given where this problem started, i.e. not being able to boot my Arch installation at all, I&rsquo;m glad
I landed in a position where I can at least get to the Grub menu during boot time. However, I&rsquo;ve got
to admit that I&rsquo;m still very curious about the root cause of the immutable EFI boot order. Have you
had a similar issue before, or know what could be causing this? If so, please reach out on <a href="https://mastodon.online/@ilyess/114616512449560060"  target="_blank" rel="noreferrer">Mastodon</a>
or via <a 
  class="email-link"
  href="#"
  data-email="aGlAaWx5ZS5zcw=="
  data-subject="RE: Dual Boot Struggles">email</a>. I would love to hear your insights! If not, no big deal. Thanks for tuning in to
my latest struggle in my Linux journey. Until next time, take care!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Also known as the Windows Boot Manager&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Arch Linux&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>I need to quickly hit the escape key on the Windows Boot Manager menu&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
<a href="https://ilye.ss/posts/dual-boot-struggles/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>Nvidia Finally Working on Wayland</title><link>https://ilye.ss/posts/nvidia-finally-working-on-wayland/</link><pubDate>Fri, 03 Jan 2025 03:00:00 -0400</pubDate><guid>https://ilye.ss/posts/nvidia-finally-working-on-wayland/</guid><description>&lt;p&gt;I&amp;rsquo;ve been on &lt;a href="https://archlinux.org/" target="_blank" rel="noreferrer"&gt;Arch Linux&lt;/a&gt; for years now and if there&amp;rsquo;s one thing I miss from the old Windows days,&#10;it&amp;rsquo;s got to be gaming. I&amp;rsquo;m not a professional gamer by any stretch of the imagination, or a gamer&#10;period, for that matter. I just like to check out some titles here and there, mainly for immersive&#10;experiences, and the occasional intellectual puzzle. If I were to guess, I&amp;rsquo;d put my average play&#10;time around the 2-to-3h-per-month ballpark. So, &amp;ldquo;sacrificing&amp;rdquo; gaming for the benefit of a better&#10;operating system was a no-brainer. I took the plunge the moment I could - no regrets there. That&#10;being said, gaming was always at the back of my mind. I&amp;rsquo;d see new games come out, or gameplay&#10;recordings of old favorite games of mine, and just wish I could whip out my Fortnite costume and&#10;sink one or two hours in an adrenaline-spiking FPS game.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>I&rsquo;ve been on <a href="https://archlinux.org/"  target="_blank" rel="noreferrer">Arch Linux</a> for years now and if there&rsquo;s one thing I miss from the old Windows days,
it&rsquo;s got to be gaming. I&rsquo;m not a professional gamer by any stretch of the imagination, or a gamer
period, for that matter. I just like to check out some titles here and there, mainly for immersive
experiences, and the occasional intellectual puzzle. If I were to guess, I&rsquo;d put my average play
time around the 2-to-3h-per-month ballpark. So, &ldquo;sacrificing&rdquo; gaming for the benefit of a better
operating system was a no-brainer. I took the plunge the moment I could - no regrets there. That
being said, gaming was always at the back of my mind. I&rsquo;d see new games come out, or gameplay
recordings of old favorite games of mine, and just wish I could whip out my Fortnite costume and
sink one or two hours in an adrenaline-spiking FPS game.</p>
<div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="note">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon">
</span></div>
        <div class="grow">
          Note
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>I don&rsquo;t actually have a gaming costume - it&rsquo;s just an expression. I don&rsquo;t even remember ever
playing Fortnite, come to think of it. Do you think I should give it a try?</p></div></div><p>Fast-forward to today, or more accurately few days ago, and I finally managed to get my NVIDIA card
working on Arch Linux and <a href="https://wayland.freedesktop.org/"  target="_blank" rel="noreferrer">Wayland</a> with no hacky workarounds or precarious maintenance routines. It
just works! It worked right after I sorted everything out, it works after reboots, it&rsquo;s working
right now as I type these words on my terminal that renders through the dedicated graphics card
(yes, you can have your terminal run on the GPU - maybe it&rsquo;s a topic for another post), and should
continue working after driver updates that are managed by my package manager! Can you believe it?
All right, I&rsquo;m getting ahead of myself here - I got to tame my excitement, and tell you how I got
here.</p>

<h2 class="relative group">The Struggle
    <div id="the-struggle" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-struggle" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I&rsquo;ve been using open-source graphic drivers that support NVIDIA, like <a href="https://wiki.archlinux.org/title/Nouveau"  target="_blank" rel="noreferrer">Nouveau</a>, for the bigger part
of my time on Linux. The problem was&hellip; they all suck, if they work at all. As a matter of fact, I
was inadvertently using the integrated GPU with the dedicated one completely off for so long without
noticing. That&rsquo;s how comparable NVIDIA&rsquo;s performance with open-source drivers was to that of just
plain simple integrated GPU. Watching my NVIDIA card sit there and collect dust never sat well with
me however. At first, whenever I had some free time I would take another stab at making it work. I
had some noticeable success when I was on <a href="https://en.wikipedia.org/wiki/X_Window_System"  target="_blank" rel="noreferrer">X11</a> by using the official proprietary driver straight from
NVIDIA&rsquo;s website. It worked well with few caveats that I was ready to put up with for the sake of
an improved graphic experience.</p>
<p>Sadly, when I switched to Wayland, all hell broke loose. Those two (NVIDIA and Wayland) did not like
one another - they actively despised and energetically repelled each other. It was like trying to
sit a magnet on top of another with similar poles facing each other. I remember once diving so deep
in the rabbit hole that I found the line in Gnome&rsquo;s source code where they explicitly ignore
everything that has to do with NVIDIA when Wayland is loaded. Yeah, it was <em>that</em> bad! Shortly after
this attempt, with a vivid memory of so many failures, black screens, frozen desktop environments,
bricked systems, and geometrically paradoxical screen resolutions, I silently gave up. I felt like
my hands were tied and that there wasn&rsquo;t much I could do to marry NVIDIA and Wayland on my machine.
This isn&rsquo;t to say that it was impossible. I&rsquo;d read multiple testimonials of people enjoying the full
performance of their NVIDIA cards with Wayland, but there was something with my system that made
reproducing a similar configuration challenging, to say the least. But, this all changed few days
ago.</p>

<h2 class="relative group">The Win
    <div id="the-win" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-win" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>A short while ago, I randomly decided it was time I tried to sort out my NVIDIA+Wayland situation. I
don&rsquo;t remember exactly what triggered it. The trauma from previous attempts had started to fade
away, I suppose, and I had some time on my hands so I thought &ldquo;Why not?&rdquo;. I headed over to the
trusty <a href="https://wiki.archlinux.org/title/Main_page"  target="_blank" rel="noreferrer">Arch Wiki</a>, read through the NVIDIA section, and diligently followed
the steps that applied to my system and configuration. Surprisingly, all I ended up doing was
installing the official NVIDIA driver from the official Arch package repository (this wasn&rsquo;t
available last time I tried by the way, so it gave me hope on the spot), and updating the
<code>/etc/mkinitcpio.conf</code> file to remove <code>kms</code> from the <code>HOOKS</code> array. Et voila! I rebooted my system
and there it was - the NVIDIA card was working! I could see it in the <code>nvidia-smi</code> output, and I
could feel it in the smoothness of the animations and the responsiveness of the system. I was
ecstatic! I couldn&rsquo;t believe it was that easy. I was so used to the idea that getting NVIDIA to work
on Wayland was a monumental task that I had to prepare myself for, that I didn&rsquo;t even consider the
possibility that it could be as simple as installing a package and updating a configuration file. I
was over the moon! But, I wasn&rsquo;t done yet.</p>
<p>Now that I had the NVIDIA card properly configured, I wanted to make the most out of it. But I
didn&rsquo;t want to use it all the time. I wanted to keep the hybrid setup I had going on with the
integrated GPU handling the day-to-day tasks and the NVIDIA card kicking in for the heavy lifting
when strenuous rendering was needed. The default hybrid mode was doing a pretty good job at this,
but I wanted to take it a step further. I wanted to force the NVIDIA card to render specific apps
that I knew would benefit from the extra power, like Firefox when watching videos. I spent quite a
bit of time on this and I&rsquo;m happy to say that I managed to get it working. I used the <code>prime-run</code>
command to launch the apps I wanted to render with the NVIDIA card, and it worked like a charm. I
was hoping to accomplish this without manually updating the launch command of each app, but I
couldn&rsquo;t find a way to do it. If you know of a different way to achieve this, please let me know via
<a 
  class="email-link"
  href="#"
  data-email="aGlAaWx5ZS5zcw=="
  data-subject="RE: Nvidia Finally Working on Wayland">email</a> or on <a href="https://mastodon.online/@ilyess/113766705195593318"  target="_blank" rel="noreferrer">Mastodon</a>.</p>

<h2 class="relative group">What&rsquo;s Next
    <div id="whats-next" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-next" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I&rsquo;m not done yet, unfortunately. Nothing needs a powerful GPU more than games, and I haven&rsquo;t had the
chance to test any games yet. I can finally satiate my envy of Linux gamers and see what all the
fuss is about. I&rsquo;m excited to see how games run on my system. I&rsquo;m not expecting to be blown away by
the performance, but I&rsquo;m hoping to have a decent experience.</p>
<p>Also, I still want to move back to a <a href="https://en.wikipedia.org/wiki/Window_manager"  target="_blank" rel="noreferrer">window manager</a>. I&rsquo;ve been using Gnome for a while now because I
really wanted to switch to Wayland, even if it meant forfeiting my beloved window manager. But now
that I have successfully configured NVIDIA to function seamlessly on Wayland, I&rsquo;m finally able to
return to the refined simplicity of a window manager. I have my eyes set on Sway. Having heard
numerous commendations, I&rsquo;m eager to explore its merits and experience it firsthand. I&rsquo;ll probably
publish a post on the subject once I&rsquo;ve had some time to play around with Sway. So, stay tuned<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> for
that.</p>

<h2 class="relative group">Wrap Up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>My goal with this post was to give you a glimpse into my journey with NVIDIA on Wayland. I wanted to
share my struggles, my wins, and my plans for the future. I hope you found it interesting and maybe
even helpful. If you&rsquo;re in a similar situation, I hope this post gives you hope that you can get
NVIDIA working on Wayland too. If you have any suggestions, tips, or questions, please don&rsquo;t
hesitate to reach out on <a href="https://mastodon.online/@ilyess/113766705195593318"  target="_blank" rel="noreferrer">Mastodon</a> or via <a 
  class="email-link"
  href="#"
  data-email="aGlAaWx5ZS5zcw=="
  data-subject="RE: Nvidia Finally Working on Wayland">email</a>. I&rsquo;d love to hear from you. Until next time, take
care and Happy New Year!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Feel free to subscribe to my blog&rsquo;s <a href="/posts/index.xml" >RSS feed</a> to get notified when I publish new posts.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
<a href="https://ilye.ss/posts/nvidia-finally-working-on-wayland/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>Signal Usernames</title><link>https://ilye.ss/posts/signal-usernames/</link><pubDate>Fri, 01 Mar 2024 11:17:49 -0500</pubDate><guid>https://ilye.ss/posts/signal-usernames/</guid><description>&lt;p&gt;&lt;a href="https://signal.org" target="_blank" rel="noreferrer"&gt;Signal&lt;/a&gt; has positioned itself as one of, if not the best mainstream messaging service when it comes&#10;to preserving its users&amp;rsquo; privacy. Everything, including metadata, is end-to-end encrypted using the&#10;&lt;a href="https://signal.org/blog/pqxdh/" target="_blank" rel="noreferrer"&gt;quantum resistant&lt;/a&gt; Signal protocol. They have group video call support, &lt;a href="https://signal.org/blog/new-features-fall-2023/" target="_blank" rel="noreferrer"&gt;text formatting&lt;/a&gt;, message&#10;editing and scheduling, disappearing messages, the controversial stories, and more. It&amp;rsquo;s all great&#10;until we bring up phone numbers. I think the phone number requirement has been one of the most&#10;contested aspects of the messaging service so far. In order to connect with anyone on the platform&#10;you have to share phone numbers and that doesn&amp;rsquo;t sit well with privacy-sensitive folks. Exchanging&#10;phone numbers isn&amp;rsquo;t that big of a deal when we talk about family and close friends, but when it&amp;rsquo;s&#10;time to link up with a client, business partner, or just an acquaintance from the internet, people&#10;tend to be reluctant in handing out their phone numbers, and rightfully so. Now all of this has&#10;changed!&lt;/p&gt;</description><content:encoded>
<![CDATA[<p><a href="https://signal.org"  target="_blank" rel="noreferrer">Signal</a> has positioned itself as one of, if not the best mainstream messaging service when it comes
to preserving its users&rsquo; privacy. Everything, including metadata, is end-to-end encrypted using the
<a href="https://signal.org/blog/pqxdh/"  target="_blank" rel="noreferrer">quantum resistant</a> Signal protocol. They have group video call support, <a href="https://signal.org/blog/new-features-fall-2023/"  target="_blank" rel="noreferrer">text formatting</a>, message
editing and scheduling, disappearing messages, the controversial stories, and more. It&rsquo;s all great
until we bring up phone numbers. I think the phone number requirement has been one of the most
contested aspects of the messaging service so far. In order to connect with anyone on the platform
you have to share phone numbers and that doesn&rsquo;t sit well with privacy-sensitive folks. Exchanging
phone numbers isn&rsquo;t that big of a deal when we talk about family and close friends, but when it&rsquo;s
time to link up with a client, business partner, or just an acquaintance from the internet, people
tend to be reluctant in handing out their phone numbers, and rightfully so. Now all of this has
changed!</p>

<h2 class="relative group">Usernames and Phone Number Privacy
    <div id="usernames-and-phone-number-privacy" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#usernames-and-phone-number-privacy" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Signal finally <a href="https://signal.org/blog/phone-number-privacy-usernames/"  target="_blank" rel="noreferrer">launched usernames</a> this week. This feature has been <a href="https://community.signalusers.org/t/usernames-in-signal/9157"  target="_blank" rel="noreferrer">in the works</a> for so long, I bet
most people have already given up on it. So much so that an inside joke was born out of it; it&rsquo;s
called &ldquo;Real Soon Now&rdquo;. I believe this started about 2 years ago when the former CEO Moxie had
publicly posted those words in <a href="https://twitter.com/moxie/status/1480643863970816001"  target="_blank" rel="noreferrer">response to a tweet</a> asking about the launch date for usernames. You know, those
&ldquo;usernames when&rdquo; type of tweets. Anyway, now that the feature is out, let&rsquo;s take a look at what the
hype is all about and see how people have reacted to the news so far.</p>
<p>What Signal calls a username is a nickname at least 3 characters long and a number discriminator of
at least 2 digits, joined with a period (e.g.: <code>bob.17</code>). The username is attached to a Signal
account and is meant to replace the phone number for contact discovery. In other words, with this
feature you&rsquo;ll no longer have to disclose your phone number in order to start conversations with
others on Signal. Alongside usernames, Signal also launched what they call Phone Number Privacy
(PNP) which basically allows you to hide your phone number from everyone on Signal, including people
you&rsquo;re already talking to (your so called &ldquo;Signal connections&rdquo;). Furthermore, PNP allows you to
prevent your account from being discoverable by your phone number. This addresses a lot of concerns
around anyone being able to probe Signal servers and discover whether a particular phone number is
registered.</p>
<p>This is great! We now have a way to send someone a short chain of characters, a link, or a QR code,
to start a chat on Signal without ever revealing our phone number. But there&rsquo;s something slightly
different about Signal&rsquo;s implementation of usernames that hasn&rsquo;t caught on (not yet at least)
despite their detailed explanation.</p>

<h2 class="relative group">The Land Grab
    <div id="the-land-grab" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-land-grab" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Unlike other popular messaging and social media services, Signal manages usernames quite
differently. In fact, I think they should&rsquo;ve used another name for it altogether. Here&rsquo;s why. While
a username in the social media world refers to a handle that represents the account, and acts as its
sole identifier in every aspect of its interaction with the service, a username in Signal is merely
a reference point to establish first contact. In other words, as soon as you start a conversation
with someone who you found through their username, and they accept it, their username becomes
entirely irrelevant. All you see is their profile name, photo, bio, etc., the same way you did
before the username launch. In addition, usernames can be, and are actually designed to be, readily
changeable, if not ephemeral<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. The intent is to give a means of discovery other than, in fact in
lieu of, the traditional phone number, and that&rsquo;s it. Now the intent is one thing, but what actually
happens is a whole other story.</p>
<p>As soon as the Signal usernames launch hit the news, the land grab began. Everything we just said
about throwaway usernames, only needed for the initial link and all? Take that and throw it out the
window. It seems like very few people actually got it, or cared. For the vast majority, a username
is a username is a username. It&rsquo;s my handle, my identity, my persona. And I need to pick one that
makes sense for me and hold on to it, come hell or high water. People started jumping into the beta
channel just to snag their &ldquo;ideal&rdquo; username. I&rsquo;ve seen some even sacrificing their entire chat
history on the desktop client just to be able to set a username using the beta client. It&rsquo;s almost
humorous, and admittedly quite entertaining to watch all of this unfold. I don&rsquo;t mean to hate on
people rushing to get their hands on a Signal username, like a crowd plowing through a Walmart glass
door to swoop a juicy Black Friday deal. I&rsquo;m one of them! I&rsquo;ve been following the development of
this feature for years, and I know most of the technical details and intricacies. So I&rsquo;m well aware
of the intended use of usernames in Signal. Yet, I still fell for the FOMO.</p>

<h2 class="relative group">Wrap Up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>We&rsquo;ve briefly touched on Signal&rsquo;s new Usernames and Phone Number Privacy features. We saw how
despite Signal&rsquo;s extensive explanation of the intention behind usernames, people, including myself,
couldn&rsquo;t help but let their emotions take over and scramble to score a &ldquo;nice&rdquo; username. Now, I&rsquo;d
love to hear from you. Are you among the I-want-a-shiny-username crew? Or you&rsquo;re part of the
one-username-per-invite gang? Feel free to respond on <a href="https://mastodon.online/@ilyess/112023526393949376"  target="_blank" rel="noreferrer">Mastodon</a>. See you there!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>There is actually no set expiry date on usernames. It&rsquo;s just that you can have a brand new one
every month, or every day, if you so desire.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
<a href="https://ilye.ss/posts/signal-usernames/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>The Four Levels of Mastodon Mastery</title><link>https://ilye.ss/posts/the-four-levels-of-mastodon-mastery/</link><pubDate>Fri, 30 Jun 2023 10:00:00 -0500</pubDate><guid>https://ilye.ss/posts/the-four-levels-of-mastodon-mastery/</guid><description>&lt;p&gt;In the vast landscape of social media platforms, Mastodon has emerged as a refreshing alternative.&#10;With its decentralized nature and commitment to user privacy, Mastodon offers a unique experience&#10;that fosters community building and meaningful interactions. However, mastering Mastodon requires&#10;navigating through different levels of proficiency, especially for someone coming from a&#10;traditional, centralized platform, like Twitter. In this article, we will explore the various stages&#10;of mastery on Mastodon and uncover the possibilities that each level brings.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>In the vast landscape of social media platforms, Mastodon has emerged as a refreshing alternative.
With its decentralized nature and commitment to user privacy, Mastodon offers a unique experience
that fosters community building and meaningful interactions. However, mastering Mastodon requires
navigating through different levels of proficiency, especially for someone coming from a
traditional, centralized platform, like Twitter. In this article, we will explore the various stages
of mastery on Mastodon and uncover the possibilities that each level brings.</p>

<h2 class="relative group">Level 1: Picking a Home
    <div id="level-1-picking-a-home" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#level-1-picking-a-home" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>As a newcomer to Mastodon, the first step is to select a server, also known as an instance. Mastodon
is built upon a federated model, meaning that it consists of multiple interconnected <a href="https://joinmastodon.org/servers"  target="_blank" rel="noreferrer">servers</a> rather
than one centralized platform. Each server has its own community, rules, and moderation policies.
This decentralized structure empowers users with the ability to choose an instance that aligns with
their interests and values.</p>
<p>During this stage, it&rsquo;s essential to research and understand the ethos of different instances. Some
instances cater to specific topics or themes, such as art, technology, or activism, while others
focus on fostering a safe and inclusive environment. By picking the right server, you can easily
connect with like-minded individuals and immerse yourself in communities that resonate with your
passions.</p>
<p>It&rsquo;s worth noting that the decision of picking a server is not permanent or binding. Mastodon allows
users to seamlessly move their account from one server to another. If you find that your chosen
server doesn&rsquo;t meet your expectations or you wish to explore new communities, you can easily migrate
your account to a different server without losing your followers or the people you follow<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. This
flexibility ensures that even if you initially choose a server that doesn&rsquo;t align perfectly with
your preferences, it&rsquo;s not a big deal, and you have the freedom to make adjustments as you explore
the Mastodon ecosystem.</p>
<p>At this point, you have pretty much everything you need to get started on Mastodon. Using the
official website or mobile app, you can set up your profile, post your first toot<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, which I
recommend be an <a href="https://mastodon.social/tags/introduction"  target="_blank" rel="noreferrer">introduction</a> post, and start exploring the platform. However, if you&rsquo;re looking
to take your Mastodon experience to the next level, you can consider other third-party apps that
offer additional features and customization options. We&rsquo;ll explore this in the next section.</p>

<h2 class="relative group">Level 2: Experimenting with different apps
    <div id="level-2-experimenting-with-different-apps" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#level-2-experimenting-with-different-apps" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>There are many <a href="https://joinmastodon.org/apps"  target="_blank" rel="noreferrer">third-party apps</a> available for Mastodon, like <a href="https://tusky.app"  target="_blank" rel="noreferrer">Tusky</a> and Tooot on Android, or <a href="https://github.com/Dimillian/IceCubesApp"  target="_blank" rel="noreferrer">iceCubes</a>
and iMast on iOS, that offer intuitive interfaces and a range of features that could enhance your
experience. Experimenting with different apps allows you to find one that suits your preferences
in terms of user interface, customization options, and additional functionalities.</p>
<p>As you delve into different apps, you&rsquo;ll discover features like customizable timelines, advanced
search options, and the ability to mute or block specific users or keywords. By familiarizing
yourself with these features, you can tailor your Mastodon experience to suit your individual needs
and preferences.</p>
<p>I personally haven&rsquo;t evolved past this level, and I&rsquo;m sure most people won&rsquo;t either. However, if
you&rsquo;re looking to take your Mastodon experience even further and gain more control over moderation
policies, server versions, data, and more; you can consider hosting your own instance which we&rsquo;ll
turn to in the next section.</p>

<h2 class="relative group">Level 3: Hosting Your Own Single-User Instance
    <div id="level-3-hosting-your-own-single-user-instance" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#level-3-hosting-your-own-single-user-instance" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>At this stage, you have the opportunity to take complete control over your Mastodon experience by
hosting your own single-user instance which allows you to personalize your Mastodon environment to
your exact specifications.</p>
<p>By hosting your own instance as a single user, you gain unparalleled freedom and flexibility. You
become the sole administrator of your instance, allowing you to define the rules, moderation
policies, and themes according to your preferences. With this level of control, you can curate a
space that truly reflects your values and interests.</p>
<p>One of the major advantages of hosting your own single-user instance is the ability to customize the
appearance and functionality to suit your needs. You can tailor the instance&rsquo;s design, branding, and
features to align perfectly with your personal style and requirements. Whether it&rsquo;s the color
scheme, layout, or additional functionalities, you have the power to shape your Mastodon experience
exactly the way you envision it.</p>
<p>Hosting your own instance also eliminates any concerns about relying on external servers. You have
full autonomy over the server maintenance, ensuring optimal performance, security, and data privacy.
It goes without saying that hosting your own instance does require technical knowledge and
resources, as you will be responsible for server setup, maintenance, and regular updates.</p>
<p>It&rsquo;s worth clarifying that being the sole user of your instance doesn&rsquo;t prevent you from engaging
with others, and building your online presence. While your instance doesn&rsquo;t aim to build a
community, you can still connect with other users as if you were all on the same server.</p>
<p>Now, you don&rsquo;t have to stop here. While hosting your own single-user instance is more than what
most people will ever need, you can take it a step further and open your instance for registration
to welcome other users. We&rsquo;ll explore this in the next section.</p>

<h2 class="relative group">Level 4: Fostering a Community
    <div id="level-4-fostering-a-community" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#level-4-fostering-a-community" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>As you continue to explore Mastodon and expand your reach, you may consider opening registration for
users on your instance, with the intention of fostering a community around shared interests and
values.</p>
<p>By opening registration, you provide an opportunity for people who resonate with your content or the
community you aim to build to join your instance and participate. This step allows you to create a
space where individuals with similar passions can connect, collaborate, and engage in meaningful
discussions.</p>
<p>As the administrator of your instance, you have the power to shape the community by defining the
rules, themes, and moderation policies. Encourage respectful and constructive interactions, foster a
supportive environment, and create spaces for people to share their thoughts, ideas, and creations.</p>
<p>Additionally, by welcoming others to your instance, you not only create a space for like-minded
individuals to connect but also contribute to the decentralization and scalability of the Mastodon
network. In a decentralized ecosystem like Mastodon, distributing the user load across multiple
instances is crucial to ensure stability, performance, and avoid instances becoming overcrowded.
Opening your instance to newcomers allows for a healthier distribution of users across the network,
preventing the concentration of a large user base on a few instances. This decentralization is key
to maintaining a vibrant and sustainable Mastodon community.</p>

<h2 class="relative group">Wrap Up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Mastering Mastodon is an exciting journey that unfolds across different stages of proficiency. From
selecting the right server to exploring various apps, hosting your own single-user instance, and
potentially fostering a community, each level offers unique opportunities to shape your Mastodon
experience. Embrace the flexibility, connect with others, and discover the diverse communities
within Mastodon. Whether you&rsquo;re seeking an alternative to traditional social media or championing
privacy and inclusivity, Mastodon has something to offer at every step of your personal mastery. So,
embark on this journey, make meaningful connections, and let Mastodon become your digital haven.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Note that posts, on the other hand, are not transferable between servers. As it stands today,
if you move your account to a new server, your posts will not be migrated.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>A toot is the Mastodon equivalent of a tweet on Twitter or a post on Facebook.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
<a href="https://ilye.ss/posts/the-four-levels-of-mastodon-mastery/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>Your Blog Does Not Need a Database</title><link>https://ilye.ss/posts/your-blog-does-not-need-a-database/</link><pubDate>Tue, 14 Feb 2023 20:00:00 -0400</pubDate><guid>https://ilye.ss/posts/your-blog-does-not-need-a-database/</guid><description>&lt;p&gt;In the present digital epoch, it has become imperative for companies with an online presence to host&#10;a blog which is nothing more than a compendium of articles or posts, typically organized&#10;chronologically. This is not limited to companies obviously and anyone can, and is even highly&#10;encouraged to, run a personal blog. If you&amp;rsquo;re one of the fortunate who own a personal blog and are&#10;currently relying on a database to power it, you may be under the impression that it&amp;rsquo;s necessary for&#10;your blog to function properly. Wrong! In this article I&amp;rsquo;m going to explain why that&amp;rsquo;s not the case&#10;and why your blog doesn&amp;rsquo;t actually need a database. I will even go further and suggest that your&#10;blog &lt;em&gt;should not&lt;/em&gt; use a database. While databases can be useful, if not mandatory, for certain types&#10;of websites, blogs are definitely not one of them.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>In the present digital epoch, it has become imperative for companies with an online presence to host
a blog which is nothing more than a compendium of articles or posts, typically organized
chronologically. This is not limited to companies obviously and anyone can, and is even highly
encouraged to, run a personal blog. If you&rsquo;re one of the fortunate who own a personal blog and are
currently relying on a database to power it, you may be under the impression that it&rsquo;s necessary for
your blog to function properly. Wrong! In this article I&rsquo;m going to explain why that&rsquo;s not the case
and why your blog doesn&rsquo;t actually need a database. I will even go further and suggest that your
blog <em>should not</em> use a database. While databases can be useful, if not mandatory, for certain types
of websites, blogs are definitely not one of them.</p>
<p>One of the main reasons why blogs don&rsquo;t need a database is that they are typically static websites.
This means that the content on the website does not change frequently and isn&rsquo;t customized for any
particular visitor, and the website does not require any user interaction. In such a case, a
database would be unnecessary as the information on the website can be stored in the form of HTML
and CSS files.</p>
<p>Before going further, if your website has any dynamic functionality like user
registration and login, or first party comment support, you&rsquo;re out of luck and there&rsquo;s no way around
using a database; unless you&rsquo;re feeling adventurous enough to delegate those functionalities to
third party providers and hook your website onto them solely through the frontend. This adds a bit
of complexity and calls for some technical knowledge though, which voids some of the benefits of
static websites. Maybe you don&rsquo;t <em>need</em> those dynamic functionalities to begin with and might be
better off without them. Food for thought.</p>

<h2 class="relative group">The Old School
    <div id="the-old-school" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-old-school" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The traditional method of running a blog is by utilizing a database to store the articles and a
server-side script that dynamically generates the pages upon a user&rsquo;s request. This method, commonly
referred to as a &ldquo;dynamic&rdquo; approach, has been the prevalent norm for a considerable duration.
However, this has several drawbacks.</p>
<p>First, the energy consumption is a paramount concern. Databases necessitate a substantial
computational capacity, and oftentimes, they are run on servers that consume copious amounts of
energy. This energy consumption leads to a substantial carbon footprint, thereby exacerbating
climate change. Furthermore, the servers that host these databases are usually situated in data
centers, which consume substantial amounts of energy to cool the servers and keep them operational.
In addition, databases are frequently over-provisioned and underutilized, resulting in the squander
of resources. This is a direct result of the fact that a database must be able to handle the peak
load, even though the actual load is frequently much lower. This is a significant waste of
resources, both in terms of energy and hardware.</p>
<p>Another drawback of using a database is the cost. Hosting a dynamic blog requires a powerful server,
as well as a database. This can be expensive, especially for personal blogs which are not typically
meant to generate revenue. Additionally, the more traffic a blog receives, the more powerful the
server must be, and the more resources are required to handle the traffic. This can make it
difficult for a blog to scale, and can also be a significant barrier to entry for those who are just
starting out and do not have a lot of resources.</p>
<p>The third drawback of using a database is the slower page load times. When a user requests a page on
a dynamic blog, the server must first query the database to retrieve the data, and then execute the
server-side scripts to generate the page. This process takes time, and the more traffic a blog
receives, the longer it takes to generate the pages. This can lead to a poor user experience, and
can also negatively impact the blog&rsquo;s search engine rankings.</p>

<h2 class="relative group">The Hot New Thing
    <div id="the-hot-new-thing" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-hot-new-thing" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>An alternative approach is to use a statically generated blog, which pre-generates all the pages and
serves them as plain HTML and CSS files. This approach has several advantages over using a database
besides the obvious benefits of not carrying the aforementioned drawbacks.</p>
<p>First, since the pages are pre-generated, they can be served directly by a web server without the
need for any dynamic processing. This means that the hosting costs are lower, as you don&rsquo;t need a
powerful server or a database. Additionally, the static pages can be served directly by a Content
Delivery Network (CDN), which can further reduce the hosting costs, and are often cached by the
browser which also improves the page load times.</p>
<p>Another advantage of using a statically generated blog is the ability to generate pages offline.
With a dynamic blog, the pages are generated on the fly, which means that the server must be online
and accessible in order for the website to function. With a statically generated blog, however, the
pages can be generated offline, and then uploaded to the web server. This can be useful for testing
and brings a high degree of portability. Given that the website is nothing but static files, you can
almost instantly move your website from one hosting provider to another.</p>
<p>The third advantage of using a statically generated blog is the increased security. With a dynamic
blog, there is always a risk that the server-side scripts or the database could be hacked, which
could potentially lead to sensitive information being compromised. With a statically generated blog,
on the other hand, there is no server-side script or database to hack, which reduces the risk of a
security breach.</p>

<h2 class="relative group">I Don&rsquo;t Want To Deal With HTML And CSS
    <div id="i-dont-want-to-deal-with-html-and-css" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#i-dont-want-to-deal-with-html-and-css" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If the thought of having to manually create and manipulate HTML and CSS files makes you sweat,
don&rsquo;t you worry. Using a statically generated website doesn&rsquo;t mean that you need to become an
proficient frontend developer. There are tools, so called static site generators (SSG), that allow
you to create your blog using simple text files, such as Markdown, and then generate the necessary
HTML and CSS files. This means that you can exclusively focus on creating content and customizing
the design of your blog, by leveraging off-the-shelf themes.</p>
<p>One of the most popular SSG is <a href="https://jekyllrb.com"  target="_blank" rel="noreferrer">Jekyll</a>, which is written in Ruby. Jekyll takes your content, written
in Markdown or Textile, and uses layouts to create a static website. Jekyll also supports data
files, which can be used to store data in a structured format, such as YAML or JSON, that can be
accessed in your templates.</p>
<p>Another popular SSG is <a href="https://gohugo.io"  target="_blank" rel="noreferrer">Hugo</a>, which is written in Go. It is a fast and flexible tool that can be used
to build a wide variety of websites, including blogs. Hugo uses markdown files to generate your
website, and it supports data files and template variables, which can be used to store and access
data in a structured format.</p>
<p>It goes without saying that there are many more static site generators to choose from. Once the
static website files are generated, they need to be hosted by a web hosting provider for the website
to go online and be accessible from the internet. Here again, there&rsquo;s no shortage of web hosting
providers on the market. In fact, many would even be happy to host your static website for free!
Providers like <a href="https://www.netlify.com"  target="_blank" rel="noreferrer">Netlify</a>, <a href="https://codeberg.page/"  target="_blank" rel="noreferrer">Codeberg Pages</a>, <a href="https://pages.github.com/"  target="_blank" rel="noreferrer">Github Pages</a>, and <a href="https://vercel.com/"  target="_blank" rel="noreferrer">Vercel</a> immediately come to mind but I&rsquo;m
sure there are plenty others.</p>

<h2 class="relative group">Wrap up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>In today&rsquo;s world, environmental sustainability is a top priority. And when it comes to blogs,
statically generated sites are the clear winner. They require less energy and computing power, are
much more secure than their dynamic counterparts, and are much easier to maintain. While I doubt
that we can solve all our climate problems by making blogs static, I do believe that every bit of
contribution towards that goal helps, however minute it seems. So if you&rsquo;re looking to reduce your
environmental impact and create a secure, cost-effective blog, be sure to go static!</p>
<a href="https://ilye.ss/posts/your-blog-does-not-need-a-database/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>My Journey With Note Apps</title><link>https://ilye.ss/posts/my-journey-with-note-apps/</link><pubDate>Thu, 19 Jan 2023 20:00:00 -0400</pubDate><guid>https://ilye.ss/posts/my-journey-with-note-apps/</guid><description>&lt;p&gt;Everybody needs to take notes, be it for school, to have a handy grocery list in the store, or&#10;to keep a log of business expenses for accounting purposes. Some people use physical notebooks, some&#10;pick up whatever napkin happens to be in the immediate vicinity and roll with it for the day, and&#10;others, like myself, prefer to leverage digital notes. In this article, I&amp;rsquo;ll take you through my&#10;journey with note taking apps. We&amp;rsquo;ll go over my personal definition of a &amp;ldquo;good&amp;rdquo; note taking app,&#10;explore some of the options I used and the challenges I faced, and finally land on the solution I&#10;ended up adopting with a couple of learnings that emerged along the way.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>Everybody needs to take notes, be it for school, to have a handy grocery list in the store, or
to keep a log of business expenses for accounting purposes. Some people use physical notebooks, some
pick up whatever napkin happens to be in the immediate vicinity and roll with it for the day, and
others, like myself, prefer to leverage digital notes. In this article, I&rsquo;ll take you through my
journey with note taking apps. We&rsquo;ll go over my personal definition of a &ldquo;good&rdquo; note taking app,
explore some of the options I used and the challenges I faced, and finally land on the solution I
ended up adopting with a couple of learnings that emerged along the way.</p>

<h2 class="relative group">The Beginning
    <div id="the-beginning" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-beginning" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>My journey with digital note taking started with whatever default notes app happened to be on my
phone. At first, I didn&rsquo;t have any particular framework for note taking; it was just a random
collection of digital post-it stickers. Similar story on the desktop, a structureless heterogeneous
cluster of text files scattered all over the place.</p>
<p>Then, I started taking notes more seriously (pun intended) to capture knowledge, such as book
summaries and highlights, things I learn from videos and documentaries, new vocabulary I come
across, random ideas or questions I want to get answers to, and more. Before you know it, my notes
catalogue started getting a bit heavy and it was apparent that I needed some sort of framework to
better tag and organize all that knowledge. At the time, <a href="https://evernote.com"  target="_blank" rel="noreferrer">Evernote</a> was the new kid on the block and
it had some attractive features, so without putting much thought into it, I created an account and
poured all my notes over to it.</p>
<p>I used Evernote for a long time, longer than I would want to admit. But one day, shortly after I&rsquo;d
begun getting serious about my online privacy, I had a &ldquo;Oh shit!&rdquo; moment that I still remember to
this day.</p>

<h2 class="relative group">The Rabbit Hole
    <div id="the-rabbit-hole" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-rabbit-hole" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I geared up and I ventured down the rabbit hole of privacy-friendly, and open-source, note taking
apps. I set a few criteria to guide my expedition:</p>
<ol>
<li>The service has to be free of charge, or offer a free plan;</li>
<li>It has to be offline and/or support end-to-end encrypted backup and/or sync; and</li>
<li>It must respect user privacy, so no tracking, ads, or telemetry.</li>
</ol>
<p>If there&rsquo;s one thing I would never accept, it would be having my notes up in the cloud in the
clear<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, may that be temporarily for synchronization or permanently for backup. My notes hold so
much information about me, from future plans, to areas of interest in life, to just general
knowledge. I bet that even my IQ could be deduced with a reasonable degree of certainty using the
right AI model. Now that I think of it, I would definitely see myself in the future training a model
on my notes and delegating some decision making to it: a tailored virtual assistant of sort. I&rsquo;m no
AI expert and this does seem far-fetched today, but I wouldn&rsquo;t be surprised if it were a thing even
half a decade from now. Anyway, I digress.</p>
<p>During my research, <a href="https://standardnotes.com"  target="_blank" rel="noreferrer">StandardNotes</a> stood out as a good candidate with its end-to-end encrypted backup
and sync, and focus on privacy. It seemed to check all the boxes, so I gave it a shot. I created a
free account and, once again, migrated all my notes off of Evernote into my StandardNotes account. I
have to admit that this immediately felt like a downgrade; the feature set of StandardNotes&rsquo; free
tier is rudimentary at best. But in the name of privacy, I sucked it up and worked around the
limitations for a while. Thinking back, I realize that this was more of a &ldquo;stop the bleeding&rdquo; step
than a permanent solution. Case in point, I never actually stopped exploring the free and open
source note taking space for better options. Then, I came across <a href="https://joplinapp.org"  target="_blank" rel="noreferrer">Joplin</a>.</p>
<p>At first, Joplin&rsquo;s UI/UX looked out-dated, especially on mobile devices, but I was willing to be
more lenient given that it&rsquo;s a free and open source software maintained by a handful of generous
people. What grabbed my attention was its self-hostable end-to-end encrypted capability. So I
downloaded a copy of the app, set up a WebDAV server to handle the sync, and migrated all my notes
over. After a few weeks of me getting used to its archaic UI, Joplin began setting itself as my
permanent note taking solution. I started tinkering with various plugins, and even looked into
making my own. All was great until the day the sync broke! My client was getting 503 errors from the
sync server which corrupted some notes and rendered them unusable. Luckily, I had an offline backup
of the affected files, so I managed to recover, albeit laboriously, from this crash. A few weeks
later, another sync crash wiped my <em>entire</em> notebook. I wasn&rsquo;t so lucky this time around, however,
and ended up permanently losing some of my newer notes. This was the straw that broke the camel&rsquo;s
back. I couldn&rsquo;t take it anymore; I had to find a better solution.</p>
<p>I decided to give StandardNotes another shot, but going with a self-hosted instance this time. This
will ensure that I have full control over my notes&rsquo; backups, and that I enjoy a more decent set of
features; or so I thought. So I migrated all my notes, once more, to my <a href="/posts/self-host-standard-notes-with-premium-extensions/" >StandardNotes instance</a> and
started playing around with premium themes and extensions. The fact that a simple notes service
required so many backend services, a.k.a micro-services, has never really sat well with me. I had
the feeling that this level of complexity will eventually prove too cumbersome to maintain. And
guess what? It did! After a recent update of the desktop client, my setup was brought to a halt. I
was no longer able to connect to my self-hosted instance, running an old version of the server. It
seemed like the new client version wasn&rsquo;t backward-compatible and required a backend upgrade. When I
tried to make that happen, I realized that the StandardNotes&rsquo; team overhauled their entire
architecture. At this point, I wasn&rsquo;t really down to learn the new setup, build new docker images,
and get everything back online. It felt like a lot more work than a simple notes service should call
for.</p>

<h2 class="relative group">Refocusing
    <div id="refocusing" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#refocusing" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>I took a step back, and went back to the drawing board. A quick cost/reward analysis confirmed that
all of this friction wasn&rsquo;t worth it. I realized that I didn&rsquo;t <em>really</em> need the cross-device sync,
since the vast majority of my notes fell under 2 categories: desktop, and phone. I seldom need notes
from my phone when I&rsquo;m on the desktop, and I almost never need notes from my desktop when I&rsquo;m on my
phone. So I decided to use 2 different note taking applications: <a href="https://www.vim.org"  target="_blank" rel="noreferrer">vim</a> with <a href="https://vimwiki.github.io"  target="_blank" rel="noreferrer">vimwiki</a> for desktop notes,
and my phone&rsquo;s default notes app with cloud backup disabled. I suppose you know the drill by now: I
migrated all of my notes, yes once again, from StandardNotes to vim and my phone&rsquo;s default note app,
hoping that this was the last time I perform this dance.</p>
<p>I&rsquo;ve been driving this setup for weeks now and so far have a couple of takeaways. First, I&rsquo;m struck
by how pleasant it is to enter a typo&rsquo;ed keyword into a search bar and watch the powerful fuzzy
search engine instantly populate the page with extremely relevant results, the first one being the
one I had in mind most of the time. I can&rsquo;t remember a single time where this failed me. This made
me realize just how mediocre, if not completely futile, StandardNotes&rsquo; search functionality was,
particularly on mobile. Second, I never <em>actually</em> needed the cross-device sync or cloud backup for
my notes. Offline backup, and segregated notebooks work just fine, check all the boxes with regards
to privacy, and cost me practically no effort.</p>
<p>Before closing this off, I&rsquo;d like to mention a couple other note services that I considered at some
point, but wasn&rsquo;t entirely sold on so I never took them for a test drive. <a href="https://obsidian.md"  target="_blank" rel="noreferrer">Obsidian</a> is more than just
a note taking app. With its graph features and automatic note linking, it appears to be more of a
knowledge management software; way more than I actually need for my personal use. Plus, the sync is
a paid service with no option to self-host. <a href="https://logseq.com"  target="_blank" rel="noreferrer">Logseq</a> is another solution that briefly caught my
attention until I learned that it had no native sync support; the only way to sync data was to go
through Github or iCloud.</p>

<h2 class="relative group">Wrap up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If there&rsquo;s a lesson to be drawn from this journey, it has to be that the most complex solution isn&rsquo;t
always the right one. I would argue that the simplest one turns out to be the best, more often than
not. It just took me multiple migrations, hours if not days of tinkering around with various setups,
and quite a bit of frustration wrestling with poor-UX software and dealing with lousy error
handling, to realize it.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I consider &ldquo;in the clear&rdquo; data any encrypted data where the keys belong to or are managed by
the server. Put differently, in this context, if it&rsquo;s not end-to-end encrypted, it&rsquo;s in the clear.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
<a href="https://ilye.ss/posts/my-journey-with-note-apps/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>Self Host Standard Notes With Premium Extensions</title><link>https://ilye.ss/posts/self-host-standard-notes-with-premium-extensions/</link><pubDate>Sat, 18 Jun 2022 19:30:00 -0400</pubDate><guid>https://ilye.ss/posts/self-host-standard-notes-with-premium-extensions/</guid><description>&lt;p&gt;&lt;a href="https://standardnotes.com" target="_blank" rel="noreferrer"&gt;Standard Notes&lt;/a&gt; is an open-source note taking application available on desktop&#10;and mobile platforms that offers end-to-end encrypted synchronization. In other&#10;words, all data are encrypted on device before they&amp;rsquo;re sent to the server to&#10;propagate to other devices. I think it&amp;rsquo;s one of the best open-source note taking&#10;products out there with a free plan, at the time of this writing.&lt;/p&gt;&#10;&lt;p&gt;That being said, their introductory free tier leaves a lot to be desired. It&#10;could work for users with basic note taking needs but if you want to organize&#10;your notes in nested folders, use multiple editors like Markdown and Rich text,&#10;switch between a variety of themes, or schedule regular backups, you have to go&#10;with a premium plan. At $12/month, or $5/month if billed yearly,&#10;&lt;a href="https://standardnotes.com/plans" target="_blank" rel="noreferrer"&gt;their cheapest plan&lt;/a&gt; is definitely at the higher end of the spectrum, especially&#10;that the service isn&amp;rsquo;t complex enough to warrant the hefty subscription fee in&#10;my view. It should come at no surprise if a Standard Notes subscription is&#10;considered a quite hard expense to justify by regular users who don&amp;rsquo;t fiddle&#10;with notes on a daily basis, or heavily rely on note taking in a professional&#10;capacity.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p><a href="https://standardnotes.com"  target="_blank" rel="noreferrer">Standard Notes</a> is an open-source note taking application available on desktop
and mobile platforms that offers end-to-end encrypted synchronization. In other
words, all data are encrypted on device before they&rsquo;re sent to the server to
propagate to other devices. I think it&rsquo;s one of the best open-source note taking
products out there with a free plan, at the time of this writing.</p>
<p>That being said, their introductory free tier leaves a lot to be desired. It
could work for users with basic note taking needs but if you want to organize
your notes in nested folders, use multiple editors like Markdown and Rich text,
switch between a variety of themes, or schedule regular backups, you have to go
with a premium plan. At $12/month, or $5/month if billed yearly,
<a href="https://standardnotes.com/plans"  target="_blank" rel="noreferrer">their cheapest plan</a> is definitely at the higher end of the spectrum, especially
that the service isn&rsquo;t complex enough to warrant the hefty subscription fee in
my view. It should come at no surprise if a Standard Notes subscription is
considered a quite hard expense to justify by regular users who don&rsquo;t fiddle
with notes on a daily basis, or heavily rely on note taking in a professional
capacity.</p>
<p>Fortunately, the Standard Notes team is nice enough to open source all their
stack for anyone to self-host for personal use. On top of the obvious benefit of
enjoying all the premium features free of charge, self-hosting comes with the
advantage of full data control. The official documentation around self-hosting
published by the Standard Notes team is clear and straightforward. They put
together a docker-compose file that defines the entire stack along with a
convenient script to create configuration files, bring the stack up and down,
view logs, and more.</p>
<p>Thanks to the power of containers, you can be up and running in a matter of
minutes, unless you&rsquo;re running an ARM chip like a Raspberry Pi. Sadly, Standard
Notes don&rsquo;t provide official Docker images for ARM processors. You can either
pick one of the third-party images made available by random users, or build your
own. I tend to go with the latter option because it grants more control over the
software version packed in the image, and most importantly it obviates the
inherent security risk that comes with running untrusted Docker images<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. In
the next section, we&rsquo;ll go over the steps to build Docker images from source
compatible with ARM processors, or any target architecture for that matter.  If
the official Docker images work on your processor architectures, feel free to
jump straight to the <a href="/posts/self-host-standard-notes-with-premium-extensions/#premium-extensions" >&ldquo;Premium extensions&rdquo;</a> section.</p>

<h2 class="relative group">Building Docker images for ARM
    <div id="building-docker-images-for-arm" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#building-docker-images-for-arm" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>First, we need to pull the code of the service we want to build a Docker image
for. Let&rsquo;s go with the <code>auth</code> service.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>git clone https://github.com/standardnotes/auth</span></span></code></pre></div></div>
<p>Then, from within the <code>auth</code> folder, let&rsquo;s switch to the latest tag, or any other
target version you want to use:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>cd auth
</span></span><span style="display:flex;"><span>git checkout 1.46.1</span></span></code></pre></div></div>
<p>If you&rsquo;re not sure what&rsquo;s the latest tag, you can either check it directly on
Github or list all available tags using:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>git tag --list</span></span></code></pre></div></div>
<p>If you&rsquo;re building the image on the same machine that&rsquo;s going to ultimately run
the container, all you have to do is run a typical Docker build command like:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>docker build -t arm-auth:1.46.1 .</span></span></code></pre></div></div>
<p>However, if you&rsquo;re building the image on a separate machine, perhaps
because your server doesn&rsquo;t have enough resources to carry out the build
process, you can target a specific CPU architecture using <code>buildx</code>. Here&rsquo;s what
the command would look like:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>docker buildx build --platform linux/arm/v6 -t arm-auth:1.46.1 . --load</span></span></code></pre></div></div>
<p>Here, we&rsquo;re targeting the version 6 of ARM and tagging the image with
<code>arm-auth:1.46.1</code>. If you&rsquo;re running a different version of ARM make sure to
update the <code>--platform</code> flag accordingly.</p>
<p>As a side note, this will only work if <code>linux/arm/v6</code> is loaded in the current
<code>buildx</code> builder. To check the platforms supported by your <code>buildx</code> builder,
run:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>docker buildx ls</span></span></code></pre></div></div>
<p>If your target platform is not currently loaded in <code>buildx</code>, try running a
<a href="https://hub.docker.com/r/linuxkit/binfmt/"  target="_blank" rel="noreferrer">binfmt</a> container like the following:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>docker run --privileged --rm linuxkit/binfmt:a17941b47f5cb262638cfb49ffc59ac5ac2bf334</span></span></code></pre></div></div>
<p>Now that the image is built, we need to move it to the server where it&rsquo;s
supposed to run. To do so, we first need to save it in a <code>tar</code> archive using the
following command:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>docker save arm-auth:1.46.1 -o arm-auth.tar</span></span></code></pre></div></div>
<p>Then, after transferring it to the server via <code>rsync</code>, a USB key, or whatever
other means deemed most convenient, we should load it on the server using:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>docker load -i arm-auth.tar</span></span></code></pre></div></div>
<p>Now, if we list all Docker images using <code>docker images</code> we should see our new
<code>arm-auth</code> image. All that&rsquo;s left to do is reference this image inside the
standalone <a href="https://github.com/standardnotes/standalone/blob/main/docker-compose.yml"  target="_blank" rel="noreferrer">docker-compose file</a> provided by Standard Notes.</p>
<p>It goes without saying that all the steps we&rsquo;ve gone through in this section
would need to be repeated for each one of the official Standard Notes services,
which currently are:</p>
<ul>
<li><a href="https://github.com/standardnotes/syncing-server-js"  target="_blank" rel="noreferrer">syncing-server-js</a>;</li>
<li><a href="https://github.com/standardnotes/api-gateway"  target="_blank" rel="noreferrer">api-gateway</a>; and</li>
<li><a href="https://github.com/standardnotes/files"  target="_blank" rel="noreferrer">files</a>.</li>
</ul>
<p>On top of their own services, Standard Notes use Redis for cache and MySQL for
storage. The latter can be replaced with a MariaDB ARM image like
<a href="https://hub.docker.com/r/linuxserver/mariadb"  target="_blank" rel="noreferrer">linuxserver/mariadb</a> while the former can be used as is; it already has a Docker
image for ARM.</p>

<h2 class="relative group">Premium extensions
    <div id="premium-extensions" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#premium-extensions" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Once your self-hosted Standard Notes instance is up, you can connect to it by
pointing your Standard Notes client to your instance, under the &ldquo;Advanced&rdquo;
section of the account creation or login view. You first need to create an
account, which will be a &ldquo;Basic&rdquo; one by default, equivalent to the free tier. To
be able to use premium themes and editors you need to upgrade your account with
<a href="https://docs.standardnotes.com/self-hosting/subscriptions"  target="_blank" rel="noreferrer">a couple of commands</a>. Unfortunately, having a premium account in your
self-hosted instance doesn&rsquo;t automatically give you access to premium themes and
editors.  Standard Notes don&rsquo;t give out their official premium extensions for
free but you can load other community-built alternatives. Here are a couple of
sources to get you started:</p>
<ul>
<li><a href="https://snexts.github.io/"  target="_blank" rel="noreferrer">https://snexts.github.io/</a></li>
<li><a href="https://github.com/jonhadfield/awesome-standard-notes"  target="_blank" rel="noreferrer">https://github.com/jonhadfield/awesome-standard-notes</a></li>
</ul>
<p>To load an extension, head over to your desktop client under Preferences &gt;
General &gt; Advanced Settings. Then, enter the extension URL in the &ldquo;Install
Custom Extension&rdquo; input field and hit &ldquo;Install&rdquo;.</p>

<h2 class="relative group">Wrap up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Self-hosting Standard Notes is strikingly straightforward, barring few extra
steps that require some Docker knowledge when working with an ARM chip. In this
article, we&rsquo;ve gone over everything needed to setup a Standard Notes server
that&rsquo;s not covered in the official documentation.</p>
<p>If you have any questions or face difficulties with the instructions laid out
above, feel free to reach out on <a href="https://mastodon.online/@ilyess"  target="_blank" rel="noreferrer">Mastodon</a>.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>The Docker image could fall out of date and lack crucial security patches;
or, more unlikely although not totally impossible, contain malware.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
<a href="https://ilye.ss/posts/self-host-standard-notes-with-premium-extensions/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>Why I Love Firefox</title><link>https://ilye.ss/posts/why-i-love-firefox/</link><pubDate>Sun, 22 May 2022 20:00:00 -0400</pubDate><guid>https://ilye.ss/posts/why-i-love-firefox/</guid><description>&lt;p&gt;Long gone are the days when websites consisted of static HTML pages styled with&#10;basic CSS, when links actually directed users to new web locations instead of&#10;calling a Javascript function. Now, websites (or should I call them web apps?)&#10;have grown so complex that browsers had no choice but to turn into operating&#10;systems. This made for a much richer user experience with so many possibilities;&#10;you can do online banking, play video games, join video conferences, shop, and&#10;even install apps, all within the same application: the browser. It&amp;rsquo;s obvious&#10;that the more complex any software gets, the more chances there are for security&#10;vulnerabilities to emerge, and the browser is no exception.&#10;&lt;a href="https://www.cvedetails.com/vulnerability-list.php?vendor_id=1224&amp;amp;product_id=15031&amp;amp;page=1&amp;amp;year=2021" target="_blank" rel="noreferrer"&gt;The surge of browser CVEs&lt;/a&gt; we&amp;rsquo;ve witnessed in recent years is a good testament&#10;to that. Additionally, the more user interactions are made possible in the&#10;browser the more attractive it becomes to marketers and companies looking for&#10;new revenue streams. Online advertising, which is fueled by online tracking,&#10;has become so lucrative that it&amp;rsquo;s consistently been &lt;a href="https://www.statista.com/statistics/1093781/distribution-of-googles-revenues-by-segment/" target="_blank" rel="noreferrer"&gt;the top revenue category&lt;/a&gt;&#10;for so many big tech corporations. So, should we just throw our hands up in the&#10;air and accept our imposed destiny? Do we have to give up our privacy if we&amp;rsquo;re&#10;to do any meaningful browsing on the internet?&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>Long gone are the days when websites consisted of static HTML pages styled with
basic CSS, when links actually directed users to new web locations instead of
calling a Javascript function. Now, websites (or should I call them web apps?)
have grown so complex that browsers had no choice but to turn into operating
systems. This made for a much richer user experience with so many possibilities;
you can do online banking, play video games, join video conferences, shop, and
even install apps, all within the same application: the browser. It&rsquo;s obvious
that the more complex any software gets, the more chances there are for security
vulnerabilities to emerge, and the browser is no exception.
<a href="https://www.cvedetails.com/vulnerability-list.php?vendor_id=1224&amp;product_id=15031&amp;page=1&amp;year=2021"  target="_blank" rel="noreferrer">The surge of browser CVEs</a> we&rsquo;ve witnessed in recent years is a good testament
to that. Additionally, the more user interactions are made possible in the
browser the more attractive it becomes to marketers and companies looking for
new revenue streams. Online advertising, which is fueled by online tracking,
has become so lucrative that it&rsquo;s consistently been <a href="https://www.statista.com/statistics/1093781/distribution-of-googles-revenues-by-segment/"  target="_blank" rel="noreferrer">the top revenue category</a>
for so many big tech corporations. So, should we just throw our hands up in the
air and accept our imposed destiny? Do we have to give up our privacy if we&rsquo;re
to do any meaningful browsing on the internet?</p>
<p>Using a sound browser will go a long way in mitigating the risk of privacy
violations. Like anything else in life, it&rsquo;s important to use the right tool for
the job. If you&rsquo;re going on a road trip across the country, you&rsquo;d want to drive
the right vehicle, have spare tires, make sure the engine is healthy and won&rsquo;t
overheat, plan charging breaks if you&rsquo;re going electric, and so on.  The same
applies to browsing the internet. In this article, I&rsquo;ll go over different
security and privacy measures implemented in <a href="https://firefox.com"  target="_blank" rel="noreferrer">Firefox</a>, my favourite browser.</p>
<p>Before we dive in, let me note that my decision to run Firefox as my daily
driver is not a political one or based on Mozilla&rsquo;s management, work conditions,
or product line. I&rsquo;m only focused on the software itself and how it serves my
personal internet browsing needs.</p>

<h2 class="relative group">HTTP header sanitization
    <div id="http-header-sanitization" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#http-header-sanitization" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>In this day and age, web pages have become remarkably complex to the point where
it&rsquo;s quite rare to find web services that don&rsquo;t pull resources from other
domains, may they be fonts, images, videos, scripts, or other types of
resources.  When a browser goes out to fetch external resources, it attaches the
original URL to every request in the <a href="https://en.wikipedia.org/wiki/HTTP_referer"  target="_blank" rel="noreferrer">HTTP Referer header</a>. The entire URL that
the user had initially put in their browser address bar ends up transmitted to
all the domains that the page being visited depends on. To give you an idea, if
a user was reading an article on a news site in dark mode with big font (URL:
<a href="https://www.allthenews.com/breaking-news-read-now?mode=dark&amp;font-size=big%29;"  target="_blank" rel="noreferrer">https://www.allthenews.com/breaking-news-read-now?mode=dark&font-size=big);</a> and this site loaded &ldquo;share&rdquo;
buttons from Google, Twitter, and Facebook; all of these companies would be made
aware of the article the user was reading <em>and</em> the preferred settings for
article consumption.</p>
<p>Firefox <a href="https://blog.mozilla.org/security/2021/03/22/firefox-87-trims-http-referrers-by-default-to-protect-user-privacy/"  target="_blank" rel="noreferrer">trims the path and query parameters from the HTTP Referer header</a> for all
cross-origin requests by default. So in our previous example the URL would be
turned into <a href="https://www.allthenews.com/"  target="_blank" rel="noreferrer">https://www.allthenews.com/</a> when communicated as a referrer to
Google, Twitter, and Facebook, thereby reducing the amount of leaked user
information.</p>

<h2 class="relative group">Cookie store isolation
    <div id="cookie-store-isolation" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#cookie-store-isolation" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>When it comes to cookie management, Firefox has really stepped up their game. I
don&rsquo;t think any other browser handles cookies nearly as well.  Firefox not only
blocks cookies from domains that were identified as trackers, it totally
cripples cookie-based tracking with its Total Cookie Protection. It works by
operating completely isolated cookie jars, each dedicated to a website that the
user explicitly visited. By way of illustration, cookies set when the user
visits website A are only ever attached to requests triggered by user actions on
website A, including third-party cookies. This is exceptionally powerful because it
means that a third-party cookie coming from Facebook, for instance, while
browsing a news site will never make it back to Facebook when browsing any other
website that uses Facebook&rsquo;s assets.</p>
<p>In addition, Firefox&rsquo;s Enhanced Cookie Clearing (ECC) allows you to wipe out all
the data that were created by a specific website. This includes first and third
party cookies, local storage data, settings, and cache. This is different than
the typical &ldquo;delete website cookies&rdquo; setting you&rsquo;ll find in other mainstream
browsers in that ECC not only deletes data that belong to the selected website,
but also all other data that were saved in your browser while visiting that
website. Let&rsquo;s say you&rsquo;re on recipes.com that pulls in some assets
from google.com, while also being directly logged into google.com in a separate
tab. Clearing cookies of recipes.com using ECC will have the following effects:</p>
<ul>
<li>Delete all the data created by recipes.com</li>
<li>Delete all the data created by google.com <em>while browsing recipes.com</em>. This
means that we&rsquo;ll remain logged into google.com in the second tab. This is
extremely potent as it grants you the ability to make your browser
effectively forget about all your activity on a given website.</li>
</ul>

<h2 class="relative group">Process isolation
    <div id="process-isolation" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#process-isolation" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Every time you visit a website with Javascript enabled, you run code that you&rsquo;ve
most likely never seen on your computer<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. The vast majority of browsers do a
great job containing this code in a tight sandbox to prevent malicious
out-of-scope access, but not all enforce good segregation within the sandbox.
Firefox goes to great lengths to isolate code pulled from the internet that runs
on your machine.  With its <a href="https://hacks.mozilla.org/2021/05/introducing-firefox-new-site-isolation-security-architecture/"  target="_blank" rel="noreferrer">Site Isolation</a>, also called Project Fission, each
website is loaded in a separate Operating System (OS) process. So if you visit
&ldquo;example.com&rdquo; and, in a new tab, load &ldquo;example.org&rdquo;, these 2 websites&rsquo; code will
run in 2 OS-segregated processes, each with its own isolated memory. And this
isn&rsquo;t specific to tabs; if &ldquo;example.com&rdquo; had an iframe that loaded content from
&ldquo;example.org&rdquo;, the same thing would happen: Two processes would be spawned, one
for each domain. This drastically improves protection against timing attacks
like <a href="https://en.wikipedia.org/wiki/Spectre_%28security_vulnerability%29"  target="_blank" rel="noreferrer">Spectre</a> and <a href="https://en.wikipedia.org/wiki/Meltdown_%28security_vulnerability%29"  target="_blank" rel="noreferrer">Meltdown</a> where one process can illegitimately peek into
another&rsquo;s memory. To see Firefox processes type &ldquo;about:processes&rdquo; in the address
bar and hit &ldquo;Enter&rdquo;.</p>
<p>Moreover, Firefox is smart enough to also account for <a href="https://publicsuffix.org"  target="_blank" rel="noreferrer">Public Suffix List</a><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>
domains where multiple sites can be served as different subdomains of the same
domain.  For instance, if you were to load &ldquo;my-site.codeberg.page&rdquo; and
&ldquo;another-site.codeberg.page&rdquo;, they would each get a separate process because
they would be considered two different websites even though they share the same
domain, since this domain is part of the Public Suffix List.</p>

<h2 class="relative group">Session isolation
    <div id="session-isolation" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#session-isolation" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Now, let&rsquo;s go up a few layers and look at what Firefox does at the session level.
While there&rsquo;s no built-in session separation beyond Private Browsing in Firefox,
their team has built the <a href="https://blog.mozilla.org/en/products/firefox/introducing-firefox-multi-account-containers/"  target="_blank" rel="noreferrer">Multi-Account Containers</a> add-on which takes
compartmentalization to the next level. Every container is a fresh browser
session with its own storage and cookie store. Think &ldquo;Private Browsing&rdquo; but not
limited to a single private session. This is particularly convenient when you
want to log into multiple accounts on the same website. Furthermore, all the
security and privacy measures we touched on so far are maintained inside each
container. When you first visit a website in a container, Firefox remembers that
and asks you the next time around if you&rsquo;d like to assign that website to that
container. Replying yes makes Firefox always open that website in the chosen
container without you having to do so every time. Who said you had to pick
between privacy and convenience again?</p>
<p>You can also set a separate Virtual Private Network (VPN) per container. For
example, if you want to use a VPN when shopping online you could create a
&ldquo;shopping&rdquo; container and configure it to always use your VPN<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>.</p>

<h2 class="relative group">Profiles
    <div id="profiles" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#profiles" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>To push compartmentalization even further, Firefox allows you to manage multiple
<a href="https://support.mozilla.org/en-US/kb/profile-manager-create-remove-switch-firefox-profiles?redirectslug=profile-manager-create-and-remove-firefox-profiles&amp;redirectlocale=en-US"  target="_blank" rel="noreferrer">profiles</a> within the same installation. A profile is nothing more than a set of
user information, like bookmarks, saved passwords, settings, etc. While using
profiles is generally not needed, there are few cases where it comes in very
handy. Imagine dealing with a website that doesn&rsquo;t work properly with Firefox
strict tracking protection mode, which you should always have on by the way. You
could, short of ditching that website and finding a better alternative, spin up
a new profile where you don&rsquo;t use strict tracking protection mode for the sole
purpose of visiting that website. This can apply to anything that&rsquo;s not impacted
by the Multi-Account Containers add-on like bookmarks, extensions, and themes.</p>

<h2 class="relative group">Wrap up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Well that was a lot of material! We&rsquo;ve covered various security and privacy
features that come with Firefox out of the box, in addition to an add-on that
brings a new dimension of compartmentalization to the mix. Things like cookie
store, session, and process isolation; HTTP header sanitization; and more.
In my view, this makes Firefox the best browser, at the time of this writing,
for a privacy-conscious internet user like myself.</p>
<p>If you know a free and open-source browser that offers all the guarantees
mentioned in this article, please bring it to my attention; I&rsquo;d love to see how
it compares to Firefox.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Provided that said website uses Javascript&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Public Suffix List is a community-maintained list of effective top level
domains (eTLDs) that host different sites as subdomains, like github.io and
codeberg.page.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>This only applies to HTTP/HTTPS requests. DNS queries are still subject to
browser DNS settings and do not go through the VPN configured for the container.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
<a href="https://ilye.ss/posts/why-i-love-firefox/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>DNS over HTTPS in Pihole with Docker</title><link>https://ilye.ss/posts/dns-over-https-in-pihole-with-docker/</link><pubDate>Tue, 15 Feb 2022 20:00:00 -0500</pubDate><guid>https://ilye.ss/posts/dns-over-https-in-pihole-with-docker/</guid><description>&lt;p&gt;There&amp;rsquo;s a saying that goes: &amp;ldquo;Show me your friends, I&amp;rsquo;ll tell you who you are&amp;rdquo;. A&#10;slight variation of this is: &amp;ldquo;Show me the websites you visit, I&amp;rsquo;ll tell you who&#10;you are&amp;rdquo;. A lot can be learned about an individual just by examining the websites&#10;they visit, the search queries they run, and the apps they use on a regular&#10;basis. A trove of information about a user&amp;rsquo;s online activity can be gleaned from&#10;their DNS traffic. Have you ever wondered why practically all Internet Service&#10;Providers (ISPs) pre-configure consumer routers with their own DNS servers?&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>There&rsquo;s a saying that goes: &ldquo;Show me your friends, I&rsquo;ll tell you who you are&rdquo;. A
slight variation of this is: &ldquo;Show me the websites you visit, I&rsquo;ll tell you who
you are&rdquo;. A lot can be learned about an individual just by examining the websites
they visit, the search queries they run, and the apps they use on a regular
basis. A trove of information about a user&rsquo;s online activity can be gleaned from
their DNS traffic. Have you ever wondered why practically all Internet Service
Providers (ISPs) pre-configure consumer routers with their own DNS servers?</p>
<p>In this article, we&rsquo;ll learn a bit about DoH with a quick refresher on DNS and
how it works, and go over a few strategies to improve online privacy by securing
DNS.</p>

<h2 class="relative group">What&rsquo;s DoH?
    <div id="whats-doh" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-doh" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>The Domain Name System (DNS) is the system used to identify computers on the
internet. Computers can only communicate with one another if they know each
other&rsquo;s IP addresses. But IP addresses, which are basically a bunch of numbers
bundled together, are hard to memorize. If we had to use IP addresses to access
websites, the web wouldn&rsquo;t have taken off the way it did and you wouldn&rsquo;t be
reading this article online today. DNS was invented to accommodate humans, not
machines.</p>
<p>Every time you type a URL in the browser, it issues a DNS query to your DNS
server in order to resolve the IP address of the website you&rsquo;re trying to visit.
The same thing happens when you open an app on your mobile device, when your
smart light bulb phones home to fetch the most up-to-date brightness level it
should shine at, and when you add a new show to your list on your smart TV
Netflix app. Basically, every single action you make online triggers one or more
DNS queries.</p>
<p>However, DNS was not built with privacy in mind. All DNS queries are routed
through the internet in plain text. This means that anyone sniffing traffic on a
network, or acting as a proxy to the internet, like an ISP or an enterprise
router, can see the web locations that everyone on that network is visiting.
While watching DNS traffic alone doesn&rsquo;t give out information about the
interactions between a client and a given website, it does paint a detailed
picture of the web locations that a user has visited, an estimate of how long
they have been on each website, the apps they&rsquo;re using, and the type of
Internet-of-Thing (IoT) devices they have installed in their homes, if any. It
goes without saying that this is extremely detrimental to one&rsquo;s online privacy.
Fortunately, there are solutions to this problem.</p>
<p>One way of protecting one&rsquo;s DNS queries from snooping eyes is to use encryption,
like with DNS over TLS (DoT) or DNS over HTTPS (DoH). In this article we&rsquo;ll
focus on DoH which routes DNS traffic in an encrypted HTTPS tunnel. The client
establishes a secure connection with the DNS server and funnels all DNS queries
through it. This effectively encrypts DNS communications and renders them
inaccessible to spying third parties. Now, let&rsquo;s explore a few options of
leveraging DoH to protect online activities<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and improve privacy.</p>

<h2 class="relative group">Client Settings
    <div id="client-settings" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#client-settings" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Many browsers<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> nowadays offer the option to configure a custom DoH server.
However, this kind of configuration only applies to the activity happening in
the browser and as mentioned previously, not all web requests originate from a
browser. If we want to protect all DNS traffic emerging from a network, say a
user&rsquo;s home network, we should configure the network to use a local DNS server
under our control that will turn around and delegate DNS resolution to a trusted
upstream provider using DoH. First, let&rsquo;s see how we can build this local DNS
server.</p>

<h2 class="relative group">Local DNS Server
    <div id="local-dns-server" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#local-dns-server" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Cloudflare built a nice little application called <code>cloudflared</code> that converts
plain DNS queries to DoH. It&rsquo;s free, open-source and easy to run.  If you&rsquo;ve
read any other post on this blog you must&rsquo;ve realized how much I love
containers. So let&rsquo;s build a <code>cloudflared</code> Docker image to run as a local DNS
server. Unfortunately, Cloudflare doesn&rsquo;t offer an official Docker image for
<code>cloudflared</code> but we can make our own fairly easily. To do so, in a file named
<code>Dockerfile</code> put the following content:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-docker" data-lang="docker"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1</span><span><span style="color:#66d9ef">FROM</span> <span style="color:#e6db74">alpine:3.15</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3</span><span><span style="color:#66d9ef">RUN</span> apk add --no-cache bash<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5</span><span><span style="color:#66d9ef">RUN</span> wget -q https://github.com/cloudflare/cloudflared/releases/download/2022.1.2/cloudflared-linux-arm <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6</span><span>	<span style="color:#f92672">&amp;&amp;</span> chmod +x cloudflared-linux-arm <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7</span><span>    <span style="color:#f92672">&amp;&amp;</span> mv /cloudflared-linux-arm /usr/local/bin/cloudflared</span></span></code></pre></div></div>
<p>Here, we&rsquo;re using <code>alpine</code> version 3.15 and <code>cloudflared</code> version 2022.1.2 but
more versions might have been released since this article was published. Feel
free to update these versions to the latest.</p>
<p>In order to use this Docker image we can build a <code>docker-compose</code> service to run
<code>cloudflared</code>. Alongside the previously created <code>Dockerfile</code>, let&rsquo;s make a new
file called <code>docker-compose.yml</code> with this content:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-docker" data-lang="docker"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span>version: <span style="color:#e6db74">&#34;3.8&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span>services:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span>  cloudflared:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span>    build: .<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span>    ports:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span>      - <span style="color:#e6db74">&#34;53:5053&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span>    <span style="color:#75715e"># replace &lt;UPSTREAM SERVER&gt; with the URL for the upstream DoH server</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span>    <span style="color:#75715e"># e.g.: https://doh.libredns.gr/dns-query</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span>    entrypoint: bash -c <span style="color:#e6db74">&#34;cloudflared proxy-dns --port 5053 --address 0.0.0.0 --upstream &lt;UPSTREAM SERVER&gt;&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span>    restart: unless-stopped</span></span></code></pre></div></div>
<p>Make sure to replace <code>&lt;UPSTREAM SERVER&gt;</code> in the <code>entrypoint</code> command with your
DoH server of choice. PrivacyGuides lists <a href="https://www.privacyguides.org/en/dns/#recommended-providers"  target="_blank" rel="noreferrer">a few options</a>
you can pick from if you don&rsquo;t already have a favourite DNS server. At this
point, you have everything you need to convert all DNS queries in your network
into DoH. Here are the steps to accomplish this:</p>
<ol>
<li>Run <code>docker-compose up cloudflared</code> from the folder where you put the
<code>Dockerfile</code> and <code>docker-compose.yml</code> files to spin up a <code>cloudflared</code>
container. Ideally, this should be done on a machine that&rsquo;s constantly
running on your network with a static IP address. A Raspberry Pi or a home
server are great candidates for this.</li>
<li>Configure your network&rsquo;s DHCP server to use the machine where <code>cloudflared</code>
is running as its default DNS server. This will automatically instruct all
devices on the network to use the <code>cloudflared</code> container for DNS resolution.
If you don&rsquo;t have control over your DHCP server, or it&rsquo;s too complicated to
reconfigure at the moment, you can always start off by manually editing the
DNS configuration on your most-used devices.</li>
</ol>

<h2 class="relative group">DoH in Pihole
    <div id="doh-in-pihole" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#doh-in-pihole" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>If you already have a Pihole docker container running in your network and
serving DNS queries, you can now set the <code>cloudflared</code> container as an upstream
DNS server in Pihole and automatically upgrade all DNS queries to DoH. We&rsquo;ve
seen in <a href="/posts/pihole-dhcp-docker-bridge-network/" >a previous article</a> how to set up Pihole using Docker. We&rsquo;ll use the same
<code>docker-compose</code> file here to illustrate how to integrate <code>cloudflared</code>.</p>
<p>First we need to place the <code>Dockerfile</code> file we created in the previous section
inside a folder called <code>cloudflared</code>. Then, using our previous Pihole
<code>docker-compose</code> file, we can add a new service for <code>cloudflared</code> as shown
below:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-docker" data-lang="docker"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span>version: <span style="color:#e6db74">&#34;3.8&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span>services:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span>  pihole:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span>    <span style="color:#75715e"># ...</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span>    environment:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span>      <span style="color:#75715e"># ...</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span>      - PIHOLE_DNS_<span style="color:#f92672">=</span>172.31.0.200#5350<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span>    <span style="color:#75715e"># ...</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span>    depends_on:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span>      - cloudflared<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12</span><span>      - dhcphelper<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13</span><span>    <span style="color:#75715e"># ...</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15</span><span>  dhcphelper:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16</span><span>    <span style="color:#75715e"># ...</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18</span><span>  cloudflared:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19</span><span>    build: ./cloudflared<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20</span><span>    environment:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21</span><span>      <span style="color:#75715e"># set UPSTREAM_PROVIDER to the URL for the upstream DOH server</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22</span><span>      <span style="color:#75715e"># e.g.: https://doh.libredns.gr/dns-query</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23</span><span>      - UPSTREAM_PROVIDER<span style="color:#f92672">=</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24</span><span>    entrypoint: bash -c <span style="color:#e6db74">&#34;cloudflared proxy-dns --port 5053 --address 0.0.0.0 --upstream </span>$$<span style="color:#e6db74">UPSTREAM_PROVIDER&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25</span><span>    networks:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26</span><span>      backend:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27</span><span>        ipv4_address: <span style="color:#e6db74">&#39;172.31.0.200&#39;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28</span><span>    restart: unless-stopped<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30</span><span>networks:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31</span><span>  <span style="color:#75715e"># ...</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33</span><span>volumes:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34</span><span>  <span style="color:#75715e"># ...</span></span></span></code></pre></div></div>
<p>You&rsquo;ll notice that the <code>cloudflared</code> service looks similar to the one we built
in the previous section. Here, we don&rsquo;t need to expose the port 53 to the host
because the only client connecting to the <code>cloudflared</code> container is the Pihole
container running in the same Docker network. Also, we have to set a static IP
address for the <code>cloudflared</code> container and add it to the <code>PIHOLE_DNS_</code>
environment variable of Pihole in order for it to be used as an upstream DNS
server.</p>
<p>With the modifications above, restart your Pihole container by running
<code>docker-compose down</code> and bringing it back up with <code>docker-compose up pihole</code>,
and you should have a <code>cloudflared</code> container running alongside Pihole, ready to
receive requests.</p>

<h2 class="relative group">Wrap up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Congratulations, you made it! You now understand why protecting your DNS traffic
is paramount to improving your online privacy and have in your toolbox 3
strategies for doing so, some more potent than others. In case you can&rsquo;t run a
full-fledged Pihole and <code>cloudflared</code> setup on your network, or don&rsquo;t have the
time to set that up yet, at least configure your most-frequently-used browser to
resolve DNS through DoH. It takes almost no time, and goes a long way in getting
you to a better place when it comes to your online privacy.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Keep in mind that this only protects DNS queries. It is still possible for
an adversary to figure out the websites you visit by doing reverse DNS
resolution on the IP addresses of those websites.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Here&rsquo;s an <a href="https://support.mozilla.org/en-US/kb/firefox-dns-over-https"  target="_blank" rel="noreferrer">example</a> for Firefox.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
<a href="https://ilye.ss/posts/dns-over-https-in-pihole-with-docker/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>My Journey with Password Managers</title><link>https://ilye.ss/posts/my-journey-with-password-managers/</link><pubDate>Tue, 28 Dec 2021 20:00:00 -0500</pubDate><guid>https://ilye.ss/posts/my-journey-with-password-managers/</guid><description>&lt;p&gt;Password managers have become a crucial tool that every online user must rely&#10;on. Since they handle extremely sensitive data, like login credentials, credit&#10;card information, secret notes, and more, careful consideration is recommended&#10;when choosing a password manager. This article goes over the phases I&amp;rsquo;ve gone&#10;through in my experience with password managers, the different solutions I used,&#10;and how I got where I am today.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;First Password Manager&#10; &lt;div id="first-password-manager" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#first-password-manager" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;My journey with password managers started way back when I used the &amp;ldquo;save&#10;password&amp;rdquo; feature in the Chrome browser. I was still &amp;ldquo;generating&amp;rdquo; passwords&#10;myself but relying on Chrome to save and automatically insert them when I&amp;rsquo;m on&#10;the login page of various websites. At that time, I didn&amp;rsquo;t really have too many&#10;accounts - probably no more than a dozen. But as the web started turning into a&#10;plethora of sign-up walls, where you can&amp;rsquo;t hover on a button without having to&#10;log in first, I quickly ran out of ideas to create new unique&#10;passwords. This is the point where I knew I needed a proper password manager.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p>Password managers have become a crucial tool that every online user must rely
on. Since they handle extremely sensitive data, like login credentials, credit
card information, secret notes, and more, careful consideration is recommended
when choosing a password manager. This article goes over the phases I&rsquo;ve gone
through in my experience with password managers, the different solutions I used,
and how I got where I am today.</p>

<h2 class="relative group">First Password Manager
    <div id="first-password-manager" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#first-password-manager" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>My journey with password managers started way back when I used the &ldquo;save
password&rdquo; feature in the Chrome browser. I was still &ldquo;generating&rdquo; passwords
myself but relying on Chrome to save and automatically insert them when I&rsquo;m on
the login page of various websites. At that time, I didn&rsquo;t really have too many
accounts - probably no more than a dozen. But as the web started turning into a
plethora of sign-up walls, where you can&rsquo;t hover on a button without having to
log in first, I quickly ran out of ideas to create new unique
passwords. This is the point where I knew I needed a proper password manager.</p>
<p>Around this time, <a href="https://www.lastpass.com"  target="_blank" rel="noreferrer">Lastpass</a> was one of the most dominant actors in the field of
password management and they were doing some good work. So I decided to give
Lastpass a try and I really liked it. The convenience of not having to come up
with a &ldquo;secure&rdquo; password that I haven&rsquo;t used before when signing up on a new
website, brought such a relief to my account creation flow and reduced a
considerable amount of friction. Lastpass had some issues however.
<a href="https://www.wired.com/2015/06/hack-brief-password-manager-lastpass-got-breached-hard/"  target="_blank" rel="noreferrer">The data breach of 2015</a>
was the first thing to sound the alarm for me. I started to realize
that fully trusting an entity with my most valuable data, the keys to every
single account I own, was probably not a good idea. To make matters more scary,
Lastpass does not publish their source code for others to view, analyze, and
even contribute to. Security by obscurity is never the way to go.
Furthermore, <a href="https://www.theverge.com/2021/2/26/22302709/lastpass-android-app-trackers-security-research-privacy"  target="_blank" rel="noreferrer">they include third-party trackers</a>
in their codebase which is a very bad practice for a security-critical service
like Lastpass.</p>
<p>I&rsquo;m not trying to sabotage Lastpass here, or imply that their security is
lacking. As a matter of fact, their security model seems to be solid. As far
as I know, there has never been any breach that exposed user sensitive data like
passwords in clear text. All I&rsquo;m saying is that it wasn&rsquo;t the right fit for me.
Not to mention that their recent <a href="https://blog.lastpass.com/2021/02/changes-to-lastpass-free/"  target="_blank" rel="noreferrer">up-sell push</a>
to convert free users to premium by restructuring their pricing and feature
models just didn&rsquo;t sit right with me.  They were turning into another Big Tech
player, and it was time for me to walk away.</p>

<h2 class="relative group">Gaining back control over my data
    <div id="gaining-back-control-over-my-data" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#gaining-back-control-over-my-data" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>When I started using Lastpass, there weren&rsquo;t many options to choose from. But
few years later, other password managers started popping up and one of them was
particularly good at distinguishing itself amongst the privacy-focused
community. This password manager is no other than <a href="https://keepassxc.org/"  target="_blank" rel="noreferrer">KeepassXC</a>. I believe it&rsquo;s the
first open-source password manager that I was made aware of and I instantly fell
for it.  It&rsquo;s a free, community-driven, offline password manager that I simply
couldn&rsquo;t ignore. Next thing you know, I was migrating all of my passwords off of
Lastpass and celebrating the addition of yet another open-source tool to my
arsenal.</p>
<p>The only problem that I had to solve was synchronizing data across multiple
devices. With a cloud-based service like Lastpass, this issue is taken care of
by the service itself and changes on one device are replicated across the others
automatically. With an offline password manager like KeepassXC on the other
hand, data synchronization becomes the responsibility of the user. So, I moved
all my credentials to a KeepassXC database (DB), and used a cloud service to
regularly back it up.  When I needed a fresh version of my DB in any of my
devices, I either transferred it directly within my home network, or pulled it
from the cloud service provider, and used an open-source client to read it. To
make things simple, I also only ever made changes to the DB on my computer, and
stuck to read-only mode on the other devices. You can imagine how this could
sometimes prove tedious, especially when I needed to update existing or create
new entries in my credentials list from a device other than my main computer. I
started to seriously miss the convenience of the seamless synchronization I
enjoyed with Lastpass, but I wasn&rsquo;t ready to downgrade my security and privacy
just for that.</p>

<h2 class="relative group">The best of all worlds
    <div id="the-best-of-all-worlds" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-best-of-all-worlds" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Luckily, there was this new kid on the block, a new solution that ticked all the
boxes for me - it&rsquo;s open source, free<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, and self-hostable! This meant that I
could get all the benefits of data control I have with KeepassXC <em>and</em> the
convenience of seamless multi-device synchronization. The solution I&rsquo;m referring
to is <a href="https://bitwarden.com/"  target="_blank" rel="noreferrer">Bitwarden</a>. Ever since I launched <a href="/posts/self-host-bitwarden-using-docker/" >my own instance of Bitwarden</a>,
I&rsquo;ve never looked back. I truly love this piece of software and am so grateful
to the amazing people behind it. Plus, the beauty of it all is that when you
host your own instance you get all of the premium features for free!</p>

<h2 class="relative group">Wrap up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>As you see, I wasn&rsquo;t fortunate enough to pick a winner right from the start. I
began my journey naive, prioritizing convenience without paying much attention
to security, let alone privacy. As I learned more about password managers, I
shifted my priorities and started giving more weight to security and privacy,
even at the expense of convenience at times.</p>
<p>If you have comments or suggestions, or if you just want to strike a
conversation on this topic, feel free to hit me up on <a href="https://mastodon.online/@ilyess"  target="_blank" rel="noreferrer">Mastodon</a>.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>They also offer premium plans but their free tier has all the basic
features expected in a password manager.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
<a href="https://ilye.ss/posts/my-journey-with-password-managers/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>Self-Host Bitwarden using Docker</title><link>https://ilye.ss/posts/self-host-bitwarden-using-docker/</link><pubDate>Wed, 02 Jun 2021 20:00:00 -0400</pubDate><guid>https://ilye.ss/posts/self-host-bitwarden-using-docker/</guid><description>&lt;p&gt;&lt;a href="https://bitwarden.com" target="_blank" rel="noreferrer"&gt;Bitwarden&lt;/a&gt; is a password manager that allows users to generate and store strong&#10;passwords. It also handles other types of data like secure notes and credit card&#10;information. At the time of this writing, it is one of the best password&#10;managers out there because in addition to offering strong and zero-knowledge&#10;encryption, the codebase is &lt;a href="https://bitwarden.com/open-source/" target="_blank" rel="noreferrer"&gt;open source&lt;/a&gt;. This means that anyone can inspect the&#10;code and run it for their personal use. In this article, we&amp;rsquo;ll go over the steps&#10;to build a fully functioning Bitwarden instance that anyone can run on a server&#10;at home.&lt;/p&gt;</description><content:encoded>
<![CDATA[<p><a href="https://bitwarden.com"  target="_blank" rel="noreferrer">Bitwarden</a> is a password manager that allows users to generate and store strong
passwords. It also handles other types of data like secure notes and credit card
information. At the time of this writing, it is one of the best password
managers out there because in addition to offering strong and zero-knowledge
encryption, the codebase is <a href="https://bitwarden.com/open-source/"  target="_blank" rel="noreferrer">open source</a>. This means that anyone can inspect the
code and run it for their personal use. In this article, we&rsquo;ll go over the steps
to build a fully functioning Bitwarden instance that anyone can run on a server
at home.</p>

<h2 class="relative group">Docker setup
    <div id="docker-setup" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#docker-setup" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Let&rsquo;s start by setting up a <code>docker-compose.yml</code> file to run the Bitwarden server:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-docker" data-lang="docker"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span>version: <span style="color:#e6db74">&#34;3.8&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span>services:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span>  bitwarden:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span>    container_name: <span style="color:#e6db74">&#34;bitwarden&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span>    image: vaultwarden/server<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span>    ports:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span>      - <span style="color:#e6db74">&#34;3012:3012&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span>    volumes:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span>      - data:/data<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12</span><span>volumes:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13</span><span>  data:</span></span></code></pre></div></div>
<p>Here, we&rsquo;re using vaultwarden/server image which is a rust-based version of
Bitwarden server. We&rsquo;re exposing a TCP port to be able to communicate with the
container from outside of Docker network. Then, we create a volume and mount it
on /data inside the container. That&rsquo;s where Bitwarden stores its data, including
users, vaults, and attachments.</p>
<p>If we bring up this container and try to access Bitwarden through
<code>https://localhost:3012</code>, it will not work.  Bitwarden requires all communications
to go through a TLS tunnel. In other words, we need to use HTTPS instead of
HTTP. We&rsquo;re going to use a second container running Nginx to handle TLS for us
and proxy requests to Bitwarden&rsquo;s container.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-docker" data-lang="docker"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span>version: <span style="color:#e6db74">&#34;3.8&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span>services:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span>  bitwarden:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span>    <span style="color:#75715e"># ...</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span>    depends_on:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span>      - nginx<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span>  nginx:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span>    image: nginx:1.21.0-alpine<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span>    ports:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12</span><span>      - <span style="color:#e6db74">&#34;23984:443&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13</span><span>    volumes:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14</span><span>      - ./nginx/ssl:/etc/ssl:ro<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15</span><span>      - ./nginx/conf.d:/etc/nginx/conf.d:ro<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16</span><span>    restart: unless-stopped<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18</span><span>volumes:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19</span><span>  data:</span></span></code></pre></div></div>
<p>In this configuration, we&rsquo;re declaring a new service <code>nginx</code> using the alpine
image of <code>nginx</code>, exposing a port so that the container can be reached from
outside Docker&rsquo;s internal network, and specifying a couple of read-only volumes.
We&rsquo;re also instructing Docker to automatically restart this container unless it
was manually stopped. This can help increase availability by automatically
recovering from sudden crashes.</p>
<p>The final result looks like follows. Notice that we introduced a environment
variable <code>ADMIN_TOKEN</code> that can be used to access the admin section of
Bitwarden. More on how to do this later.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-docker" data-lang="docker"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span>version: <span style="color:#e6db74">&#34;3.8&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span>services:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span>  bitwarden:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span>    container_name: <span style="color:#e6db74">&#34;bitwarden&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span>    image: vaultwarden/server<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span>    environment:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span>      - ADMIN_TOKEN<span style="color:#f92672">=</span>SOME_SECRET_TOKEN<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span>    volumes:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span>      - data:/data<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span>    depends_on:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12</span><span>      - nginx<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14</span><span>  nginx:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15</span><span>    image: nginx:1.21.0-alpine<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16</span><span>    ports:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17</span><span>      - <span style="color:#e6db74">&#34;23984:443&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18</span><span>    volumes:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19</span><span>      - ./nginx/ssl:/etc/ssl:ro<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20</span><span>      - ./nginx/conf.d:/etc/nginx/conf.d:ro<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21</span><span>    restart: unless-stopped<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23</span><span>volumes:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24</span><span>  data:</span></span></code></pre></div></div>

<h2 class="relative group">TLS Support
    <div id="tls-support" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#tls-support" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Now that we have our containers set up, it&rsquo;s time to configure Nginx to handle
TLS connections and forward them to Bitwarden&rsquo;s container. Let&rsquo;s create a
server configuration file named <code>bitwarden.conf</code> and place it under
<code>nginx/conf.d/</code>. This path is relative to wherever you place the
<code>docker-compose.yml</code> file.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span>    <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span>    <span style="color:#75715e"># Allow large attachments
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span>    <span style="color:#f92672">client_max_body_size</span> <span style="color:#e6db74">128M</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span>    <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span>        <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://bitwarden:80</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span>        <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span>        <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Real-IP</span> $remote_addr;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span>        <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-For</span> $proxy_add_x_forwarded_for;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12</span><span>        <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> $scheme;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13</span><span>    }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15</span><span>    <span style="color:#f92672">location</span> <span style="color:#e6db74">/notifications/hub</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16</span><span>        <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://bitwarden:3012</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17</span><span>        <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Upgrade</span> $http_upgrade;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18</span><span>        <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Connection</span> <span style="color:#e6db74">&#34;upgrade&#34;</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19</span><span>    }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21</span><span>    <span style="color:#f92672">location</span> <span style="color:#e6db74">/notifications/hub/negotiate</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22</span><span>        <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://bitwarden:80</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23</span><span>    }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24</span><span>}</span></span></code></pre></div></div>
<p>This is a pretty typical Nginx proxy configuration where we define a port to
listen to along with protocols we want to handle. Next, we have a directive to
allow large attachments<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and we declare a few paths and how they should be
handled. We&rsquo;re not going to dive into each directive used here but if you want
to know more about how to configure Nginx, I encourage to check out their
<a href="https://nginx.org/en/docs/"  target="_blank" rel="noreferrer">official documentation.</a></p>
<p>If you&rsquo;ve been following along you&rsquo;ll notice that we haven&rsquo;t addressed the SSL
connection yet, and without it our setup will not work. So let&rsquo;s figure that
out. In case you&rsquo;re planning to use a real domain name for your Bitwarden instance,
you can skip this section and jump directly to <a href="https://ilye.ss/posts/self-host-bitwarden-using-docker/#nginx-configuration" >Nginx Configuration</a>.</p>
<p>In order to make a self-signed TLS certificate, first we need to generate a
private key and a certificate for our Bitwarden server using the following
command.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>OUT_FOLDER<span style="color:#f92672">=</span>/path/to/ssl
</span></span><span style="display:flex;"><span>DOMAIN<span style="color:#f92672">=</span>your.bitwarden.domain
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>openssl req -x509 -nodes -days <span style="color:#ae81ff">365</span> -newkey rsa:4096 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>        -config &lt;<span style="color:#f92672">(</span>cat /etc/ssl/openssl.cnf &lt;<span style="color:#f92672">(</span>printf <span style="color:#e6db74">&#34;[SAN]\nsubjectAltName=DNS:</span>$DOMAIN<span style="color:#e6db74">\nbasicConstraints=CA:true&#34;</span><span style="color:#f92672">))</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>        -keyout $OUT_FOLDER/private/nginx-bitwarden.key <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>        -out $OUT_FOLDER/certs/nginx-bitwarden.cert <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>        -reqexts SAN -extensions SAN <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>        -subj <span style="color:#e6db74">&#34;/C=US/ST=New York/L=New York/O=Company Name/OU=Bitwarden/CN=</span>$DOMAIN<span style="color:#e6db74">&#34;</span></span></span></code></pre></div></div>
<p>This will generate a new public key <code>nginx-bitwarden.key</code> using RSA with a key
length of <code>4096</code> bits, and a self-signed TLS certificate <code>nginx-bitwarden.cert</code>
valid for <code>365</code> days in the <code>private/</code> and <code>certs/</code> folders respectively. These
folders should be placed under the folder <code>ssl/</code> that lives alongside our
<code>docker-compose.yml</code> file, so make sure you set the <code>$OUT_FOLDER</code> variable
properly. In addition, the <code>$DOMAIN</code> should be set to the fully qualified domain
name of the machine where you&rsquo;re planning to run Bitwarden and Nginx. It&rsquo;s not
complicated to assign an FQDN to your local machine using a local DNS,
especially if you&rsquo;re running Pihole. If you&rsquo;re interested in getting Pihole set
up locally with Docker, check out this <a href="https://ilye.ss/posts/pihole-dhcp-docker-bridge-network/" >article</a>.</p>

<h3 class="relative group">Nginx Configuration
    <div id="nginx-configuration" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#nginx-configuration" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>Now that we have our certificate and private key, we should tell Nginx where
they are located in order to use them for TLS connections.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1</span><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2</span><span>    <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4</span><span>    <span style="color:#f92672">ssl_certificate</span>      <span style="color:#e6db74">/etc/ssl/certs/nginx-bitwarden.crt</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5</span><span>    <span style="color:#f92672">ssl_certificate_key</span>  <span style="color:#e6db74">/etc/ssl/private/nginx-bitwarden.key</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7</span><span>    <span style="color:#75715e"># ...
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">8</span><span>}</span></span></code></pre></div></div>
<p>It&rsquo;s recommended to generate a Diffie-Hellman file for stronger connections and
use it in our Nginx configuration. We can generate one with the command:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>openssl dhparam -out $OUT_FOLDER/certs/dhparam.pem <span style="color:#ae81ff">4096</span></span></span></code></pre></div></div>
<p>To be honest, I haven&rsquo;t yet looked at the functions of this Diffie-Hellman file
to fully understand how it improves security. But if you know more about it
and are interested in contributing your knowledge to this article, feel free to
reach out to me on <a href="https://mastodon.online/@ilyess"  target="_blank" rel="noreferrer">Mastodon</a>.</p>
<p>The complete Nginx configuration after linking the <code>.pem</code> file should look like
follows.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span>    <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span> <span style="color:#e6db74">http2</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span>    <span style="color:#f92672">ssl_certificate</span>      <span style="color:#e6db74">/etc/ssl/certs/nginx-bitwarden.crt</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span>    <span style="color:#f92672">ssl_certificate_key</span>  <span style="color:#e6db74">/etc/ssl/private/nginx-bitwarden.key</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span>    <span style="color:#f92672">ssl_dhparam</span> <span style="color:#e6db74">/etc/ssl/certs/dhparam.pem</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span>    <span style="color:#f92672">client_max_body_size</span> <span style="color:#e6db74">128M</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span>    <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12</span><span>        <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://bitwarden:80</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13</span><span>        <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14</span><span>        <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Real-IP</span> $remote_addr;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15</span><span>        <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-For</span> $proxy_add_x_forwarded_for;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16</span><span>        <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> $scheme;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17</span><span>    }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19</span><span>    <span style="color:#f92672">location</span> <span style="color:#e6db74">/notifications/hub</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20</span><span>        <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://bitwarden:3012</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21</span><span>        <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Upgrade</span> $http_upgrade;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22</span><span>        <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Connection</span> <span style="color:#e6db74">&#34;upgrade&#34;</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23</span><span>    }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25</span><span>    <span style="color:#f92672">location</span> <span style="color:#e6db74">/notifications/hub/negotiate</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26</span><span>        <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://bitwarden:80</span>;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27</span><span>    }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28</span><span>}</span></span></code></pre></div></div>
<p>Finally, we have our containers configured to run Bitwarden. All we need to do in
order to bring them up is run:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>docker-compose up -d bitwarden</span></span></code></pre></div></div>
<p>Once the containers are up, you&rsquo;ll be able to reach Bitwarden through its domain
name. Given our example configuration, that would be
<code>https://your.bitwarden.domain</code>. The first time you open the page, your browser
will most likely complain about an insecure connection and display a flashy
warning.  If it doesn&rsquo;t, stop using this browser and get yourself a real one!
This is due to the self-signed certificate. Since the certificate is not signed
by a Certificate Authority (CA) present in the browsers or the OS&rsquo;s root CA
store, the browser considers the connection insecure. You can add an exception
for this certificate in your browser so that it doesn&rsquo;t freak out every time you
access your self-hosted Bitwarden. Similarly, you will have to install this
certificate on your device and trust it, otherwise the OS will prevent
Bitwarden&rsquo;s app to communicate with our running container. To do so, send the
<code>nginx-bitwarden.crt</code> file we generated in the <a href="https://ilye.ss/posts/self-host-bitwarden-using-docker/#tls-support" >TLS Support section</a> to your
device<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, open it and follow the installation steps. It should be
straightforward in most OS&rsquo;s but it&rsquo;s always safe to follow the official guide
to make sure you cover all the steps. For instance, in iOS, on top of installing
the certificate you have to explicitly trust it for TLS connections.</p>

<h2 class="relative group">Admin Access
    <div id="admin-access" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#admin-access" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Like we&rsquo;ve seen in a previous section, in order to unlock the admin panel we
need to set a token in the <code>ADMIN_TOKEN</code> environment variable. Let&rsquo;s generate a
token using the following command.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>openssl rand -base64 <span style="color:#ae81ff">48</span></span></span></code></pre></div></div>
<p>Once Bitwarden&rsquo;s container is restarted after setting the admin token, you can
access the admin panel at <code>https://your.bitwarden.domain/admin</code>.</p>

<h2 class="relative group">Wrap up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>We finally made it. We built a docker setup running a container for the
Bitwarden server with an Nginx proxy handling TLS connections using a
self-signed certificate. This grants us full control over our credentials and
any other data we choose to store in Bitwarden, all without having to trust any
third party to handle this for us.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>This one is optional and is only necessary if you use Bitwarden for
attachments or to send large files.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>It&rsquo;s safe to use email here since the certificate doesn&rsquo;t contain any
sensitive information.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
<a href="https://ilye.ss/posts/self-host-bitwarden-using-docker/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item><item><title>Pihole DHCP and Docker Bridge Network</title><link>https://ilye.ss/posts/pihole-dhcp-docker-bridge-network/</link><pubDate>Tue, 25 May 2021 13:00:00 -0400</pubDate><guid>https://ilye.ss/posts/pihole-dhcp-docker-bridge-network/</guid><description>&lt;p&gt;&lt;a href="https://pi-hole.net/" target="_blank" rel="noreferrer"&gt;Pihole&lt;/a&gt; is a great tool to protect your home network from&#10;trackers and annoying ads. It can be deployed either directly on a server or in&#10;a Docker container. I personally lean toward using Docker whenever possible for&#10;the flexibility and isolation it provides. Services deployed in Docker&#10;containers are significantly easier to migrate than raw installations and Pihole&#10;is no exception.&lt;/p&gt;&#10;&lt;p&gt;Running Pihole on Docker is pretty straightforward, but things start to get a&#10;bit complicated when it comes to enabling DHCP - using Pihole to serve DHCP&#10;requests. With Docker&amp;rsquo;s default bridge network mode, we can&amp;rsquo;t use Pihole as a&#10;DHCP server. So we have two options: (1) use the host network&#10;mode, or (2) run a DHCP relay (more on this later).&lt;/p&gt;</description><content:encoded>
<![CDATA[<p><a href="https://pi-hole.net/"  target="_blank" rel="noreferrer">Pihole</a> is a great tool to protect your home network from
trackers and annoying ads. It can be deployed either directly on a server or in
a Docker container. I personally lean toward using Docker whenever possible for
the flexibility and isolation it provides. Services deployed in Docker
containers are significantly easier to migrate than raw installations and Pihole
is no exception.</p>
<p>Running Pihole on Docker is pretty straightforward, but things start to get a
bit complicated when it comes to enabling DHCP - using Pihole to serve DHCP
requests. With Docker&rsquo;s default bridge network mode, we can&rsquo;t use Pihole as a
DHCP server. So we have two options: (1) use the host network
mode, or (2) run a DHCP relay (more on this later).</p>
<p>Option (1) is the easiest because all we&rsquo;d have to do is set <code>network_mode: &quot;host&quot;</code> in the service definition, but it comes with a considerable
disadvantage. It undermines the network isolation provided by Docker&rsquo;s bridge
driver, not to mention that it will take up ports from the host&rsquo;s network which
could be a concern depending on what&rsquo;s running on your server. Option
(2) allows us to stick to the bridge mode but adds a good amount of complexity.
If you&rsquo;re anything like me, you&rsquo;ll favor security and try to maintain Docker&rsquo;s
network isolation.</p>
<p>Option (2) it is. Make sure your cup of coffee is full, and let&rsquo;s dive in!</p>

<h2 class="relative group">Pihole on Docker
    <div id="pihole-on-docker" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#pihole-on-docker" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>First, let&rsquo;s build a <code>docker-compose.yml</code> file with a basic configuration to run
Pihole.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-docker" data-lang="docker"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span>version: <span style="color:#e6db74">&#34;3.2&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span>services:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span>  pihole:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span>    container_name: pihole<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span>    image: pihole/pihole:latest<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span>    ports:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span>      - <span style="color:#e6db74">&#34;53:53/tcp&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span>      - <span style="color:#e6db74">&#34;53:53/udp&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span>      - <span style="color:#e6db74">&#34;8080:80/tcp&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span>    environment:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12</span><span>      - TZ<span style="color:#f92672">=</span>America/New_York<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13</span><span>      - WEBPASSWORD<span style="color:#f92672">=</span>very-secure-password <span style="color:#75715e"># Choose a password for admin</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14</span><span>      - PIHOLE_DNS_<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;1.1.1.1;1.0.0.1&#34;</span> <span style="color:#75715e"># Set upstream DNS servers</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15</span><span>      - ServerIP<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;10.10.0.10&#34;</span> <span style="color:#75715e"># Your host&#39;s external IP</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16</span><span>    volumes:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17</span><span>      - pihole:/etc/pihole/<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18</span><span>      - dnsmasqd:/etc/dnsmasq.d/<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19</span><span>    restart: unless-stopped<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21</span><span>volumes:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22</span><span>  pihole:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23</span><span>  dnsmasqd:</span></span></code></pre></div></div>
<p>In this example, we define a service called <code>pihole</code> using the official Pihole
Docker image. Then, we publish a few ports to get DNS requests (UDP port 53)
forwarded to the container running Pihole, and to allow access to the web panel
(TCP port 80). After that, we set some environment variables:</p>
<ul>
<li><code>WEBPASSWORD</code><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>: The password to use when logging into the admin panel.</li>
<li><code>PIHOLE_DNS_</code>: A semicolon separated list of upstream DNS servers. These are
the servers Pihole will reach out to in order to resolve DNS queries.</li>
<li><code>ServerIP</code>: The external host IP. This is the IP that machines on the network
will send their DNS queries to. In other words, this should be the host&rsquo;s IP on
the LAN network.</li>
</ul>
<p>Last, we define a couple of volumes to persist configuration across multiple
container restarts.</p>

<h2 class="relative group">DHCP through Docker&rsquo;s Bridge Network
    <div id="dhcp-through-dockers-bridge-network" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#dhcp-through-dockers-bridge-network" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>At this point, we have a working Pihole deployment ready to manage DNS on the
host&rsquo;s network.  Since we didn&rsquo;t specify the network mode in the docker-compose
file, the container is provisioned using the default driver: bridge network.
This means that Pihole lives in a separate network than the host along with
other machines on the LAN. Docker daemon bridges these 2 networks and forwards
ports as specified in the docker-compose file. This works great for DNS, but not
so much for DHCP. If we tried to enable DHCP on Pihole&rsquo;s web interface, it would
unfortunately not work. To understand why, let&rsquo;s go over a brief overview of how
DHCP works.</p>
<p>When a client joins a network it starts broadcasting a DHCP discovery request on
that same network on UDP port 67. If there&rsquo;s a DHCP server listening, it will
respond to this request with a lease offer that the client then accepts<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.
This is how machines get assigned IP addresses dynamically when joining a
network.</p>
<p>DHCP is not routable however, so discovery requests don&rsquo;t span across different
networks. In our setup, since Pihole is running in a Docker container with
bridge network mode, it lives in a separate network (Docker internal network)
than the LAN, so DHCP discovery requests stop at the edge of the LAN and never
make it to Pihole. To solve this, we need a DHCP relay connected to both the LAN
and Docker&rsquo;s internal network. It will intercept discovery requests on one
network and forward them to Pihole on the other.</p>
<p>With the theory out of the way, let&rsquo;s move on to a practical solution to our
problem. We will use <code>dhcp-helper</code> as a DHCP relay. First, we create a small
image with that package and place the <code>Dockerfile</code> in a folder called
<code>dhcp-helper</code>.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-docker" data-lang="docker"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1</span><span><span style="color:#66d9ef">FROM</span> <span style="color:#e6db74">alpine:latest</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2</span><span><span style="color:#66d9ef">RUN</span> apk --no-cache add dhcp-helper<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3</span><span><span style="color:#66d9ef">EXPOSE</span> <span style="color:#e6db74">67</span> 67/udp<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4</span><span><span style="color:#66d9ef">ENTRYPOINT</span> [<span style="color:#e6db74">&#34;dhcp-helper&#34;</span>, <span style="color:#e6db74">&#34;-n&#34;</span>]</span></span></code></pre></div></div>
<p>The file content is self explanatory. We&rsquo;re using an <code>alpine</code> image, adding the
<code>dhcp-helper</code> package, exposing the UDP port 67 in order to receive DHCP
discovery requests, and we&rsquo;re running the helper command at startup.</p>
<p>The next step is to update the original Pihole docker-compose file to include
the DHCP relay. We will need to add a service for the relay, an additional
network with a fixed IP for the Pihole container, and make the Pihole service
depend on the DHCP relay.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-docker" data-lang="docker"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span>version: <span style="color:#e6db74">&#34;3.2&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span>services:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span>  pihole:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span>    container_name: pihole<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span>    <span style="color:#75715e"># ...</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span>  dhcphelper:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span>    build: ./dhcp-helper<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span>    restart: unless-stopped<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span>    network_mode: <span style="color:#e6db74">&#34;host&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12</span><span>    command: -s 172.31.0.111<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13</span><span>    cap_add:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14</span><span>      - NET_ADMIN</span></span></code></pre></div></div>
<p>Notice that we&rsquo;re using the <code>host</code> network mode for the DHCP relay service. This
is important if we want to intercept discovery requests on the same network as
the host (LAN). Also, we&rsquo;re adding a flag to the entry point command specifying
the server&rsquo;s IP. We&rsquo;ll assign this IP to Pihole&rsquo;s service as follows:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-docker" data-lang="docker"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1</span><span>version: <span style="color:#e6db74">&#34;3.2&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3</span><span>services:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4</span><span>  pihole:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5</span><span>    container_name: pihole<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6</span><span>    <span style="color:#75715e"># ...</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7</span><span>    networks:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">8</span><span>      backend:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">9</span><span>        ipv4_address: <span style="color:#e6db74">&#39;172.31.0.111&#39;</span></span></span></code></pre></div></div>
<p>In order to use this new network that we named <code>backend</code> we need to define it in
our docker-compose file:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-docker" data-lang="docker"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span>version: <span style="color:#e6db74">&#34;3.2&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span>services:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span>  pihole:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span>    container_name: pihole<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span>    <span style="color:#75715e"># ...</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span>  dhcphelper:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span>    build: ./dhcp-helper<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span>    <span style="color:#75715e"># ...</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12</span><span>networks:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13</span><span>  backend:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14</span><span>    ipam:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15</span><span>      config:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16</span><span>        - subnet: 172.31.0.0/16</span></span></code></pre></div></div>
<p>With all these modifications, we end up with the following docker-compose file:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-docker" data-lang="docker"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span>version: <span style="color:#e6db74">&#34;3.2&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span>services:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span>  pihole:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span>    container_name: pihole<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span>    image: pihole/pihole:latest<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span>    ports:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span>      - <span style="color:#e6db74">&#34;53:53/tcp&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span>      - <span style="color:#e6db74">&#34;53:53/udp&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span>      - <span style="color:#e6db74">&#34;8080:80/tcp&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span>    environment:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12</span><span>      - TZ<span style="color:#f92672">=</span>America/New_York<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13</span><span>      - WEBPASSWORD<span style="color:#f92672">=</span>very-secure-password <span style="color:#75715e"># Choose a password for admin</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14</span><span>      - PIHOLE_DNS_<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;1.1.1.1;1.0.0.1&#34;</span> <span style="color:#75715e"># Set upstream DNS servers</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15</span><span>      - ServerIP<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;10.10.0.10&#34;</span> <span style="color:#75715e"># Your host&#39;s external IP</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16</span><span>    volumes:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17</span><span>      - pihole:/etc/pihole/<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18</span><span>      - dnsmasqd:/etc/dnsmasq.d/<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19</span><span>    restart: unless-stopped<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20</span><span>    depends_on:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21</span><span>      - dhcphelper<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22</span><span>    cap_add:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23</span><span>      - NET_ADMIN<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24</span><span>    networks:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25</span><span>      backend:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26</span><span>        ipv4_address: <span style="color:#e6db74">&#39;172.31.0.111&#39;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28</span><span>  dhcphelper:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29</span><span>    build: ./dhcp-helper<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30</span><span>    restart: unless-stopped<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31</span><span>    network_mode: <span style="color:#e6db74">&#34;host&#34;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32</span><span>    command: -s 172.31.0.111<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33</span><span>    cap_add:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34</span><span>      - NET_ADMIN<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36</span><span>networks:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37</span><span>  backend:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38</span><span>    ipam:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39</span><span>      config:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40</span><span>        - subnet: 172.31.0.0/16<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41</span><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">42</span><span>volumes:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">43</span><span>  pihole:<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">44</span><span>  dnsmasqd:</span></span></code></pre></div></div>

<h2 class="relative group">One More Thing
    <div id="one-more-thing" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#one-more-thing" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>So far, we&rsquo;ve managed to:</p>
<ol>
<li>Run a Pihole container with bridge network mode</li>
<li>Run a DHCP relay container that listens for discovery requests on the LAN and
forwards them to Pihole on Docker&rsquo;s internal network.</li>
</ol>
<p>However, by default Pihole sends out leases instructing clients to use its IP as
a DNS server. The IP used here is Pihole&rsquo;s IP address on the network where it
received the DHCP request. This means that the clients will be configured to
send their DNS queries to an address on the network <code>172.31.0.0/16</code> and this
will not work. This is an internal Docker network that we created to connect
Pihole to the DHCP relay, and that machines on the LAN have no access to. In
order to resolve this, Pihole needs to grant DHCP leases with the host&rsquo;s
<strong>external</strong> IP as the DNS. We can accomplish this by adding a configuration option
to <code>dnsmasq</code> running inside Pihole&rsquo;s container. This is as simple as creating a
file inside Pihole&rsquo;s container under <code>/etc/dnsmasq.d/</code> with the following:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>dhcp-option<span style="color:#f92672">=</span>option:dns-server,&lt;PIHOLE_HOST_EXTERNAL_IP&gt;</span></span></code></pre></div></div>
<p>To make our lives easier, let&rsquo;s put this in a script file called
<code>update-dhcp-dns</code> and also support multiple DNS servers while we&rsquo;re at it.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span><span style="color:#75715e">#!/bin/bash
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span>DNS_SERV_IPS<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;&#34;</span> <span style="color:#75715e"># A comma separated list of DNS IPs. E.g.: &#34;10.10.0.10,1.1.1.1,1.0.0.1&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span><span style="color:#66d9ef">if</span> <span style="color:#f92672">[[</span> -z $DNS_SERV_IPS <span style="color:#f92672">]]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span>    echo <span style="color:#e6db74">&#34;Please set DNS servers IPs by modifying the script file.&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span>    exit 1;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span><span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span>docker-compose exec pihole bash -c <span style="color:#e6db74">&#34;echo &#39;dhcp-option=option:dns-server,</span>$DNS_SERV_IPS<span style="color:#e6db74">&#39; &gt; /etc/dnsmasq.d/07-dhcp-options&#34;</span></span></span></code></pre></div></div>

<h2 class="relative group">Wrap up
    <div id="wrap-up" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wrap-up" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>We finally have all the pieces we need to run Pihole in a Docker container with
bridge network mode and have it serve DHCP requests on the host&rsquo;s LAN. After
updating <code>docker-compose.yml</code> and <code>update-dhcp-dns</code> with your desired
configuration values, like the web password, server IP, and DNS IPs, you can
deploy using the following commands.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>docker-compose up -d pihole
</span></span><span style="display:flex;"><span>./update-dhcp-dns</span></span></code></pre></div></div>
<p>Note that you only need to run <code>./update-dhcp-dns</code> once. The modifications
introduced by this script will persist across container restarts because we&rsquo;re
using a Docker volume in Pihole&rsquo;s service definition.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>If this environment variable is not set, Pihole will generate a random
password. In this case, you&rsquo;ll need to check the logs and search for the word
&ldquo;random&rdquo; in order to find the generated password.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>This is actually done in two steps: the client sends a lease request to
the server, then the server replies with an acknowledgement.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
<a href="https://ilye.ss/posts/pihole-dhcp-docker-bridge-network/#reply"> Leave a reply </a> | <a href="https://ilye.ss/guestbook/"> Sign my Guestbook </a>]]></content:encoded></item></channel></rss>