<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>RollUrOwn on Halley Adams | Blog</title><link>https://blog.halleyadams.uk/tags/rollurown/</link><description>Recent content in RollUrOwn on Halley Adams | Blog</description><generator>Hugo</generator><language>en-GB</language><lastBuildDate>Thu, 21 May 2026 11:00:00 +0000</lastBuildDate><atom:link href="https://blog.halleyadams.uk/tags/rollurown/index.xml" rel="self" type="application/rss+xml"/><item><title>PHP FPM</title><link>https://blog.halleyadams.uk/posts/292/</link><pubDate>Thu, 21 May 2026 11:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/292/</guid><description>&lt;p&gt;PHP-FPM is one of those things that quietly sits in the background of a Linux web server until it breaks, at which point you suddenly become very aware of it.&lt;/p&gt;
&lt;p&gt;At a basic level:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nginx handles the web requests&lt;/li&gt;
&lt;li&gt;PHP-FPM processes the PHP&lt;/li&gt;
&lt;li&gt;the browser gets the result&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nginx itself does not execute PHP. It hands the request off to PHP-FPM over a socket, PHP does its thing, then Nginx returns the output back to the client.&lt;/p&gt;</description></item><item><title>nginx Scripts and Tools</title><link>https://blog.halleyadams.uk/posts/291/</link><pubDate>Wed, 20 May 2026 11:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/291/</guid><description>&lt;p&gt;The source material covers four scripts: static compression, OCSP staple generation, TLS session key rotation, and Tor exit node list management. Two of those are no longer needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OCSP stapling&lt;/strong&gt; is handled automatically by nginx with &lt;code&gt;ssl_stapling on&lt;/code&gt; in the global TLS configuration. The complex script for pre-generating OCSP response files was needed before nginx handled this natively. It is not needed here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TLS session key rotation&lt;/strong&gt; was needed when &lt;code&gt;ssl_session_tickets on&lt;/code&gt; was used. This series disables session tickets (&lt;code&gt;ssl_session_tickets off&lt;/code&gt;) in favour of session cache. There are no ticket keys to rotate.&lt;/p&gt;</description></item><item><title>Content Security Policies</title><link>https://blog.halleyadams.uk/posts/290/</link><pubDate>Tue, 19 May 2026 19:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/290/</guid><description>&lt;p&gt;Content Security Policy (CSP) is a browser security feature that restricts what resources a web page can load and from where. A well-configured CSP prevents cross-site scripting (XSS) attacks by refusing to execute scripts, load stylesheets, or display images that do not come from approved sources.&lt;/p&gt;
&lt;p&gt;The source material covers CSP-Builder: a PHP/Composer tool for generating CSP headers from JSON files. This adds a dependency and a build step to what is essentially a single header line. This page writes CSP headers directly in nginx, which is simpler, more transparent, and does not require PHP or Composer on the web server.&lt;/p&gt;</description></item><item><title>Virtual Web Servers</title><link>https://blog.halleyadams.uk/posts/289/</link><pubDate>Tue, 19 May 2026 16:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/289/</guid><description>&lt;p&gt;Every website or service served by nginx is a virtual server: a &lt;code&gt;server {}&lt;/code&gt; block in a configuration file under &lt;code&gt;sites-available/&lt;/code&gt;. This page covers the structure of a virtual server configuration and shows three common patterns used throughout this series.&lt;/p&gt;
&lt;p&gt;The source material shows a static website with a Tor hidden service, dehydrated certificate paths, and TLS session ticket key rotation via a cron job marked &lt;code&gt;tbd&lt;/code&gt;. This page replaces all of that with current patterns using Let&amp;rsquo;s Encrypt certificates, the modern &lt;code&gt;http2&lt;/code&gt; directive, and the snippet approach established in the previous pages.&lt;/p&gt;</description></item><item><title>Extra Settings</title><link>https://blog.halleyadams.uk/posts/288/</link><pubDate>Tue, 19 May 2026 13:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/288/</guid><description>&lt;p&gt;These snippets are not included automatically. They are prepared configurations for specific situations: restricting access to internal networks, debugging, maintenance mode, PHP handling, and rate limiting for sensitive endpoints. Include them in specific virtual server or location blocks as needed.&lt;/p&gt;
&lt;p&gt;All files go in &lt;code&gt;/etc/nginx/snippets/&lt;/code&gt; alongside the snippets from the previous page.&lt;/p&gt;
&lt;h2 id="internal-access-only"&gt;Internal access only&lt;/h2&gt;
&lt;p&gt;The most commonly needed extra snippet: restricting a service so it is only accessible from the internal network, not the public internet.&lt;/p&gt;</description></item><item><title>Server Default Settings</title><link>https://blog.halleyadams.uk/posts/287/</link><pubDate>Tue, 19 May 2026 11:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/287/</guid><description>&lt;p&gt;The source material uses a &lt;code&gt;server-conf.d/&lt;/code&gt; directory that each virtual server includes. This series uses nginx&amp;rsquo;s &lt;code&gt;snippets/&lt;/code&gt; directory, which is the standard Ubuntu nginx location for reusable configuration fragments.&lt;/p&gt;
&lt;p&gt;Each virtual server includes the relevant snippets. Some snippets apply universally: security headers and file access restrictions. Others are applied selectively: browser cache control rules are included by static sites but not by reverse proxy configurations where the upstream backend controls caching.&lt;/p&gt;</description></item><item><title>Global HTTP Settings</title><link>https://blog.halleyadams.uk/posts/286/</link><pubDate>Mon, 18 May 2026 22:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/286/</guid><description>&lt;p&gt;The global HTTP settings live in &lt;code&gt;/etc/nginx/conf.d/&lt;/code&gt;. Every file in this directory is included by the &lt;code&gt;http {}&lt;/code&gt; block in &lt;code&gt;nginx.conf&lt;/code&gt; and applies to all virtual servers unless explicitly overridden. The source material uses numbered files in a custom &lt;code&gt;http-conf.d/&lt;/code&gt; directory. This series uses the standard &lt;code&gt;conf.d/&lt;/code&gt; directory with descriptive filenames.&lt;/p&gt;
&lt;h2 id="tcp-optimisation"&gt;TCP optimisation&lt;/h2&gt;
&lt;p&gt;Create &lt;code&gt;/etc/nginx/conf.d/tcp.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;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;"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# TCP/IP network optimisation
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# /etc/nginx/conf.d/tcp.conf
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Use sendfile() for efficient file transfer.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Default: off
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;sendfile&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;on&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Send response headers and file content in one packet.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Only effective when sendfile is enabled.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Default: off
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;tcp_nopush&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;on&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Send small packets immediately without buffering.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Most useful for keepalive connections.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Default: on
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;tcp_nodelay&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;on&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Reset timed-out connections, freeing RAM.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Default: off
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;reset_timedout_connection&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;on&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="tls-global-settings"&gt;TLS global settings&lt;/h2&gt;
&lt;p&gt;Create &lt;code&gt;/etc/nginx/conf.d/ssl.conf&lt;/code&gt;:&lt;/p&gt;</description></item><item><title>Main Configuration File</title><link>https://blog.halleyadams.uk/posts/285/</link><pubDate>Mon, 18 May 2026 19:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/285/</guid><description>&lt;p&gt;The main nginx configuration file lives at &lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt;. It sets the global parameters that apply to the entire nginx process: how many workers run, how many connections each can handle, where the PID file lives, and what else to include.&lt;/p&gt;
&lt;p&gt;The packaged nginx on Ubuntu ships with a reasonable default &lt;code&gt;nginx.conf&lt;/code&gt;. This page replaces the default with a configuration suited to the February homelab server and the modular file structure established in the configuration introduction.&lt;/p&gt;</description></item><item><title>nginx Configuration</title><link>https://blog.halleyadams.uk/posts/284/</link><pubDate>Mon, 18 May 2026 16:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/284/</guid><description>&lt;p&gt;nginx configuration is hierarchical and modular. A single top-level file includes everything else. Global settings apply to all virtual servers. Per-server settings override globals where needed. Individual site configurations are activated and deactivated by creating and removing symlinks.&lt;/p&gt;
&lt;p&gt;Understanding this structure before writing any configuration makes every subsequent page in this section easier to follow.&lt;/p&gt;
&lt;h2 id="configuration-hierarchy"&gt;Configuration hierarchy&lt;/h2&gt;
&lt;p&gt;nginx processes configuration from the top down. The main configuration file &lt;code&gt;nginx.conf&lt;/code&gt; sets global parameters and includes everything else. The included files add HTTP-level settings, TLS defaults, security headers, and finally the individual virtual server definitions.&lt;/p&gt;</description></item><item><title>nginx Installation</title><link>https://blog.halleyadams.uk/posts/283/</link><pubDate>Mon, 18 May 2026 13:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/283/</guid><description>&lt;p&gt;The source material this page replaces covers cloning the nginx source, installing build dependencies, compiling with custom flags, and packaging the result as a &lt;code&gt;.deb&lt;/code&gt; file. It is a thorough guide to building nginx from source on Ubuntu, and in 2016 when it was written there were genuine reasons to do so: the Ubuntu repositories lagged significantly behind upstream, and modules like &lt;code&gt;ngx_pagespeed&lt;/code&gt; had no packaged alternative.&lt;/p&gt;
&lt;p&gt;The situation in 2026 is different. The official nginx package repository provides current stable and mainline releases, updated within days of upstream. The modules needed for this setup are all present in the standard package. The maintenance overhead of a source build, manually tracking CVEs, rebuilding for OpenSSL patches, managing package conflicts, is no longer justified for a self-hosted homelab.&lt;/p&gt;</description></item><item><title>Web Server</title><link>https://blog.halleyadams.uk/posts/282/</link><pubDate>Mon, 18 May 2026 11:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/282/</guid><description>&lt;p&gt;nginx is the web server and reverse proxy for this network. Every service accessible via HTTPS, whether that is Nextcloud, SnappyMail, the ebook library, or the mail autoconfig endpoint, reaches the internet through nginx. It handles TLS termination, HTTP/2, security headers, rate limiting, and routing requests to the appropriate backend service.&lt;/p&gt;
&lt;p&gt;The source material installs nginx from source, which provides more control over compiled modules but significantly increases maintenance overhead. On Ubuntu 24.04 with regular security updates, the packaged version of nginx from the official nginx repository is current and well-maintained. This series installs from the package repository.&lt;/p&gt;</description></item><item><title>Mail Services Testing</title><link>https://blog.halleyadams.uk/posts/281/</link><pubDate>Sun, 17 May 2026 16:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/281/</guid><description>&lt;p&gt;A mail server has many moving parts. Postfix, Dovecot, rspamd, DNS records, TLS certificates, DKIM signing, and the PostfixAdmin database all have to work together correctly. Testing each component in isolation, then testing end-to-end delivery, gives confidence that the whole system is working before you rely on it for real mail.&lt;/p&gt;
&lt;p&gt;This page works through the tests in order: local delivery first, then SMTP, then IMAP, then TLS, then authentication, then external deliverability. Run each section after initial setup and repeat after any significant configuration change.&lt;/p&gt;</description></item><item><title>Mail Server Monitoring</title><link>https://blog.halleyadams.uk/posts/280/</link><pubDate>Sun, 17 May 2026 11:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/280/</guid><description>&lt;p&gt;A mail server that is silently failing is worse than one that is loudly broken. Silent failures accumulate: messages deferred for days before bouncing, spam filtering quietly blocking legitimate mail, a blacklisted IP sending every outbound message to the recipient&amp;rsquo;s spam folder. Monitoring catches these before users notice.&lt;/p&gt;
&lt;p&gt;The source material covers Munin graphs for Postfix. This series already has Grafana and InfluxDB running for IoT telemetry. Mail metrics fit naturally into the same stack. This page covers the full monitoring picture: daily log summaries, real-time queue monitoring, Grafana integration, and external deliverability health checks.&lt;/p&gt;</description></item><item><title>Backup MX Server</title><link>https://blog.halleyadams.uk/posts/279/</link><pubDate>Sat, 16 May 2026 11:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/279/</guid><description>&lt;p&gt;A backup MX server accepts inbound mail when the primary mail server is unreachable and queues it until the primary comes back online. Without one, sending mail servers retry delivery for a period (typically up to five days) before bouncing the message. With a backup MX, mail is accepted and queued locally, eliminating the risk of bounce during planned or unplanned downtime.&lt;/p&gt;
&lt;p&gt;For this three-site network, the backup MX opportunity is built into the existing architecture. The secondary sites each have a server capable of running a lightweight Postfix instance as a backup relay. Using one of the secondary site servers means the backup MX is geographically separated from the primary, connected via the inter-site WireGuard VPN, and already part of the managed infrastructure.&lt;/p&gt;</description></item><item><title>Mail DNS Records</title><link>https://blog.halleyadams.uk/posts/278/</link><pubDate>Fri, 15 May 2026 11:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/278/</guid><description>&lt;p&gt;DNS is the foundation of email deliverability. The mail server itself can be perfectly configured, but if the DNS records are missing or incorrect, outbound mail will be rejected or classified as spam by receiving servers, and inbound mail may not arrive at all.&lt;/p&gt;
&lt;p&gt;This page covers every DNS record needed for a self-hosted mail server: the required records that make basic delivery work, and the authentication records that tell the world your mail is legitimate.&lt;/p&gt;</description></item><item><title>Mail Client Auto-Configuration</title><link>https://blog.halleyadams.uk/posts/277/</link><pubDate>Thu, 14 May 2026 13:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/277/</guid><description>&lt;p&gt;When a user adds an email account to a mail client, the client tries to discover the correct server settings automatically before asking the user to enter anything. Mozilla&amp;rsquo;s autoconfig protocol and Microsoft&amp;rsquo;s Autodiscover protocol are the two mechanisms most clients support. Configure both and new mail clients configure themselves from an email address and password alone, with no manual server entry required.&lt;/p&gt;
&lt;p&gt;This is worth doing even for a personal mail server with one or two users. Adding a new device, a new phone, or a new desktop, takes thirty seconds rather than looking up server names and port numbers.&lt;/p&gt;</description></item><item><title>SnappyMail vs Roundcube</title><link>https://blog.halleyadams.uk/posts/275/</link><pubDate>Wed, 13 May 2026 16:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/275/</guid><description>&lt;p&gt;The source material uses RainLoop. RainLoop is abandoned and its community fork, SnappyMail, is the current equivalent. The other main option for self-hosted webmail is Roundcube. The two take different approaches and understanding the difference is worth a brief note before committing to either.&lt;/p&gt;
&lt;h2 id="rainloop--snappymail"&gt;RainLoop / SnappyMail&lt;/h2&gt;
&lt;p&gt;RainLoop was written to be fast and minimal. SnappyMail, its maintained fork, renders email significantly faster than Roundcube, uses less RAM, and has a cleaner default UI. PGP encryption is built in with no plugins needed.&lt;/p&gt;</description></item><item><title>Virtual Mailbox Administration</title><link>https://blog.halleyadams.uk/posts/273/</link><pubDate>Wed, 13 May 2026 11:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/273/</guid><description>&lt;p&gt;The source material this page replaces covers ViMbAdmin: a PHP-based virtual mailbox administration tool that was positioned as a modern alternative to PostfixAdmin. The situation has reversed. ViMbAdmin&amp;rsquo;s last commit was in 2022 and the project shows no signs of continued development. Installing abandoned software on a mail server is not a good idea. PostfixAdmin is actively maintained, has a large community, and integrates cleanly with Postfix and Dovecot.&lt;/p&gt;
&lt;p&gt;This page covers PostfixAdmin as the replacement for ViMbAdmin. The database schema is compatible with what the rest of the mail server section expects, and the Postfix and Dovecot query files reference the same table structure.&lt;/p&gt;</description></item><item><title>Server — Mail — MTA-STS</title><link>https://blog.halleyadams.uk/posts/271/</link><pubDate>Mon, 11 May 2026 11:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/271/</guid><description>&lt;p&gt;The Postfix TLS article configured TLS correctly on February&amp;rsquo;s end. MTA-STS, Mail Transfer Agent Strict Transport Security, is the mechanism that tells the rest of the internet to use it. Without MTA-STS, a sending server could silently downgrade from TLS to plaintext during an active attack, even if February is perfectly configured to accept TLS. MTA-STS closes that gap by publishing a policy that says: only deliver to this server over TLS, and verify the certificate.&lt;/p&gt;</description></item><item><title>Server — Mail — TLS Certificate</title><link>https://blog.halleyadams.uk/posts/270/</link><pubDate>Sun, 10 May 2026 19:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/270/</guid><description>&lt;p&gt;The Dovecot and Postfix TLS articles used a certificate signed by February&amp;rsquo;s internal CA. Mail clients connecting to Dovecot over IMAPS will see a certificate warning, because the internal CA is not in their trust store. Remote mail servers delivering to Postfix may log warnings or behave inconsistently, depending on how strictly they verify certificates. MTA-STS enforcement requires a publicly trusted certificate for the &lt;code&gt;mta-sts&lt;/code&gt; subdomain.&lt;/p&gt;
&lt;p&gt;A Let&amp;rsquo;s Encrypt certificate solves all of these problems. It is publicly trusted, free, and renews automatically. The 90-day certificate lifetime is short enough to limit exposure if a key is compromised, and certbot handles renewal without intervention. The only requirement is that the mail server&amp;rsquo;s hostname is publicly resolvable and that port 80 or 443 is accessible for the ACME challenge.&lt;/p&gt;</description></item><item><title>Server — Mail — MX and SPF</title><link>https://blog.halleyadams.uk/posts/269/</link><pubDate>Sun, 10 May 2026 16:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/269/</guid><description>&lt;p&gt;The Postfix article completed February&amp;rsquo;s mail stack at the application layer. The remaining work is in DNS. Without the right DNS records, inbound mail cannot find February, outbound mail will be rejected or marked as spam, and the reputation February builds as a sender is meaningless.&lt;/p&gt;
&lt;p&gt;This article covers two records: MX, which tells the internet where to deliver mail for your domain, and SPF, which tells the internet which servers are authorised to send mail from your domain. Both go into PowerDNS via &lt;code&gt;pdnsutil&lt;/code&gt; and, since DNSSEC is enabled on the zone, are automatically signed.&lt;/p&gt;</description></item><item><title>Server — Mail — Postfix</title><link>https://blog.halleyadams.uk/posts/268/</link><pubDate>Sun, 10 May 2026 13:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/268/</guid><description>&lt;p&gt;Every article in the mail sub-series has been building to this one. Postfix is the component everything else connects to: Dovecot hands it the auth socket, Rspamd attaches as a milter, Fastmail is the relay for outbound. Getting Postfix right means understanding what each of those connections is for and how the configuration reflects them.&lt;/p&gt;
&lt;p&gt;This article configures Postfix from installation through to a working send-and-receive mail setup. The Dovecot, Sieve, and Rspamd articles are prerequisites; this article assumes both are installed and their sockets are in place.&lt;/p&gt;</description></item><item><title>Server — Mail — Sieve</title><link>https://blog.halleyadams.uk/posts/267/</link><pubDate>Sun, 10 May 2026 11:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/267/</guid><description>&lt;p&gt;The Dovecot article configured Sieve as a plugin and dropped a minimal default script that sorts spam to a Junk folder. This article covers what Sieve actually is, how the script hierarchy works, what the language looks like, and how to build the more interesting part: using IMAPSieve to feed Rspamd training data when users move messages between folders.&lt;/p&gt;
&lt;h2 id="what-sieve-is"&gt;What Sieve is&lt;/h2&gt;
&lt;p&gt;Sieve is a mail filtering language defined in RFC 5228. It runs server-side, at delivery time, before mail reaches the user&amp;rsquo;s inbox. The key point is when it runs: not when the user&amp;rsquo;s mail client connects to retrieve mail, but at the moment Dovecot receives the message from Postfix via LMTP. A Sieve rule that sorts spam to Junk runs before the message ever appears in the inbox.&lt;/p&gt;</description></item><item><title>Server — Mail — Dovecot</title><link>https://blog.halleyadams.uk/posts/266/</link><pubDate>Sat, 09 May 2026 22:45:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/266/</guid><description>&lt;p&gt;Dovecot has two jobs in February&amp;rsquo;s mail setup. The first is to provide an IMAP server so mail clients can connect, authenticate, and read mailboxes. The second is to provide a local delivery agent for Postfix: rather than Postfix writing mail directly to disk, it hands incoming messages to Dovecot via LMTP, and Dovecot delivers them to the correct virtual mailbox. This arrangement lets Dovecot handle Sieve filtering at delivery time and gives Postfix a clean, simple delivery interface.&lt;/p&gt;</description></item><item><title>Server — Mail — Rspamd</title><link>https://blog.halleyadams.uk/posts/265/</link><pubDate>Sat, 09 May 2026 22:40:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/265/</guid><description>&lt;p&gt;The Razor, Pyzor, and DCC articles established SpamAssassin as the spam filtering foundation for February&amp;rsquo;s mail setup, with each collaborative tool adding a layer on top. Before going further, this article addresses a question that should have come earlier: is SpamAssassin the right choice for a new mail server deployment in 2026, or has the field moved on?&lt;/p&gt;
&lt;p&gt;The honest answer is that the field has moved on. Rspamd is now the default for new mail deployments, and understanding why helps clarify what decision February&amp;rsquo;s mail setup is actually making.&lt;/p&gt;</description></item><item><title>Fixing My Wardrobe</title><link>https://blog.halleyadams.uk/posts/264/</link><pubDate>Sat, 09 May 2026 22:35:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/264/</guid><description>&lt;p&gt;I don&amp;rsquo;t like my clothes anymore.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s not entirely new information. I wrote about it recently, the wardrobe post, the labels and the aesthetic I&amp;rsquo;m moving toward. But writing about what I want is easier than confronting what I actually have, and what I actually have is a rail of jeans and neutral tops that belong to a version of me I&amp;rsquo;m quietly in the process of leaving behind.&lt;/p&gt;
&lt;p&gt;I haven&amp;rsquo;t bought anything for a while. I think I stopped buying things when I stopped knowing what to buy, when the old defaults stopped feeling right and the new direction wasn&amp;rsquo;t yet clear enough to shop toward. That gap between knowing what you&amp;rsquo;re moving away from and knowing what you&amp;rsquo;re moving toward is an uncomfortable place to stand, and I&amp;rsquo;ve been standing in it for longer than I&amp;rsquo;d like.&lt;/p&gt;</description></item><item><title>Server — Mail — DCC</title><link>https://blog.halleyadams.uk/posts/263/</link><pubDate>Sat, 09 May 2026 22:30:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/263/</guid><description>&lt;p&gt;The previous two articles covered Razor and Pyzor: collaborative spam detection tools that check whether a message&amp;rsquo;s signature matches known spam in a shared database. DCC, the Distributed Checksum Clearinghouse, takes a meaningfully different approach. Understanding the distinction before installing anything makes the configuration decisions sensible rather than arbitrary.&lt;/p&gt;
&lt;h2 id="how-dcc-differs-from-razor-and-pyzor"&gt;How DCC differs from Razor and Pyzor&lt;/h2&gt;
&lt;p&gt;Razor and Pyzor ask: has this specific message been reported as spam? They compare a message&amp;rsquo;s hash against a database of known-bad messages. A hit means this exact content, or something very close to it, has been identified as spam by someone in the network.&lt;/p&gt;</description></item><item><title>Server — Mail — Pyzor</title><link>https://blog.halleyadams.uk/posts/260/</link><pubDate>Sat, 09 May 2026 22:15:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/260/</guid><description>&lt;p&gt;Pyzor is a collaborative spam detection network that operates on the same principle as Razor: it computes a hash of each message&amp;rsquo;s body and queries a distributed server to see whether that hash has been reported as spam. The two networks are independent and draw from different pools of reporters, so running both increases coverage. A spam campaign that slips past Razor may be caught by Pyzor, and vice versa.&lt;/p&gt;</description></item><item><title>Server — Mail — Razor</title><link>https://blog.halleyadams.uk/posts/259/</link><pubDate>Sat, 09 May 2026 22:10:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/259/</guid><description>&lt;p&gt;SpamAssassin scores incoming mail against a large rule set to determine how likely it is to be spam. Most of those rules are heuristic: they look at message structure, header patterns, and content characteristics that correlate with spam. Razor adds a different kind of check: it computes a signature of the message and queries a distributed network to see whether that exact message, or something very close to it, has already been reported as spam by other users.&lt;/p&gt;</description></item><item><title>Server — Mail — Virtual Domains</title><link>https://blog.halleyadams.uk/posts/258/</link><pubDate>Sat, 09 May 2026 22:05:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/258/</guid><description>&lt;p&gt;A fresh Postfix installation accepts mail for one domain: the server&amp;rsquo;s own hostname, set in &lt;code&gt;mydestination&lt;/code&gt;. That is enough for a server sending system notifications to a local user. It is not enough for a server that needs to receive mail for &lt;code&gt;yourdomain.com&lt;/code&gt;, serve it over IMAP to a mail client, and potentially receive mail for a second domain alongside the first.&lt;/p&gt;
&lt;p&gt;Virtual domains are Postfix&amp;rsquo;s mechanism for accepting mail for any number of domains beyond the server&amp;rsquo;s own hostname. Understanding how Postfix thinks about domains before configuring anything prevents the most common category of mistakes.&lt;/p&gt;</description></item><item><title>Server — Mail — Fastmail as a Relay</title><link>https://blog.halleyadams.uk/posts/257/</link><pubDate>Sat, 09 May 2026 22:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/257/</guid><description>&lt;p&gt;The mail introduction article explained why outbound mail from a homelab server needs an external relay: residential IP addresses have no sending reputation and will be treated with suspicion or rejected outright by major mail providers. The relay is the established sending infrastructure that bridges February&amp;rsquo;s outbound mail to the internet.&lt;/p&gt;
&lt;p&gt;Fastmail is a reasonable choice for this role if you already use it as your primary email provider. The case for using it as a relay rather than a dedicated transactional mail service like Mailgun or Brevo is straightforwardly pragmatic: you are already paying for it, you already trust it, and the Postfix relay configuration is a handful of lines. For low-volume sending from a homelab server, Fastmail&amp;rsquo;s SMTP infrastructure is more than sufficient.&lt;/p&gt;</description></item><item><title>Server — Mail — Introduction</title><link>https://blog.halleyadams.uk/posts/256/</link><pubDate>Sat, 09 May 2026 19:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/256/</guid><description>&lt;p&gt;Email is the oldest and most stubborn piece of internet infrastructure most people interact with daily. It predates the web, it predates DNS as most people understand it, and it has accumulated thirty years of layered standards, anti-spam measures, authentication protocols, and deliverability folklore. Running your own mail server means engaging with all of that.&lt;/p&gt;
&lt;p&gt;It is also the component of this series where the honest case for not doing it is strongest. Gmail, Fastmail, and similar providers handle deliverability, spam filtering, redundancy, and storage on your behalf in exchange for a monthly fee or your data. For many people that is the correct trade. Self-hosting mail makes sense when you want control over your own domain, when you are building infrastructure that needs to send and receive mail as part of its operation, or when the self-sufficiency argument that runs through this series extends to communication as well as compute.&lt;/p&gt;</description></item><item><title>Server — Redis</title><link>https://blog.halleyadams.uk/posts/255/</link><pubDate>Sat, 09 May 2026 16:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/255/</guid><description>&lt;p&gt;Most of the infrastructure on February was chosen without significant controversy. MariaDB is MariaDB. WireGuard is WireGuard. Borgbackup is Borgbackup. Redis is more complicated, because in March 2024 Redis Ltd. changed the licence on the Redis codebase from the BSD 3-Clause licence to a dual RSALv2/SSPL model, making it effectively proprietary for anyone offering it as a managed service. The response was significant: the Linux Foundation launched Valkey, a BSD-licenced fork of the last open-source Redis release, backed by AWS, Google Cloud, Oracle, Ericsson, and much of the original Redis contributor community.&lt;/p&gt;</description></item><item><title>Server — MariaDB — phpMyAdmin</title><link>https://blog.halleyadams.uk/posts/253/</link><pubDate>Sat, 09 May 2026 11:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/253/</guid><description>&lt;p&gt;The MariaDB command line is entirely sufficient for everything covered in this series. But the command line is not always the fastest tool for exploration: inspecting table structures, running ad-hoc queries, checking user permissions, or getting a quick overview of what is in a database. phpMyAdmin provides a web interface for those tasks. It is not elegant software, but it is functional, widely understood, and available as a Docker image that installs cleanly without touching February&amp;rsquo;s system PHP.&lt;/p&gt;</description></item><item><title>Server — MariaDB — Systemd</title><link>https://blog.halleyadams.uk/posts/252/</link><pubDate>Fri, 08 May 2026 23:45:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/252/</guid><description>&lt;p&gt;Every service on February runs under systemd. Most of the time that means you interact with it via &lt;code&gt;systemctl start&lt;/code&gt;, &lt;code&gt;systemctl stop&lt;/code&gt;, &lt;code&gt;systemctl status&lt;/code&gt;, and &lt;code&gt;journalctl&lt;/code&gt;. MariaDB is no different, but it is worth understanding a bit more about how the service unit is structured, what systemd does for it during startup and shutdown, and how to harden the unit without breaking anything.&lt;/p&gt;
&lt;h2 id="the-mariadb-service-unit"&gt;The MariaDB service unit&lt;/h2&gt;
&lt;p&gt;The unit file installed by the Ubuntu package lives at &lt;code&gt;/lib/systemd/system/mariadb.service&lt;/code&gt;. Do not edit this file directly: package upgrades will overwrite it. The right way to customise systemd unit files is with a drop-in override, covered later in this article.&lt;/p&gt;</description></item><item><title>Server — MariaDB — Replication</title><link>https://blog.halleyadams.uk/posts/251/</link><pubDate>Fri, 08 May 2026 23:30:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/251/</guid><description>&lt;p&gt;The binary log article introduced replication as one of two reasons the binary log exists, alongside point-in-time recovery. It then immediately noted that February has no replicas and moved on. This article is the part that got deferred: what replication actually is, how it works at a conceptual level, and what it would mean for February&amp;rsquo;s architecture if it were ever worth implementing.&lt;/p&gt;
&lt;p&gt;Nothing in this article needs to be done. It is context, not instructions.&lt;/p&gt;</description></item><item><title>Server — MariaDB — Backup</title><link>https://blog.halleyadams.uk/posts/250/</link><pubDate>Fri, 08 May 2026 23:15:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/250/</guid><description>&lt;p&gt;The borgmatic backup sub-series earlier in this collection covered February&amp;rsquo;s general backup strategy: borgmatic runs daily, backs up source directories to the NAS, and keeps a sensible retention history. The &lt;code&gt;/var/lib/mysql&lt;/code&gt; data directory was included in those source directories.&lt;/p&gt;
&lt;p&gt;That approach has a problem specific to databases. InnoDB is designed for concurrent access. At any given moment, some pages in the data directory may be mid-write, some transactions may be uncommitted, and the write-ahead log may contain changes that have not yet been flushed to the data files. Copying these files while MariaDB is running produces a snapshot that is internally inconsistent. MariaDB can recover from this on startup via crash recovery, but the result is a backup you cannot reliably restore to a specific known state.&lt;/p&gt;</description></item><item><title>Server — MariaDB — Users</title><link>https://blog.halleyadams.uk/posts/249/</link><pubDate>Fri, 08 May 2026 23:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/249/</guid><description>&lt;p&gt;The PowerDNS and PowerDNS Admin databases were created in the PowerDNS article with users that have &lt;code&gt;ALL PRIVILEGES&lt;/code&gt; on their respective databases. That is a reasonable starting point for getting things running quickly, but &lt;code&gt;ALL PRIVILEGES&lt;/code&gt; includes permissions those applications will never use: the ability to drop the database, create new tables, manage indexes, and change other users&amp;rsquo; permissions. This article covers the right way to structure user accounts for February&amp;rsquo;s applications, how to audit what already exists, and how to tighten the permissions that were created earlier in the series.&lt;/p&gt;</description></item><item><title>Server — MariaDB — Tuning</title><link>https://blog.halleyadams.uk/posts/248/</link><pubDate>Fri, 08 May 2026 22:55:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/248/</guid><description>&lt;p&gt;The configuration, InnoDB, Aria, and MyISAM articles established a baseline. The values there were chosen by reasoning about what February is and what it is not, rather than by measuring a running system. That reasoning is sound, but reasoning is not measurement. This article covers reading the status variables that tell you whether the configuration is working as intended, and using MySQLTuner to surface anything that reasoning missed.&lt;/p&gt;
&lt;p&gt;One important caveat before anything else: status variables reflect the server&amp;rsquo;s experience since the last restart, and they need time to accumulate meaningful data. Running a tuner tool on a freshly restarted server, or on one that has only been running for an hour, produces recommendations based on almost no real workload. Run these checks after February has been running under normal conditions for at least 24 hours, ideally longer.&lt;/p&gt;</description></item><item><title>Server — MariaDB — Aria</title><link>https://blog.halleyadams.uk/posts/246/</link><pubDate>Fri, 08 May 2026 22:45:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/246/</guid><description>&lt;p&gt;The previous two articles covered InnoDB and MyISAM: the default engine for application tables and the legacy engine it replaced. Aria is a third engine that sits somewhere between the two, and unlike MyISAM, it is genuinely in use on every MariaDB installation regardless of whether you have touched it.&lt;/p&gt;
&lt;h2 id="what-aria-is"&gt;What Aria is&lt;/h2&gt;
&lt;p&gt;Aria was developed by the MariaDB team as a crash-safe replacement for MyISAM. It shares MyISAM&amp;rsquo;s general approach — simpler than InnoDB, lower overhead, no row-level locking — but adds the crash recovery that MyISAM fundamentally lacks. Aria tables survive an unclean shutdown without corruption, because Aria maintains its own transaction log for recovery purposes.&lt;/p&gt;</description></item><item><title>Server — MariaDB — MyISAM</title><link>https://blog.halleyadams.uk/posts/245/</link><pubDate>Fri, 08 May 2026 22:40:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/245/</guid><description>&lt;p&gt;The previous article covered InnoDB: the default storage engine, the one all application tables use, the one with transactions, crash recovery, and row-level locking. MyISAM is the one that came before it. Understanding why InnoDB replaced it, where MyISAM still shows up on February, and what to do about it is a clean way to close out the storage engine topic.&lt;/p&gt;
&lt;h2 id="what-myisam-is"&gt;What MyISAM is&lt;/h2&gt;
&lt;p&gt;MyISAM was MySQL&amp;rsquo;s default storage engine from the beginning until version 5.5, when InnoDB took over. It predates most of the requirements we now take for granted in a database engine: transactions, foreign keys, crash recovery, row-level locking. It was designed for a simpler world and optimised for read-heavy workloads on hardware where RAM was scarce and disk was slow.&lt;/p&gt;</description></item><item><title>Server — MariaDB — InnoDB</title><link>https://blog.halleyadams.uk/posts/244/</link><pubDate>Fri, 08 May 2026 22:35:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/244/</guid><description>&lt;p&gt;Every table in February&amp;rsquo;s MariaDB databases is stored using InnoDB. You have been configuring it since the config article — the buffer pool size, the flush behaviour, the redo log — without a full picture of what those settings actually do. This article fills that gap.&lt;/p&gt;
&lt;p&gt;InnoDB is a storage engine: the component of MariaDB responsible for reading data from disk, writing it back, managing transactions, and recovering cleanly from crashes. It is not the only storage engine available, but it is the default and the right choice for every application running on February. Understanding how it works at a conceptual level makes the configuration settings legible and the log messages interpretable, which is the goal here.&lt;/p&gt;</description></item><item><title>Server — MariaDB — Binary Log</title><link>https://blog.halleyadams.uk/posts/243/</link><pubDate>Fri, 08 May 2026 22:30:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/243/</guid><description>&lt;p&gt;The borgmatic backup configuration from earlier in this series takes a daily snapshot of everything on February, including the MariaDB data directory. If February loses a drive tomorrow, you restore from last night&amp;rsquo;s backup and lose at most 24 hours of data. For most of what February stores, that is acceptable. PowerDNS zone data changes infrequently. ChirpStack device registrations change rarely. The loss window is bounded and manageable.&lt;/p&gt;
&lt;p&gt;The binary log changes that calculation. The binary log records every change made to every database in real time, as a sequence of events that can be replayed forward from any backup point. With a binary log running alongside the borgmatic backup, the recovery window shrinks from &amp;ldquo;since last backup&amp;rdquo; to &amp;ldquo;since the last binlog entry before the failure.&amp;rdquo; On a server that takes changes continuously, that can be the difference between losing hours of data and losing seconds.&lt;/p&gt;</description></item><item><title>Server — MariaDB — Log</title><link>https://blog.halleyadams.uk/posts/242/</link><pubDate>Fri, 08 May 2026 22:25:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/242/</guid><description>&lt;p&gt;MariaDB maintains several distinct logs and each one answers a different question. Treating them as interchangeable or ignoring all but one is how you end up knowing a database problem exists without having the information to diagnose it. This article covers the three logs that matter for February&amp;rsquo;s operational posture: the error log, the slow query log, and the audit log. The binary log is covered separately.&lt;/p&gt;
&lt;p&gt;Configuration for two of these was already set in the config article and touched on in the network article. This article brings everything together in one place, explains what each log actually contains, and covers how to read them usefully rather than just confirming they exist.&lt;/p&gt;</description></item><item><title>Server — MariaDB — TLS</title><link>https://blog.halleyadams.uk/posts/240/</link><pubDate>Fri, 08 May 2026 22:15:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/240/</guid><description>&lt;p&gt;Most MariaDB TLS articles assume you are running a database server that accepts remote connections. That is the scenario where TLS is genuinely critical: credentials and query results travelling across a network in plaintext is an obvious problem that encryption solves directly.&lt;/p&gt;
&lt;p&gt;February is not that scenario. MariaDB on February is bound to &lt;code&gt;127.0.0.1&lt;/code&gt;, accepts no connections from outside the machine, and most applications connect via the Unix socket rather than the TCP port at all. Traffic on the loopback interface and the local Unix socket does not leave the machine and cannot be intercepted by anything on the network.&lt;/p&gt;</description></item><item><title>Server — MariaDB — Network</title><link>https://blog.halleyadams.uk/posts/238/</link><pubDate>Fri, 08 May 2026 22:05:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/238/</guid><description>&lt;p&gt;The configuration article set &lt;code&gt;bind-address = 127.0.0.1&lt;/code&gt; and moved on. That single line does more security work than anything else in the MariaDB configuration. This article is about understanding why, verifying that it is doing what you expect, and adding the audit layer that tells you when something inside the machine is connecting to the database in a way you did not anticipate.&lt;/p&gt;
&lt;p&gt;The threat model for MariaDB on February is not an attacker scanning the internet for open port 3306. The network binding prevents that. The more realistic concerns are an application misconfiguration that opens an unintended connection path, a Docker container escaping its network constraints and reaching the host database, or a service running with broader database access than it needs. Auditing at the database level catches those things regardless of whether they came from outside or inside.&lt;/p&gt;</description></item><item><title>Server — MariaDB — Config</title><link>https://blog.halleyadams.uk/posts/236/</link><pubDate>Fri, 08 May 2026 19:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/236/</guid><description>&lt;p&gt;Most MariaDB configuration articles are written for dedicated database servers with 16 or 32 GB of RAM, handling hundreds of concurrent connections and thousands of queries per second. February is not that. February is a homelab server running MariaDB alongside PowerDNS, ChirpStack, Home Assistant, WireGuard, and everything else in this series. MariaDB is important infrastructure on February, but it is not the only thing running, and the configuration should reflect that.&lt;/p&gt;</description></item><item><title>Server — MariaDB — Install</title><link>https://blog.halleyadams.uk/posts/234/</link><pubDate>Fri, 08 May 2026 13:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/234/</guid><description>&lt;p&gt;If you followed the PowerDNS article in this series, MariaDB is already installed on February. The install command was covered there as a one-liner and the security hardening was not. This article fills that gap and is also the reference for anyone adding MariaDB to a fresh server from scratch.&lt;/p&gt;
&lt;p&gt;The version in Ubuntu 24.04&amp;rsquo;s default repository is MariaDB 10.11, the current long-term support release. That is the right version to use: it stays inside Ubuntu&amp;rsquo;s normal APT update flow, it has a support window through 2028, and it is what every other article in this series assumes. There is no reason to add the upstream MariaDB repository unless a specific feature from a newer release is actually needed.&lt;/p&gt;</description></item><item><title>Server — MariaDB — Introduction</title><link>https://blog.halleyadams.uk/posts/233/</link><pubDate>Fri, 08 May 2026 11:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/233/</guid><description>&lt;p&gt;MariaDB did not get its own article when it was first installed. It arrived as a prerequisite in the PowerDNS article, was set up in a few commands, and immediately put to work storing zone data. That is fine as far as it goes, but it undersells what is actually on February now.&lt;/p&gt;
&lt;p&gt;A running, properly configured MariaDB instance is not just a PowerDNS dependency. It is a piece of shared infrastructure that every service on February which needs relational storage can use. PowerDNS and PowerDNS Admin are already using it. Future services, including anything that needs structured persistent data, will use it too. It is worth treating it as a first-class component rather than an incidental installation.&lt;/p&gt;</description></item><item><title>Server — DNS — DANE</title><link>https://blog.halleyadams.uk/posts/232/</link><pubDate>Thu, 07 May 2026 23:45:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/232/</guid><description>&lt;p&gt;TLS has a trust problem that is easy to overlook until you think about it directly. When your browser connects to a site over HTTPS, it trusts the certificate because it was signed by one of the roughly 150 root Certificate Authorities your OS ships with. Any one of those CAs can issue a certificate for any domain. Most of them will not do so fraudulently. Some have, historically, either through compromise or negligence. The CA system works because it is good enough, not because it is watertight.&lt;/p&gt;</description></item><item><title>Server — DNS — SSH Server Keys</title><link>https://blog.halleyadams.uk/posts/231/</link><pubDate>Thu, 07 May 2026 23:30:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/231/</guid><description>&lt;p&gt;Every time you connect to an SSH server for the first time, OpenSSH presents you with a fingerprint and asks whether you trust it. The honest answer, almost always, is that you have no idea. You accept it anyway, it gets written to &lt;code&gt;~/.ssh/known_hosts&lt;/code&gt;, and from that point on OpenSSH trusts that server silently. This is the leap-of-faith model, and it works well enough in practice, but it is not verification in any meaningful sense. You are trusting whatever key happened to respond on that IP when you first connected.&lt;/p&gt;</description></item><item><title>PowerDNS IP Update</title><link>https://blog.halleyadams.uk/posts/230/</link><pubDate>Thu, 07 May 2026 23:15:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/230/</guid><description>&lt;p&gt;The REFUSED test at the end is worth actually running before publishing, since it is the clearest confirmation that the access control is doing what you think it is. An unsigned update that silently succeeds means the zone metadata was not applied correctly.&lt;/p&gt;
&lt;p&gt;The note about orphaned record cleanup at the end is intentionally left open. If that becomes a follow-on article in the series, the groundwork is laid. Ready for the next one.&lt;/p&gt;</description></item><item><title>PowerDNS Admin</title><link>https://blog.halleyadams.uk/posts/229/</link><pubDate>Thu, 07 May 2026 23:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/229/</guid><description>&lt;p&gt;PowerDNS is perfectly manageable from the command line. &lt;code&gt;pdnsutil add-record&lt;/code&gt;, &lt;code&gt;pdnsutil create-zone&lt;/code&gt;, &lt;code&gt;pdnsutil list-zone&lt;/code&gt; — all of it works, and for scripted or infrequent changes it is entirely sufficient. The problem appears when you want to make a quick DNS change while doing something else, or when you want to see the full state of your zones at a glance without piping output through grep. That is what PowerDNS Admin is for.&lt;/p&gt;</description></item><item><title>DNSSEC</title><link>https://blog.halleyadams.uk/posts/228/</link><pubDate>Thu, 07 May 2026 22:55:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/228/</guid><description>&lt;p&gt;DNSSEC is one of those topics that generates a lot of hand-waving about cryptographic signing and chain of trust without anyone explaining what problem it is actually solving or what it costs to enable it. This article covers both, and then walks through enabling it on February&amp;rsquo;s internal zones.&lt;/p&gt;
&lt;h2 id="what-dnssec-does"&gt;What DNSSEC does&lt;/h2&gt;
&lt;p&gt;DNS responses are unauthenticated by default. When you ask a resolver for the IP address of a hostname, there is no mechanism in the original DNS protocol to verify that the answer you receive is the same one the authoritative server sent. A response can be forged or modified in transit, and neither the client nor the resolver has any way to detect it. This is the DNS cache poisoning attack surface: feed a resolver a false answer, it caches it, and everyone who asks that resolver gets the false answer until the TTL expires.&lt;/p&gt;</description></item><item><title>PowerDNS Authoritative Server</title><link>https://blog.halleyadams.uk/posts/227/</link><pubDate>Thu, 07 May 2026 22:50:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/227/</guid><description>&lt;p&gt;Unbound handles recursive resolution and DNSSEC validation. PowerDNS handles the authoritative side: it holds the zone files for the internal domain and (as a hidden primary) for the public domain. When Unbound encounters a name under &lt;code&gt;internal.yourdomain.net&lt;/code&gt;, it forwards to PowerDNS on port 5300. When a public resolver anywhere in the world looks up a record for &lt;code&gt;yourdomain.net&lt;/code&gt;, it ultimately reaches the registrar&amp;rsquo;s nameservers, which get their data from PowerDNS via zone transfer.&lt;/p&gt;</description></item><item><title>Unbound Server</title><link>https://blog.halleyadams.uk/posts/225/</link><pubDate>Thu, 07 May 2026 22:40:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/225/</guid><description>&lt;p&gt;The DNS Introduction article established the architecture: Unbound handles recursive resolution and DNSSEC validation, PowerDNS handles authoritative answers for internal and public zones. This article installs and configures Unbound on the primary server.&lt;/p&gt;
&lt;p&gt;Once this is done, Unbound is listening on the server&amp;rsquo;s private IP address. Every device on the network points at it for DNS. External names resolve recursively with DNSSEC validation. Internal names forward to PowerDNS, which does not exist yet but is the next article.&lt;/p&gt;</description></item><item><title>DNS Introduction</title><link>https://blog.halleyadams.uk/posts/224/</link><pubDate>Thu, 07 May 2026 22:35:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/224/</guid><description>&lt;p&gt;Every name on your network resolves to an IP address through DNS. The mail server, the file server, the monitoring dashboard, the internal services that never touch the public internet: all of them are reachable by name only because something is doing the translation. For most homelab setups that something is the router, forwarding queries to whatever the ISP provided. This series replaces that with something more capable, more private, and more correct.&lt;/p&gt;</description></item><item><title>ClamAV</title><link>https://blog.halleyadams.uk/posts/222/</link><pubDate>Thu, 07 May 2026 22:25:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/222/</guid><description>&lt;p&gt;ClamAV on a Linux server is not the same proposition as antivirus on a Windows desktop. February is not going to be infected by a Windows ransomware payload. The threat model is different: the concern is that February might receive a malicious file through Nextcloud or the mail server and pass it on to someone running Windows, or that a file uploaded to Nextcloud by a user on a compromised machine gets shared to others before anyone notices.&lt;/p&gt;</description></item><item><title>I'm Going With MeshCore</title><link>https://blog.halleyadams.uk/posts/221/</link><pubDate>Thu, 07 May 2026 22:20:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/221/</guid><description>&lt;p&gt;I got into mesh networking through Meshtastic, wrote about it, got interested in MeshCore as an alternative, wrote about that too, and then watched the project go through a pretty public and unpleasant split right as I was getting started. I covered the drama in a previous post. This one is about what I decided to do after it.&lt;/p&gt;
&lt;p&gt;The short answer: I am going with MeshCore. Specifically, the team at meshcore.io. And I want to say a bit about why, because it is not purely a technical decision.&lt;/p&gt;</description></item><item><title>Certbot</title><link>https://blog.halleyadams.uk/posts/220/</link><pubDate>Thu, 07 May 2026 22:15:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/220/</guid><description>&lt;p&gt;The Dehydrated article covered the dns-01 challenge path: wildcard certificates, multi-domain certificates, services without port 80. Certbot covers the other case. For a service like a mail server that has a single public hostname, accepts SMTP connections, and does not run a web server of its own, certbot is the more straightforward tool.&lt;/p&gt;
&lt;p&gt;The distinction in this series is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dehydrated&lt;/strong&gt; manages wildcard and multi-domain certificates via dns-01 challenge, and handles reload hooks across multiple services.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Certbot&lt;/strong&gt; manages individual certificates for specific services, using HTTP-01 or the standalone authenticator when no web server is present.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both produce Let&amp;rsquo;s Encrypt certificates. The certificates are identical in format and can be used interchangeably.&lt;/p&gt;</description></item><item><title>Dehydrated</title><link>https://blog.halleyadams.uk/posts/219/</link><pubDate>Thu, 07 May 2026 22:10:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/219/</guid><description>&lt;p&gt;The Server TLS article covered certbot for obtaining Let&amp;rsquo;s Encrypt certificates via HTTP-01 challenge. That approach works well for any service that has a public DNS record and can respond on port 80. For most of what February exposes publicly, certbot is the right tool.&lt;/p&gt;
&lt;p&gt;Dehydrated is for the cases certbot cannot handle cleanly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Wildcard certificates&lt;/strong&gt; (&lt;code&gt;*.yourdomain.net&lt;/code&gt;) require dns-01 challenge rather than HTTP-01, because there is no single hostname to verify via a web request. Dehydrated&amp;rsquo;s hook system makes dns-01 automation straightforward.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Services without port 80 access&lt;/strong&gt;, such as the mail server which does not run a web server at all, or internal services that are reachable only via VPN.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multiple domains on one certificate&lt;/strong&gt; where managing a single certificate with hooks is cleaner than maintaining several certbot certificates.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both clients speak ACMEv2, both obtain certificates from Let&amp;rsquo;s Encrypt, and the certificates they produce are interchangeable. The choice between them is purely about which fits the deployment scenario better.&lt;/p&gt;</description></item><item><title>Server Ciphers</title><link>https://blog.halleyadams.uk/posts/218/</link><pubDate>Thu, 07 May 2026 22:05:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/218/</guid><description>&lt;p&gt;A TLS connection negotiates two things before any data flows: the protocol version (TLS 1.2, TLS 1.3) and the cipher suite (the specific algorithms used for key exchange, authentication, and bulk encryption). The server publishes a list of what it accepts. The client publishes what it supports. They agree on the best option they share.&lt;/p&gt;
&lt;p&gt;The problem is that &amp;ldquo;best option they share&amp;rdquo; depends entirely on what you put in the list. An old default might include cipher suites that have known weaknesses, or protocol versions that should have been retired years ago. This article sets the list correctly for February, once, as shared nginx configuration, so every service that runs behind nginx inherits it automatically.&lt;/p&gt;</description></item><item><title>Server TLS</title><link>https://blog.halleyadams.uk/posts/217/</link><pubDate>Thu, 07 May 2026 22:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/217/</guid><description>&lt;p&gt;Every service on February that accepts a connection needs a TLS certificate. The certificate is what lets a client verify it is talking to the right server, and what makes the connection encrypted. Without one, every connection is either plaintext or throwing up a browser warning that most people will click through without reading.&lt;/p&gt;
&lt;p&gt;The complication is that February hosts two different kinds of service: some are publicly accessible (a webmail interface, a Nextcloud instance, maybe a Kavita ebook server), and some are private to the homelab network (ChirpStack, Grafana, internal monitoring, MariaDB). These two categories need different certificates from different sources, and mixing them up creates problems that are annoying to unpick later.&lt;/p&gt;</description></item><item><title>Borgmatic Restore</title><link>https://blog.halleyadams.uk/posts/216/</link><pubDate>Thu, 07 May 2026 19:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/216/</guid><description>&lt;p&gt;A backup you have never tested is not a backup. It is a belief. You believe the files are there, you believe the encryption passphrase is correct, you believe the SSH key still works, you believe the archive contains what you think it contains. Until you actually pull something out and verify it, those are all assumptions.&lt;/p&gt;
&lt;p&gt;This article covers how to restore from a borgmatic backup on February: listing archives, browsing their contents, extracting individual files, extracting directories, and doing a full restore. It also covers the harder scenario: restoring to a new machine when the original is gone.&lt;/p&gt;</description></item><item><title>Server - Backup - Borgmatic Backup</title><link>https://blog.halleyadams.uk/posts/214/</link><pubDate>Thu, 07 May 2026 13:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/214/</guid><description>&lt;p&gt;The previous article installed Borg and borgmatic on February and set up the SSH key that connects to the NAS. This article configures borgmatic: writing the config file, initialising the repositories, and running the first backup manually to confirm everything works.&lt;/p&gt;
&lt;p&gt;The following article covers scheduling, so borgmatic runs automatically on a timer, and notifications, so you find out if something goes wrong.&lt;/p&gt;
&lt;h2 id="before-you-start"&gt;Before you start&lt;/h2&gt;
&lt;p&gt;Three things need to be in place before this article makes sense.&lt;/p&gt;</description></item><item><title>Server — Backup — Install</title><link>https://blog.halleyadams.uk/posts/212/</link><pubDate>Wed, 06 May 2026 23:45:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/212/</guid><description>&lt;p&gt;The previous article covered installing Borgbackup and borgmatic on February, writing the initial config, and running the first backup manually. This article covers the rest: running borgmatic automatically on a schedule, adding hooks so you know when a backup fails, and confirming the whole thing is working without you having to think about it.&lt;/p&gt;
&lt;p&gt;The goal by the end of this article is a backup that runs daily, logs to the journal, and sends a notification if it fails. That is the minimum bar for a backup setup you can actually rely on.&lt;/p&gt;</description></item><item><title>Server — Backup — Install</title><link>https://blog.halleyadams.uk/posts/211/</link><pubDate>Wed, 06 May 2026 23:30:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/211/</guid><description>&lt;p&gt;This article covers the February side of the backup setup. The NAS side, which involves installing Borgbackup on the receiving end, creating the backup user, and initialising the repository, is covered in the next article. The two articles are meant to be read together, but this one is written to stand alone so you can refer back to it without losing context.&lt;/p&gt;
&lt;p&gt;This assumes the NAS is already set up and reachable over SSH with a dedicated backup user and an initialised Borg repository. If that is not done yet, read the NAS article first and come back.&lt;/p&gt;</description></item><item><title>Server — Backup Introduction</title><link>https://blog.halleyadams.uk/posts/210/</link><pubDate>Wed, 06 May 2026 23:15:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/210/</guid><description>&lt;p&gt;At some point February will fail. The SD card will corrupt, a config change will go badly wrong, something will happen that leaves the server unbootable or the data inaccessible. This is not pessimism, it is the baseline assumption any honest backup strategy starts from. The question is not whether something will eventually go wrong. It is whether you will be able to recover when it does.&lt;/p&gt;
&lt;p&gt;This article is the introduction to a short series on how February&amp;rsquo;s backup strategy is structured. The implementation details come in follow-on articles. This one is about the thinking.&lt;/p&gt;</description></item><item><title>Server — WireGuard</title><link>https://blog.halleyadams.uk/posts/208/</link><pubDate>Wed, 06 May 2026 22:55:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/208/</guid><description>&lt;p&gt;The case for running your own VPN rather than buying one from a provider is simple: you know exactly what it does, because you built it. There is no privacy policy to read, no logging practices to trust, and no subscription to forget about. The traffic goes from your device to February and from there to wherever it was going. That is the entire chain.&lt;/p&gt;
&lt;p&gt;WireGuard is the right tool for this. It is fast, it is built into the Linux kernel, its configuration is minimal, and the client apps are available on every platform that matters. Setup on the server side takes about 20 minutes. Setup on the client side takes as long as it takes to scan a QR code.&lt;/p&gt;</description></item><item><title>Server — UPS</title><link>https://blog.halleyadams.uk/posts/207/</link><pubDate>Wed, 06 May 2026 22:50:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/207/</guid><description>&lt;p&gt;A UPS without software is just a battery with a beeper. It will keep your server running through a short outage, but it has no way to tell the server that the clock is running, and the server has no way to shut itself down cleanly before the battery gives out. When the battery eventually dies, the server loses power mid-write, the filesystem gets corrupted, and you spend the next hour with fsck trying to recover. The UPS bought you time and then wasted it.&lt;/p&gt;</description></item><item><title>Server — Serial Console</title><link>https://blog.halleyadams.uk/posts/206/</link><pubDate>Wed, 06 May 2026 22:45:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/206/</guid><description>&lt;p&gt;Every time you SSH into a headless server, you are relying on a chain of things that have to be working: the network, the SSH daemon, the operating system, the SD card, the configuration you wrote last time. When something in that chain breaks, SSH breaks with it. The Pi sits there, completely inaccessible, and you are left staring at a connection timeout with no idea what went wrong.&lt;/p&gt;
&lt;p&gt;The serial console breaks that dependency. It connects directly to the Pi&amp;rsquo;s hardware UART before the operating system is fully up, before the network is configured, before SSH has started. If the Pi is powered on and the bootloader is running, you can talk to it. If the kernel is panicking on startup, you will see exactly why. If you made a mistake in a config file that prevents the network from coming up, you can fix it without pulling the SD card.&lt;/p&gt;</description></item><item><title>Server Firewall</title><link>https://blog.halleyadams.uk/posts/205/</link><pubDate>Wed, 06 May 2026 22:40:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/205/</guid><description>&lt;p&gt;A server with no firewall is a server that trusts everyone equally. That sounds generous until you remember that the internet is full of automated scanners working their way through IP ranges, probing every port they can reach, looking for anything that responds. Your homelab server will be found. The question is what happens when it is.&lt;/p&gt;
&lt;p&gt;UFW is not the interesting part of server security. It is the boring, foundational part that makes the interesting parts possible. You configure it once, you make sure it is correct, and then you mostly stop thinking about it. This article is about getting to that point.&lt;/p&gt;</description></item><item><title>Server Network</title><link>https://blog.halleyadams.uk/posts/203/</link><pubDate>Wed, 06 May 2026 22:30:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/203/</guid><description>&lt;p&gt;The source material this page replaces covers systemd-networkd configuration on a bare Ubuntu server with LACP bonding. For this series, network configuration is handled by Proxmox&amp;rsquo;s own networking stack, which sits on top of Linux networking primitives but is configured via the Proxmox web interface and &lt;code&gt;/etc/network/interfaces&lt;/code&gt; rather than systemd-networkd unit files.&lt;/p&gt;
&lt;p&gt;This page covers two distinct networking layers: the Proxmox host itself, and the network configuration inside containers.&lt;/p&gt;
&lt;h2 id="proxmox-host-networking"&gt;Proxmox host networking&lt;/h2&gt;
&lt;p&gt;Proxmox uses traditional Linux networking via &lt;code&gt;ifupdown2&lt;/code&gt; rather than &lt;code&gt;systemd-networkd&lt;/code&gt;. All configuration lives in &lt;code&gt;/etc/network/interfaces&lt;/code&gt;. The Proxmox web interface edits this file when you make changes via the GUI.&lt;/p&gt;</description></item><item><title>LoRaWAN Classes Explained</title><link>https://blog.halleyadams.uk/posts/202/</link><pubDate>Wed, 06 May 2026 22:25:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/202/</guid><description>&lt;p&gt;One of the things that distinguishes LoRaWAN from simpler radio protocols is that it defines three different classes of device behaviour. Not all LoRaWAN devices are the same, and understanding why the classes exist helps you make better decisions when choosing sensors and designing a deployment.&lt;/p&gt;
&lt;p&gt;The classes are about one specific question: when is a LoRaWAN device able to receive a message?&lt;/p&gt;
&lt;p&gt;That question sounds simple until you think about what a battery-powered sensor is actually doing most of the time, which is nothing. It is asleep, conserving power, waking up periodically to take a reading and transmit it. The radio receiver consumes power when it is listening. Keeping the receiver on continuously would drain a battery in days. So the design question is: how do you get a message back to a device that is mostly asleep?&lt;/p&gt;</description></item><item><title>Server Additions</title><link>https://blog.halleyadams.uk/posts/201/</link><pubDate>Wed, 06 May 2026 22:20:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/201/</guid><description>&lt;p&gt;This page covers the additional configuration applied to the Proxmox host and the base container template after the core setup is complete: automatic updates, useful tools, login information, and the settings that make long-running unattended systems behave correctly.&lt;/p&gt;
&lt;h2 id="suspend-and-hibernate"&gt;Suspend and hibernate&lt;/h2&gt;
&lt;p&gt;The source material covers disabling suspend and hibernate for a server running on a laptop. The February server is a tower running Proxmox on bare metal, so suspend and hibernate should be disabled at the hypervisor level regardless.&lt;/p&gt;</description></item><item><title>SSH Server</title><link>https://blog.halleyadams.uk/posts/200/</link><pubDate>Wed, 06 May 2026 22:15:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/200/</guid><description>&lt;p&gt;SSH is the primary management interface for every server and container in this network. Getting the configuration right once, in the base container template, means every subsequent container inherits a hardened baseline without repetition.&lt;/p&gt;
&lt;p&gt;This page covers the SSH server configuration applied to the Proxmox host and the base container template, updated for OpenSSH 9.6p1 as shipped with Ubuntu 24.04 LTS.&lt;/p&gt;
&lt;h2 id="algorithm-selection"&gt;Algorithm selection&lt;/h2&gt;
&lt;p&gt;The source material covers algorithm selection in detail. The principles remain the same but some specifics have changed with OpenSSH 9.6p1 on Ubuntu 24.04.&lt;/p&gt;</description></item><item><title>Server Entropy</title><link>https://blog.halleyadams.uk/posts/199/</link><pubDate>Wed, 06 May 2026 22:10:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/199/</guid><description>&lt;p&gt;The source material this page replaces recommends installing &lt;code&gt;haveged&lt;/code&gt; and &lt;code&gt;pollinate&lt;/code&gt; to ensure sufficient random data for cryptographic operations. This was sound advice in 2014. In 2024, the situation has changed enough that following the original guidance without understanding the current context would install unnecessary software and potentially create a false sense of security.&lt;/p&gt;
&lt;p&gt;This page explains what actually changed, what the current recommendations are, and what, if anything, needs to be done on the February server.&lt;/p&gt;</description></item><item><title>Server Basic Configuration</title><link>https://blog.halleyadams.uk/posts/198/</link><pubDate>Wed, 06 May 2026 22:05:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/198/</guid><description>&lt;p&gt;The source material covers basic Ubuntu Server configuration: hostname, domain, resolver, timezone, and locale. In this network, these settings apply in two places: the Proxmox host itself, and the base Ubuntu 24.04 container template that every service container is built from.&lt;/p&gt;
&lt;p&gt;This page covers both. The Proxmox host configuration is done first, then the container template configuration which is applied once and inherited by every container provisioned from it.&lt;/p&gt;
&lt;h2 id="proxmox-host-basic-configuration"&gt;Proxmox host: basic configuration&lt;/h2&gt;
&lt;p&gt;Connect to the Proxmox host via SSH or use the shell in the web interface (node &amp;gt; Shell).&lt;/p&gt;</description></item><item><title>Server Installation</title><link>https://blog.halleyadams.uk/posts/197/</link><pubDate>Wed, 06 May 2026 22:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/197/</guid><description>&lt;p&gt;The source material this page replaces covers Ubuntu Server 14.04 installation in 24 screenshot steps. This series diverges at the most fundamental level: the server runs &lt;strong&gt;Proxmox VE 8.x&lt;/strong&gt;, not bare Ubuntu Server. Proxmox is the hypervisor layer. Ubuntu Server runs inside LXC containers and VMs on top of it, not on the bare metal.&lt;/p&gt;
&lt;p&gt;This page covers downloading and verifying the Proxmox ISO, creating the installation USB drive, and working through the Proxmox installer on the February hardware.&lt;/p&gt;</description></item><item><title>Server</title><link>https://blog.halleyadams.uk/posts/196/</link><pubDate>Wed, 06 May 2026 19:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/196/</guid><description>&lt;p&gt;The source material this page replaces is two sentences. The server section deserves considerably more than that, because the server is where most of the self-hosted infrastructure actually lives.&lt;/p&gt;
&lt;p&gt;The router handles packet routing, firewall, and VPN. The desktop handles day-to-day work. The server handles everything in between: DNS, mail, web services, file synchronisation, media streaming, database hosting, monitoring, backups, LoRaWAN network management, MQTT brokering, and every other service that needs to run continuously regardless of whether anyone is sitting at a desk.&lt;/p&gt;</description></item><item><title>Bridged Access Point</title><link>https://blog.halleyadams.uk/posts/194/</link><pubDate>Wed, 06 May 2026 13:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/194/</guid><description>&lt;p&gt;The source material this page replaces covers configuring an OpenWrt router as a dumb access point: disabling DHCP, disabling firewall, disabling WAN, and bridging the LAN interfaces so the device acts purely as a wireless extension of the main network. That approach is correct for commodity hardware running OpenWrt. For this network, the same goal is achieved differently depending on the hardware involved.&lt;/p&gt;
&lt;h2 id="unifi-access-points-automatic-adoption"&gt;UniFi access points: automatic adoption&lt;/h2&gt;
&lt;p&gt;If the additional access point is a UniFi device (U6 Lite, U6 Pro, U6 Enterprise, or any other UniFi AP), the configuration is almost entirely automatic. No DHCP configuration, no firewall changes, and no static routes need to be set manually because UniFi handles all of this through the controller.&lt;/p&gt;</description></item><item><title>Virtual Private Network</title><link>https://blog.halleyadams.uk/posts/193/</link><pubDate>Wed, 06 May 2026 11:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/193/</guid><description>&lt;p&gt;The source material this page replaces covers OpenVPN on OpenWrt. This series uses WireGuard throughout. WireGuard is built into the Linux kernel, natively supported by UniFi, significantly faster than OpenVPN, and considerably simpler to configure. There is no reason to use OpenVPN for new deployments.&lt;/p&gt;
&lt;p&gt;This network uses WireGuard for two distinct purposes:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Roaming client access:&lt;/strong&gt; connecting the Kubuntu desktop, phones, and laptops to the home network from anywhere. When away from the primary site, devices tunnel back through Prevernal and appear on the internal network as if they were local. The desktop WireGuard client configuration was covered in the Desktop Network section.&lt;/p&gt;</description></item><item><title>Firewall Web Services</title><link>https://blog.halleyadams.uk/posts/192/</link><pubDate>Tue, 05 May 2026 23:45:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/192/</guid><description>&lt;p&gt;The source material this page replaces covers HTTP and HTTPS port forwarding for a Calibre ebook content server, with the traffic rules section left entirely empty. This page covers the same ground properly: the port forwarding entries, the firewall rules, the HTTP-to-HTTPS redirect pattern, and the considerations that apply to any web service exposed to the public internet.&lt;/p&gt;
&lt;p&gt;The ebook library is the concrete example, but the pattern applies equally to any self-hosted web service: a Nextcloud instance, a Jellyfin server, a Bitwarden vault, a personal website, or any other HTTPS service.&lt;/p&gt;</description></item><item><title>Firewall Mail Server</title><link>https://blog.halleyadams.uk/posts/191/</link><pubDate>Tue, 05 May 2026 23:30:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/191/</guid><description>&lt;p&gt;Running a self-hosted mail server requires several ports to be accessible from the internet. Inbound mail delivery uses SMTP on port 25. Mail client access uses IMAPS on port 993 and SMTP submission on port 587. Each needs a port forwarding entry pointing at the mail server&amp;rsquo;s internal address, and a corresponding firewall rule permitting the inbound traffic.&lt;/p&gt;
&lt;p&gt;Equally important is blocking port 25 from devices other than the mail server. This is SMTP port management: without it, any device on the network with malware could attempt to send spam directly to external mail servers, bypassing the mail server entirely and potentially getting the network&amp;rsquo;s IP address blacklisted.&lt;/p&gt;</description></item><item><title>Firewall</title><link>https://blog.halleyadams.uk/posts/190/</link><pubDate>Tue, 05 May 2026 23:15:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/190/</guid><description>&lt;p&gt;The source material this page replaces is a bare list of two sub-pages covering port forwarding for specific services. There is no explanation of the firewall model, the default policies, or the reasoning behind the rules. This introduction covers all of that before getting into specific rules.&lt;/p&gt;
&lt;h2 id="unifi-firewall-model"&gt;UniFi firewall model&lt;/h2&gt;
&lt;p&gt;UniFi&amp;rsquo;s firewall is a stateful packet filter. It tracks the state of each connection and applies rules based on direction and origin, not just source and destination addresses. Understanding the direction model is the most important thing to get right before writing any rules.&lt;/p&gt;</description></item><item><title>Uninterruptible Power Supply</title><link>https://blog.halleyadams.uk/posts/189/</link><pubDate>Tue, 05 May 2026 23:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/189/</guid><description>&lt;p&gt;The source material this page replaces covers installing the NUT server on an OpenWrt router with the UPS connected directly to it via USB. In this network, the UPS is connected to the February homelab server, which runs as the NUT primary. The router, desktop, and NAS are all NUT secondary clients.&lt;/p&gt;
&lt;p&gt;This page covers the router&amp;rsquo;s role in the UPS shutdown sequence: how the UDM-SE connects to the NUT server on the homelab, receives power event notifications, and shuts down cleanly before the battery is exhausted.&lt;/p&gt;</description></item><item><title>Keeping the Router Updated</title><link>https://blog.halleyadams.uk/posts/188/</link><pubDate>Tue, 05 May 2026 22:55:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/188/</guid><description>&lt;p&gt;The source material this page replaces covers the OpenWrt upgrade process: downloading firmware images, verifying GPG signatures, transferring via SCP, and running &lt;code&gt;sysupgrade&lt;/code&gt;. For UniFi, the update process is handled through the web interface and requires significantly less ceremony. The discipline is not in the technical steps but in the habits around them: keeping backups current, reading release notes, and updating on a schedule rather than whenever prompted.&lt;/p&gt;
&lt;h2 id="unifi-update-channels"&gt;UniFi update channels&lt;/h2&gt;
&lt;p&gt;UniFi maintains two update channels:&lt;/p&gt;</description></item><item><title>Router Backup</title><link>https://blog.halleyadams.uk/posts/187/</link><pubDate>Tue, 05 May 2026 22:50:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/187/</guid><description>&lt;p&gt;The source material this page replaces covers an elaborate OpenWrt backup script using &lt;code&gt;opkg&lt;/code&gt;, &lt;code&gt;dropbear&lt;/code&gt;, &lt;code&gt;gnupg&lt;/code&gt;, and a custom cron job. For UniFi, the backup model is considerably simpler: the UniFi Network application maintains its own backup mechanism, and the configuration is stored in a portable &lt;code&gt;.unf&lt;/code&gt; file that can restore the entire network from scratch.&lt;/p&gt;
&lt;p&gt;The job here is not to build a backup system from scratch. It is to understand what UniFi backs up, configure automatic backups, and ensure copies land somewhere safe outside the device itself.&lt;/p&gt;</description></item><item><title>Router Mail</title><link>https://blog.halleyadams.uk/posts/186/</link><pubDate>Tue, 05 May 2026 22:45:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/186/</guid><description>&lt;p&gt;The source material this page replaces covers installing &lt;code&gt;ssmtp&lt;/code&gt; on OpenWrt to relay system mail from the router. &lt;code&gt;ssmtp&lt;/code&gt; is abandoned software and does not apply to UniFi OS in any case.&lt;/p&gt;
&lt;p&gt;For this network, system notifications reach you through two paths depending on their source:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UniFi OS alerts&lt;/strong&gt; are handled natively by the UniFi notification system. The UDM-SE sends alerts about network events, device state changes, and security events through the UniFi platform rather than via system mail.&lt;/p&gt;</description></item><item><title>DNS Resolver</title><link>https://blog.halleyadams.uk/posts/185/</link><pubDate>Tue, 05 May 2026 22:40:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/185/</guid><description>&lt;p&gt;The source material this page replaces covers installing Unbound directly on an OpenWrt router. For this network, the DNS resolver does not run on the router. It runs on the homelab server, which is always on, has more memory, and can handle the full Unbound configuration without the constraints of an embedded device.&lt;/p&gt;
&lt;p&gt;The router&amp;rsquo;s role in DNS is simpler: distribute the homelab server&amp;rsquo;s address as the DNS server via DHCP, so every device on every VLAN uses the correct resolver automatically. This page covers both: the DHCP DNS distribution on the router, and the Unbound configuration on the server.&lt;/p&gt;</description></item><item><title>LEDs and Buttons</title><link>https://blog.halleyadams.uk/posts/182/</link><pubDate>Tue, 05 May 2026 22:25:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/182/</guid><description>&lt;p&gt;The source material this page replaces covers the LED and button configuration of a NETGEAR WNDR3800 running OpenWrt, including custom &lt;code&gt;wifitoggle&lt;/code&gt; software and manual LED trigger configuration. The UDM-SE has a significantly different hardware design and all LED behaviour is managed by UniFi OS without requiring any custom configuration.&lt;/p&gt;
&lt;h2 id="udm-se-front-panel"&gt;UDM-SE front panel&lt;/h2&gt;
&lt;p&gt;The UDM-SE front panel has a single multi-colour LED ring around the UniFi logo, a USB-A port, and a power button.&lt;/p&gt;</description></item><item><title>Wireless Configuration</title><link>https://blog.halleyadams.uk/posts/180/</link><pubDate>Tue, 05 May 2026 22:15:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/180/</guid><description>&lt;p&gt;The source material this page replaces covers OpenWrt wireless configuration on 802.11n hardware from 2019. This page covers UniFi wireless configuration for a current deployment, updated for WPA3, WiFi 6, and the UniFi management model.&lt;/p&gt;
&lt;p&gt;The UDM-SE includes built-in WiFi radios, but for a rack-mounted device in a homelab, the built-in radios are rarely the primary wireless infrastructure. The more common and effective approach is deploying dedicated UniFi access points: ceiling or wall-mounted units that provide better coverage, more flexible placement, and independent radio hardware. This page covers the wireless configuration as it applies to both the built-in radios and external access points, since the configuration is identical from the UniFi perspective.&lt;/p&gt;</description></item><item><title>Network Configuration</title><link>https://blog.halleyadams.uk/posts/179/</link><pubDate>Tue, 05 May 2026 22:10:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/179/</guid><description>&lt;p&gt;The network design section established the addressing scheme and VLAN structure for all three sites. This page implements that design on Prevernal, the primary site gateway. The same steps apply to Vernal and Estival, substituting the appropriate address ranges for each site.&lt;/p&gt;
&lt;p&gt;All configuration in this page is done via the UniFi Network application at &lt;code&gt;https://10.1.0.1&lt;/code&gt; after completing the initial configuration steps.&lt;/p&gt;
&lt;h2 id="understanding-unifi-networks"&gt;Understanding UniFi networks&lt;/h2&gt;
&lt;p&gt;In UniFi terminology, a &lt;strong&gt;network&lt;/strong&gt; is a combination of a VLAN and a subnet. Creating a network in UniFi simultaneously creates:&lt;/p&gt;</description></item><item><title>Router - Initial Configuration</title><link>https://blog.halleyadams.uk/posts/177/</link><pubDate>Tue, 05 May 2026 22:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/177/</guid><description>&lt;p&gt;The setup wizard gets the UDM-SE onto the network and establishes a basic working configuration. This page covers the initial hardening and configuration that should happen immediately after: setting a strong admin password, configuring SSH access, setting the hostname, timezone, and NTP, and establishing the shell environment for command line work.&lt;/p&gt;
&lt;p&gt;These steps should be completed before moving on to network configuration, firewall rules, or VPN setup. Getting the foundations right first avoids having to revisit them later.&lt;/p&gt;</description></item><item><title>Router Installation</title><link>https://blog.halleyadams.uk/posts/175/</link><pubDate>Tue, 05 May 2026 16:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/175/</guid><description>&lt;p&gt;The source material this series draws on covers a pfSense-based router build. This network runs Ubiquiti UniFi hardware at all three sites. The router section covers the UniFi setup rather than pfSense.&lt;/p&gt;
&lt;p&gt;The primary router at Burnage Mad House is the &lt;strong&gt;UniFi Dream Machine SE&lt;/strong&gt; (UDM-SE), sometimes still referred to by its original name, the Dream Machine Pro SE. It is a 1U rack-mounted appliance that combines a security gateway, an 8-port PoE switch, a UniFi Network controller, a UniFi Protect NVR, and 128GB of integrated storage into a single device. For a homelab or small office with a rack, it is a clean solution that eliminates a dedicated controller machine.&lt;/p&gt;</description></item><item><title>Router Intro</title><link>https://blog.halleyadams.uk/posts/174/</link><pubDate>Tue, 05 May 2026 13:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/174/</guid><description>&lt;p&gt;The router is the most important piece of infrastructure in this network. Everything that connects to the internet passes through it. Every VLAN, every firewall rule, every VPN tunnel starts here. Getting this layer right is the foundation everything else builds on.&lt;/p&gt;
&lt;p&gt;The source material this series draws on covers OpenWrt running on a NETGEAR consumer router. This series diverges significantly: all three sites in this network run Ubiquiti UniFi hardware, and the approach reflects that choice throughout. Where the original covers command line configuration of OpenWrt, this series covers the UniFi web interface and the decisions behind each configuration.&lt;/p&gt;</description></item><item><title>Nextcloud Desktop Client</title><link>https://blog.halleyadams.uk/posts/173/</link><pubDate>Tue, 05 May 2026 11:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/173/</guid><description>&lt;p&gt;The Nextcloud desktop client keeps a local folder synchronised with your self-hosted Nextcloud server. Changes made on the desktop appear on every other device connected to the same server. Changes made on your phone, tablet, or another desktop appear on this one. The sync is bidirectional, continuous, and happens silently in the background.&lt;/p&gt;
&lt;p&gt;The server setup is covered in the server section of this series. This page covers the desktop client: installation, account configuration, Dolphin integration, selective sync, and the command line tools for scripted operations.&lt;/p&gt;</description></item><item><title>WebP Image Tools</title><link>https://blog.halleyadams.uk/posts/172/</link><pubDate>Mon, 04 May 2026 23:45:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/172/</guid><description>&lt;p&gt;WebP is an image format developed by Google that produces significantly smaller files than JPEG and PNG at equivalent visual quality. Lossless WebP images average 26% smaller than equivalent PNGs. Lossy WebP images average 25-34% smaller than equivalent JPEGs. For a self-hosted blog or website, the difference in page load times and storage costs is meaningful enough to be worth understanding.&lt;/p&gt;
&lt;p&gt;Kubuntu handles WebP natively in Brave, Gwenview, and most other modern applications. The tools on this page are for working with WebP from the command line: converting existing images, optimising files for web use, and batch processing large collections.&lt;/p&gt;</description></item><item><title>Managing Ubiquiti UniFi from the Desktop</title><link>https://blog.halleyadams.uk/posts/171/</link><pubDate>Mon, 04 May 2026 23:30:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/171/</guid><description>&lt;p&gt;The source material this page replaces covers Winbox, MikroTik&amp;rsquo;s proprietary management GUI. This network runs Ubiquiti UniFi at all three sites, so Winbox has no relevance here. The UniFi management story is quite different: a web-based interface that works in any browser, SSH access for advanced operations, and a set of command line tools for scripting and automation.&lt;/p&gt;
&lt;h2 id="the-unifi-web-interface"&gt;The UniFi web interface&lt;/h2&gt;
&lt;p&gt;The primary management interface for all three routers is the UniFi Network application, accessible via a web browser. Each router runs its own local instance:&lt;/p&gt;</description></item><item><title>Networking Tools</title><link>https://blog.halleyadams.uk/posts/170/</link><pubDate>Mon, 04 May 2026 23:15:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/170/</guid><description>&lt;p&gt;A self-hosted network is only as manageable as your ability to see what is happening on it. This page covers the networking tools worth having on the Kubuntu desktop: Wireshark for deep packet inspection, a set of indispensable command line utilities, and a few graphical tools that are genuinely useful rather than just elaborate wrappers around things the terminal already does better.&lt;/p&gt;
&lt;p&gt;Most of these tools have already appeared elsewhere in this series. This page collects them in one place as a reference.&lt;/p&gt;</description></item><item><title>Pomodoro Technique</title><link>https://blog.halleyadams.uk/posts/168/</link><pubDate>Mon, 04 May 2026 22:55:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/168/</guid><description>&lt;p&gt;The Pomodoro Technique was developed by Francesco Cirillo in the late 1980s. The idea is simple: work in focused 25-minute intervals, separated by 5-minute breaks. After four intervals, take a longer break of 15-30 minutes. Each interval is a pomodoro, named after the tomato-shaped kitchen timer Cirillo used as a student.&lt;/p&gt;
&lt;p&gt;It sounds almost too simple to be worth writing about. The reason it works is not magic: it is just that having a defined unit of work with a visible countdown changes the relationship between you and the task. The timer creates a mild, productive urgency. The breaks are enforced. You cannot defer the break any more than you can defer the end of the interval.&lt;/p&gt;</description></item><item><title>What is LoRa and LoRaWAN</title><link>https://blog.halleyadams.uk/posts/169/</link><pubDate>Mon, 04 May 2026 22:55:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/169/</guid><description>&lt;p&gt;If you have spent any time around IoT hardware, mesh networks, or amateur radio communities, you have encountered LoRa. You have probably also encountered LoRaWAN. And you have probably encountered people using both terms to mean the same thing, which they do not, and which causes genuine confusion when you are trying to understand how any of this actually works.&lt;/p&gt;
&lt;p&gt;So. Let us be clear about what these things are.&lt;/p&gt;</description></item><item><title>KDE Plasma Widgets</title><link>https://blog.halleyadams.uk/posts/167/</link><pubDate>Mon, 04 May 2026 22:50:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/167/</guid><description>&lt;p&gt;GNOME Shell extensions exist because GNOME is deliberately minimal and extensions are the primary mechanism for adding functionality. The source material this series draws on covers GSConnect (KDE Connect reimplemented for GNOME) and the browser integration needed to install extensions from extensions.gnome.org.&lt;/p&gt;
&lt;p&gt;On Kubuntu, neither of these is needed. KDE Connect is native and already covered in the Toolbox introduction. KDE Plasma&amp;rsquo;s extension equivalent is Plasmoids (widgets), which extend the panel and desktop, and KWin scripts, which extend the window manager. Both are already covered in the KDE Tweaks page.&lt;/p&gt;</description></item><item><title>KDE Plasma Tweaks</title><link>https://blog.halleyadams.uk/posts/166/</link><pubDate>Mon, 04 May 2026 22:45:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/166/</guid><description>&lt;p&gt;GNOME Tweaks exists because GNOME&amp;rsquo;s standard settings panel deliberately omits a large number of options. KDE Plasma does not have this problem: almost everything is in System Settings, which is significantly more comprehensive than any GNOME equivalent. There is no separate tweaks application needed.&lt;/p&gt;
&lt;p&gt;That said, some of the most useful KDE configuration is not obvious from browsing System Settings, and a handful of additional tools extend what the GUI exposes. This page covers the settings worth changing and the tools worth knowing about.&lt;/p&gt;</description></item><item><title>Uninterruptible Power Supply</title><link>https://blog.halleyadams.uk/posts/165/</link><pubDate>Mon, 04 May 2026 22:40:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/165/</guid><description>&lt;p&gt;Network UPS Tools (NUT) is a client/server system for monitoring UPS devices and coordinating safe shutdowns across multiple machines. The UPS is physically connected to one machine, which runs the NUT server and monitors the battery state. All other machines on the network run NUT clients. When the server detects a critical battery condition, it instructs all clients to shut down cleanly before the power cuts entirely.&lt;/p&gt;
&lt;p&gt;In this network&amp;rsquo;s architecture:&lt;/p&gt;</description></item><item><title>Sphinx Documentation</title><link>https://blog.halleyadams.uk/posts/164/</link><pubDate>Mon, 04 May 2026 22:35:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/164/</guid><description>&lt;p&gt;Sphinx is a documentation generator. It takes plain text source files written in reStructuredText or Markdown and produces HTML, PDF, ePub, and other output formats. It was originally created for the Python documentation and has since become the standard tool for technical documentation across a wide range of projects.&lt;/p&gt;
&lt;p&gt;This series is written in reStructuredText and built with Sphinx. Setting up Sphinx locally means you can preview the documentation as you write it, check for broken references, and build the full HTML site before publishing. The same setup works for any other Sphinx-based documentation project.&lt;/p&gt;</description></item><item><title>Drop-down Terminal</title><link>https://blog.halleyadams.uk/posts/163/</link><pubDate>Mon, 04 May 2026 22:30:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/163/</guid><description>&lt;p&gt;The concept comes from the Quake game engine, where pressing a key slides a console down from the top of the screen. Applied to a desktop terminal, it means you always have a shell one keypress away, without managing a terminal window in the taskbar, without switching workspaces, without breaking your current focus. Press the key, run a command, press the key again. Gone.&lt;/p&gt;
&lt;p&gt;The source material covers Guake, which is the GNOME implementation. On Kubuntu, the native KDE equivalent is Yakuake. Both work on KDE Plasma, but Yakuake integrates more cleanly: it uses the KDE terminal engine (Konsole), respects KDE themes, stores its configuration in the KDE config system, and does not pull in GTK dependencies. The recommendation for Kubuntu is Yakuake. Guake is documented below for completeness.&lt;/p&gt;</description></item><item><title>Sublime Text</title><link>https://blog.halleyadams.uk/posts/162/</link><pubDate>Mon, 04 May 2026 22:25:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/162/</guid><description>&lt;p&gt;The Toolbox section introduced VS Code as the primary editor for this setup. Sublime Text earns its place alongside it for different reasons. It starts instantly, handles very large files without complaint, uses considerably less memory than VS Code, and is particularly good for quick edits where opening a full IDE feels disproportionate. The two tools complement each other rather than compete.&lt;/p&gt;
&lt;p&gt;Sublime Text 4 is the current version. It is proprietary software with a perpetual licence: it can be evaluated indefinitely without purchase, with occasional nag prompts to buy. A licence is around $99 and covers all future updates within version 4.&lt;/p&gt;</description></item><item><title>Version Control</title><link>https://blog.halleyadams.uk/posts/161/</link><pubDate>Mon, 04 May 2026 22:20:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/161/</guid><description>&lt;p&gt;Git is the standard for version control and it is already installed on Kubuntu. This page is not a Git tutorial: it covers the specific configuration that makes Git work well with the rest of the desktop setup built in this series. GPG commit signing via the Yubikey, SSH authentication via the GPG agent, and the GitHub integration that ties everything together.&lt;/p&gt;
&lt;h2 id="installation"&gt;Installation&lt;/h2&gt;
&lt;p&gt;Git is available in the Ubuntu repositories. The version in the repositories is kept reasonably current:&lt;/p&gt;</description></item><item><title>Toolbox</title><link>https://blog.halleyadams.uk/posts/159/</link><pubDate>Mon, 04 May 2026 22:10:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/159/</guid><description>&lt;p&gt;This section covers the miscellaneous tools that any technically-oriented Kubuntu desktop accumulates: a text editor worth using, system monitoring tools, terminal utilities, KDE configuration tools, and a handful of command line utilities that make daily work more efficient. None of these are strictly necessary to follow the rest of this series, but all of them are worth knowing about.&lt;/p&gt;
&lt;h2 id="text-editor-visual-studio-code"&gt;Text editor: Visual Studio Code&lt;/h2&gt;
&lt;p&gt;The source material covers Sublime Text, which remains a capable editor. VS Code has largely replaced it as the default choice for most developers and system administrators, with better terminal integration, remote development support, and an extension ecosystem that covers everything from shell script linting to Terraform formatting.&lt;/p&gt;</description></item><item><title>3rd-party Software Package Sources</title><link>https://blog.halleyadams.uk/posts/158/</link><pubDate>Mon, 04 May 2026 22:05:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/158/</guid><description>&lt;p&gt;Over the course of this series, several software packages have been installed from third-party repositories rather than the standard Ubuntu repositories. This page documents all of them in one place: where each repository came from, why it was added, and how to manage or remove it if needed.&lt;/p&gt;
&lt;p&gt;The source material this series draws on uses the deprecated &lt;code&gt;apt-key adv --keyserver&lt;/code&gt; approach for adding repository keys. On Kubuntu 24.04, the correct approach is storing keys in &lt;code&gt;/usr/share/keyrings/&lt;/code&gt; as binary or ASCII-armored files and referencing them in the repository definition via the &lt;code&gt;signed-by&lt;/code&gt; option. All repositories documented here use the current method.&lt;/p&gt;</description></item><item><title>WireGuard VPN Client</title><link>https://blog.halleyadams.uk/posts/155/</link><pubDate>Mon, 04 May 2026 16:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/155/</guid><description>&lt;p&gt;WireGuard is built into the Linux kernel and natively supported by Ubiquiti UniFi. The setup is straightforward: configure the WireGuard server on Prevernal (the Burnage Mad House router), generate a client configuration file from the UniFi interface, and import it into NetworkManager on the desktop. Once done, connecting to the home network from anywhere is a single click.&lt;/p&gt;
&lt;p&gt;This page covers the complete workflow: server-side setup on UniFi and desktop client configuration on Kubuntu.&lt;/p&gt;</description></item><item><title>Media Services</title><link>https://blog.halleyadams.uk/posts/154/</link><pubDate>Mon, 04 May 2026 13:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/154/</guid><description>&lt;p&gt;The source material this series draws on describes the Linux DLNA situation as &amp;ldquo;far from perfect&amp;rdquo; and then presents three empty section headings. It was written in 2014. The situation has improved substantially since then, and the modern approach to media on a self-hosted network looks quite different from the DLNA-centric model it was building toward.&lt;/p&gt;
&lt;p&gt;This page covers the practical media stack for a Kubuntu desktop connected to a self-hosted network: local playback tools, Jellyfin for self-hosted media streaming, DLNA for UPnP device compatibility, and PipeWire for network audio.&lt;/p&gt;</description></item><item><title>PDF Documents</title><link>https://blog.halleyadams.uk/posts/152/</link><pubDate>Sun, 03 May 2026 23:59:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/152/</guid><description>&lt;p&gt;The source material this series draws on covers installing Adobe Reader 11 via Wine under a 32-bit Windows XP emulation layer. That approach has not been relevant since Adobe dropped Linux support in 2013 and stopped updating the Windows version meaningfully around the same time. Kubuntu&amp;rsquo;s native PDF toolchain handles everything Acrobat Reader did and quite a lot it never did.&lt;/p&gt;
&lt;h2 id="okular"&gt;Okular&lt;/h2&gt;
&lt;p&gt;Okular is the KDE document viewer and the default PDF reader on Kubuntu. It handles PDF, EPUB, DjVu, PostScript, and a range of other document formats in a single application.&lt;/p&gt;</description></item><item><title>Electronic Books</title><link>https://blog.halleyadams.uk/posts/151/</link><pubDate>Sun, 03 May 2026 23:55:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/151/</guid><description>&lt;p&gt;Calibre is the standard for e-book library management on Linux. It handles format conversion, metadata management, device syncing, and library organisation in a single application. For a self-hosted setup, the library lives in a directory that syncs to Nextcloud, making the collection accessible from any device on the network.&lt;/p&gt;
&lt;h2 id="installation"&gt;Installation&lt;/h2&gt;
&lt;p&gt;The version of Calibre in the Ubuntu repositories tends to lag behind the current release. The official installer from the Calibre website is the recommended approach:&lt;/p&gt;</description></item><item><title>Transmission BitTorrent Client</title><link>https://blog.halleyadams.uk/posts/150/</link><pubDate>Sun, 03 May 2026 23:50:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/150/</guid><description>&lt;p&gt;Running a BitTorrent client directly on a desktop has a couple of drawbacks. Torrents require the client to stay running for hours or days to seed properly. A desktop that gets switched off or suspended stops seeding. Storage on a desktop is also finite and less organised than a dedicated NAS.&lt;/p&gt;
&lt;p&gt;The better architecture is a Transmission daemon running permanently on the NAS, with the desktop connecting to it via the remote management interface when you want to add a torrent, check progress, or manage downloads. Downloads land directly on the NAS storage. Seeding continues regardless of whether the desktop is on. The desktop is just a control interface.&lt;/p&gt;</description></item><item><title>Instant Messaging</title><link>https://blog.halleyadams.uk/posts/149/</link><pubDate>Sun, 03 May 2026 23:45:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/149/</guid><description>&lt;p&gt;Instant messaging is one of the harder problems in the self-hosted space. The centralised services work well precisely because everyone is already on them. Replacing them requires either convincing the people you communicate with to move, or running something alongside your self-hosted infrastructure for the contacts who will not.&lt;/p&gt;
&lt;p&gt;This page covers three tools: Dino for XMPP-based messaging on your self-hosted server, Signal for encrypted messaging with contacts who will not move to XMPP, and Jitsi for video conferencing.&lt;/p&gt;</description></item><item><title>Wine</title><link>https://blog.halleyadams.uk/posts/148/</link><pubDate>Sun, 03 May 2026 23:40:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/148/</guid><description>&lt;p&gt;Wine is a compatibility layer that allows Windows applications to run on Linux. It translates Windows API calls into POSIX calls on the fly, meaning it is not a virtual machine or emulator. There is no Windows licence required and no full Windows installation to maintain. For Windows applications that have no Linux equivalent and no adequate substitute, Wine is often the cleanest solution.&lt;/p&gt;
&lt;p&gt;The landscape of needing Wine has narrowed considerably since the original source material for this series was written. Most things that required Wine in 2015 now have native Linux versions, web-based alternatives, or adequate open source substitutes. If you find yourself reaching for Wine, it is worth spending a few minutes first confirming there is genuinely no native option.&lt;/p&gt;</description></item><item><title>X Certificate and Key Management</title><link>https://blog.halleyadams.uk/posts/147/</link><pubDate>Sun, 03 May 2026 23:35:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/147/</guid><description>&lt;p&gt;XCA is a graphical application for creating and managing X.509 certificates, certificate requests, private keys, and certificate revocation lists. It wraps the same OpenSSL operations covered in the certificates section of this series in a GUI that makes the CA workflow considerably more navigable, particularly for operations that are done infrequently enough that the command line syntax is not committed to memory.&lt;/p&gt;
&lt;p&gt;XCA is not a replacement for the command line CA setup covered in the CA section of this series. It is a companion tool: useful for certificate inspection, CSR review, template-based certificate issuance, and maintaining a visual overview of the certificate hierarchy. For automated or scripted operations, the command line remains the right approach.&lt;/p&gt;</description></item><item><title>Safe Storage</title><link>https://blog.halleyadams.uk/posts/146/</link><pubDate>Sun, 03 May 2026 23:30:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/146/</guid><description>&lt;p&gt;The Yubikey LUKS section covered full disk encryption for the desktop itself. This page covers a different but related need: encrypted removable storage for sensitive material that needs to exist offline and physically separate from the machine.&lt;/p&gt;
&lt;p&gt;The primary use case in this series is the offline root CA key material. The root CA private key must never touch a network-connected machine, must be physically secured, and must be accessible only with a strong passphrase. An encrypted USB drive satisfies all of these requirements. The same approach works for GPG key backups, KeePassXC database backups, Borg repository key exports, and any other material that needs to exist offline in a form that cannot be read if the drive is lost or stolen.&lt;/p&gt;</description></item><item><title>Safe System</title><link>https://blog.halleyadams.uk/posts/145/</link><pubDate>Sun, 03 May 2026 23:25:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/145/</guid><description>&lt;p&gt;Kubuntu is the daily driver. It is well-configured, encrypted, and backed up. For most tasks it is the right tool. But there are a handful of situations where a persistent operating system, however well secured, is not appropriate.&lt;/p&gt;
&lt;p&gt;Managing the offline root CA key material. Working with highly sensitive documents on an unfamiliar machine. Accessing accounts from a network you do not control. Situations where you need certainty that nothing persists after the session ends, that all traffic goes through Tor, and that the operating system itself has not been compromised.&lt;/p&gt;</description></item><item><title>Mail Client</title><link>https://blog.halleyadams.uk/posts/144/</link><pubDate>Sun, 03 May 2026 23:20:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/144/</guid><description>&lt;p&gt;The source material this series draws on covers Thunderbird with a stack of extensions to add calendar support, CardDAV contacts, OpenPGP encryption, and Sieve filter management. Evolution includes all of these natively. It is a heavier application than a mail-only client, but for a self-hosted setup where the server provides IMAP, CalDAV, CardDAV, and Sieve, having everything in one well-integrated application is the cleaner arrangement.&lt;/p&gt;
&lt;p&gt;Evolution is a GNOME application. It runs well on KDE Plasma with no issues beyond using GTK styling rather than Qt. The functionality is unaffected by the desktop environment.&lt;/p&gt;</description></item><item><title>Web Browser</title><link>https://blog.halleyadams.uk/posts/143/</link><pubDate>Sun, 03 May 2026 23:15:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/143/</guid><description>&lt;p&gt;The source material this series draws on covers Firefox with a significant stack of privacy extensions. That approach is not wrong, but it represents a lot of configuration to achieve a baseline that Brave ships with out of the box. Brave is a Chromium-based browser with built-in ad blocking, fingerprint protection, HTTPS upgrades, and tracker blocking that work without any extension installation. For a self-hosted infrastructure setup where minimising attack surface and external dependencies matters, Brave is the more coherent choice.&lt;/p&gt;</description></item><item><title>Borgmatic User Data Backup</title><link>https://blog.halleyadams.uk/posts/142/</link><pubDate>Sun, 03 May 2026 23:10:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/142/</guid><description>&lt;p&gt;The user data backup covers the home directory. This is the backup that runs every day and protects everything that would be genuinely painful to lose: documents, code, configuration, keys, databases, years of accumulated work. It runs as your regular user account, not root, and targets a dedicated repository separate from the system configuration backup.&lt;/p&gt;
&lt;p&gt;The approach is the same as the system backup: BorgBackup for the repository format, borgmatic for automation, two destinations for redundancy.&lt;/p&gt;</description></item><item><title>System Configuration Backup</title><link>https://blog.halleyadams.uk/posts/140/</link><pubDate>Sun, 03 May 2026 23:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/140/</guid><description>&lt;p&gt;The system configuration backup covers everything that would be lost in a fresh install: &lt;code&gt;/etc&lt;/code&gt;, the boot configuration, locally installed software, package lists, custom scripts, and certificates. It does not cover user data (that is the next section) and it is not an image backup. The goal is to capture what is specific to this machine so that a fresh Kubuntu install plus a configuration restore gets back to a working state without having to reconstruct everything from memory.&lt;/p&gt;</description></item><item><title>Backup</title><link>https://blog.halleyadams.uk/posts/138/</link><pubDate>Sun, 03 May 2026 22:50:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/138/</guid><description>&lt;p&gt;Everything built in the secrets section of this series, GPG keys, SSH keys, certificates, KeePassXC databases, represents a significant investment of time and generates material that cannot be recreated if lost. The same is true of a home directory more broadly: documents, code, configuration, years of accumulated work. Disk failure, accidental deletion, ransomware, a stolen laptop: any of these can happen. The question is not whether to back up but how, and how to verify that the backup actually works when needed.&lt;/p&gt;</description></item><item><title>Anacron</title><link>https://blog.halleyadams.uk/posts/135/</link><pubDate>Sun, 03 May 2026 22:20:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/135/</guid><description>&lt;p&gt;Cron is designed for servers: machines that run continuously and can be relied upon to be awake at 3am when the daily backup is scheduled. A desktop is not a server. It gets switched off, put to sleep, closed, and generally ignored for stretches of time. A cron job scheduled for midnight on a machine that is off at midnight simply does not run. There is no catch-up.&lt;/p&gt;
&lt;p&gt;Anacron solves this. Rather than scheduling tasks at a specific time, anacron schedules them at a minimum frequency: once per day, once per week, once per month. Every time anacron runs, it checks when each job was last executed. If enough time has passed and the job has not run, it runs it. If the machine was off for three days, the daily job runs shortly after the next boot, not at the scheduled time that was missed.&lt;/p&gt;</description></item><item><title>Postfix Null Client</title><link>https://blog.halleyadams.uk/posts/134/</link><pubDate>Sun, 03 May 2026 22:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/134/</guid><description>&lt;p&gt;A desktop running Kubuntu generates mail. Cron jobs send output. System tools send alerts. Package managers notify of security updates. By default, all of this goes into a local mailbox that nobody reads, or gets dropped entirely because there is no mail transport configured. Neither is useful.&lt;/p&gt;
&lt;p&gt;A null client solves this cleanly. Postfix is configured in its simplest possible mode: listen only on localhost, accept no incoming mail, deliver nothing locally, and relay everything to your mail server. System mail from the desktop lands in your inbox alongside everything else.&lt;/p&gt;</description></item><item><title>SSH Client Configuration</title><link>https://blog.halleyadams.uk/posts/133/</link><pubDate>Sun, 03 May 2026 19:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/133/</guid><description>&lt;p&gt;OpenSSH ships with sensible defaults, but a properly configured &lt;code&gt;~/.ssh/config&lt;/code&gt; file is one of those things that pays back the time invested every single day. Instead of typing long connection strings with ports, keys, and options, you type a hostname and everything else is handled automatically. Agent forwarding, GPG agent forwarding, jump hosts, non-standard ports, per-host key selection: all of it configured once and forgotten.&lt;/p&gt;
&lt;p&gt;This page covers the desktop SSH client configuration for a setup with multiple servers across three sites, GPG agent forwarding to servers, and Yubikey-based authentication.&lt;/p&gt;</description></item><item><title>WireGuard VPN Client</title><link>https://blog.halleyadams.uk/posts/132/</link><pubDate>Sun, 03 May 2026 16:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/132/</guid><description>&lt;p&gt;WireGuard is the right VPN protocol for this network. It is built into the Linux kernel, has minimal attack surface compared to OpenVPN or IPsec, uses modern cryptography by default, and roams cleanly between networks without dropping connections. On Kubuntu it integrates natively with NetworkManager, which means connecting and disconnecting is a single click or keyboard shortcut rather than a terminal command.&lt;/p&gt;
&lt;p&gt;This page covers the desktop client configuration. The server-side WireGuard setup, running on the Ubiquiti UniFi routers at each site, is covered in the router section of this series. This page assumes that the server-side configuration is in place and you have a client configuration file ready.&lt;/p&gt;</description></item><item><title>Dynamic DNS</title><link>https://blog.halleyadams.uk/posts/131/</link><pubDate>Sun, 03 May 2026 13:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/131/</guid><description>&lt;p&gt;Servers get static addresses and permanent DNS records. Desktops do not. A desktop or laptop connected to the Core VLAN gets whatever address the DHCP server decides to give it, and that address may change between connections. For most purposes this does not matter: the machine can reach the network regardless of its address, and most traffic originates outbound rather than inbound.&lt;/p&gt;
&lt;p&gt;Where it does matter is when you want to reach your desktop by hostname from elsewhere on the network. Logging in via SSH from a server, connecting a remote desktop session, or any other scenario where something else needs to find your machine requires a DNS record that reflects the current address. Dynamic DNS handles this: whenever the desktop&amp;rsquo;s address changes, it updates its own DNS record automatically.&lt;/p&gt;</description></item><item><title>Unbound, Local DNS Resolver</title><link>https://blog.halleyadams.uk/posts/130/</link><pubDate>Sun, 03 May 2026 11:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/130/</guid><description>&lt;p&gt;The network section introduced Unbound as the local DNS resolver for the desktop. This page covers the full configuration in detail: what Unbound is doing, how it handles DNSSEC validation, how it forwards internal domain queries to your own DNS server, and how it integrates with NetworkManager so the right resolvers are used on every network you connect to.&lt;/p&gt;
&lt;p&gt;The source material this series draws on uses &lt;code&gt;dnssec-trigger&lt;/code&gt; alongside Unbound to test upstream resolvers and reconfigure Unbound dynamically. That approach is not recommended here. &lt;code&gt;dnssec-trigger&lt;/code&gt; has a known crashing bug, is effectively unmaintained, and adds significant complexity for limited benefit. The configuration below uses Unbound directly, with a NetworkManager dispatcher script handling the split DNS behaviour that &lt;code&gt;dnssec-trigger&lt;/code&gt; was responsible for.&lt;/p&gt;</description></item><item><title>Network Time Synchronisation</title><link>https://blog.halleyadams.uk/posts/129/</link><pubDate>Sat, 02 May 2026 22:40:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/129/</guid><description>&lt;p&gt;Time synchronisation is one of those things that works silently in the background until it does not, at which point it causes failures in places that do not obviously relate to the clock. TLS handshakes fail because a certificate appears to be from the future. Log correlation becomes impossible because two machines disagree on when something happened. Kerberos authentication breaks entirely. Getting this right early and doing it properly is worth the small amount of effort it takes.&lt;/p&gt;</description></item><item><title>Desktop Network Configuration</title><link>https://blog.halleyadams.uk/posts/127/</link><pubDate>Sat, 02 May 2026 22:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/127/</guid><description>&lt;p&gt;A desktop connected to a well-designed network should be doing more than just getting a DHCP lease and using the ISP&amp;rsquo;s DNS resolvers. It should be on the correct VLAN for its purpose, using a local DNS resolver that validates DNSSEC, and capable of reaching all three sites in the network through the VPN. This page covers getting from a freshly installed Kubuntu desktop to that state.&lt;/p&gt;
&lt;p&gt;The network design section established the addressing scheme. This page assumes you are working from one of the three sites and connecting to the relevant core VLAN.&lt;/p&gt;</description></item><item><title>Backup Yubikey</title><link>https://blog.halleyadams.uk/posts/126/</link><pubDate>Sat, 02 May 2026 19:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/126/</guid><description>&lt;p&gt;This page exists because losing your only Yubikey is genuinely unpleasant. Depending on how far through this series you have gone, a lost or damaged Yubikey without a backup could mean: no SSH access to your servers, no GPG signing or decryption, no LUKS disk unlock without a recovery key, and no desktop login if PAM is configured to require the key. That is a bad afternoon.&lt;/p&gt;
&lt;p&gt;The solution is simple and should have been in place before you moved any key material to hardware: a second Yubikey, configured identically to the primary, stored somewhere physically separate. This page covers how to set one up.&lt;/p&gt;</description></item><item><title>Yubikey PIV / Smartcard</title><link>https://blog.halleyadams.uk/posts/125/</link><pubDate>Sat, 02 May 2026 16:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/125/</guid><description>&lt;p&gt;PIV, Personal Identity Verification, is a standard defined by NIST (SP 800-73) for smartcard-based cryptography. The Yubikey 5 implements PIV alongside OpenPGP, giving it two separate cryptographic identities that coexist on the same hardware. Where the OpenPGP interface was covered in the GPG section and is used for email, file encryption, and SSH via the GPG agent, PIV uses the PKCS#11 interface and is better suited to X.509 certificate operations, client certificate authentication, and integration with systems that speak standard smartcard protocols.&lt;/p&gt;</description></item><item><title>SSH Authentication with Yubikey</title><link>https://blog.halleyadams.uk/posts/123/</link><pubDate>Sat, 02 May 2026 11:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/123/</guid><description>&lt;p&gt;The GPG SSH section earlier in this series covered using your GPG authentication subkey for SSH connections via the GPG agent. This page covers what changes when that authentication subkey is stored on a Yubikey rather than on disk.&lt;/p&gt;
&lt;p&gt;The short version: almost nothing changes from the user&amp;rsquo;s perspective. SSH still connects the same way. The GPG agent still handles the authentication handshake. The difference is that the agent now delegates the actual cryptographic operation to the Yubikey rather than performing it in software. The private key never touches RAM. Every SSH connection requires the physical card.&lt;/p&gt;</description></item><item><title>GnuPG with Yubikey</title><link>https://blog.halleyadams.uk/posts/122/</link><pubDate>Fri, 01 May 2026 22:40:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/122/</guid><description>&lt;p&gt;The Yubikey 5 implements the OpenPGP card specification, giving it three dedicated slots for GPG key material: one for signing, one for encryption, and one for authentication. Once your subkeys are moved onto the card, all GPG operations use the hardware rather than key files on disk. The private key material never leaves the card. Signing a message, decrypting a file, authenticating an SSH connection, all of these require the Yubikey to be physically present.&lt;/p&gt;</description></item><item><title>Linux Login with Yubikey</title><link>https://blog.halleyadams.uk/posts/121/</link><pubDate>Fri, 01 May 2026 22:20:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/121/</guid><description>&lt;p&gt;PAM, Pluggable Authentication Modules, is the system Linux uses to handle authentication for logins, sudo, screen unlock, and most other local authentication events. By adding a Yubikey to the PAM configuration, you require physical presence of the key in addition to a password for any of these operations. An attacker with your password but not your Yubikey cannot log in, unlock the screen, or escalate privileges.&lt;/p&gt;
&lt;p&gt;This page uses &lt;code&gt;pam-u2f&lt;/code&gt;, the U2F/FIDO2 PAM module from Yubico. It is the modern, maintained approach and works with all Yubikey 5 series devices without consuming a Yubikey slot.&lt;/p&gt;</description></item><item><title>LUKS Disk Encryption with Yubikey</title><link>https://blog.halleyadams.uk/posts/120/</link><pubDate>Fri, 01 May 2026 22:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/120/</guid><description>&lt;p&gt;Full disk encryption protects your data at rest. A strong LUKS passphrase is a reasonable first line of defence, but a passphrase alone has limitations: it can be observed, guessed, or extracted from a compromised system. Adding a Yubikey to the unlock process means the disk requires both something you know (the passphrase or PIN) and something you have (the physical key). Without the Yubikey present at boot, the drive will not open regardless of what passphrase is entered.&lt;/p&gt;</description></item><item><title>Yubikey</title><link>https://blog.halleyadams.uk/posts/119/</link><pubDate>Fri, 01 May 2026 19:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/119/</guid><description>&lt;p&gt;Everything in the GPG section so far has kept private keys on disk, protected by a passphrase. That is a reasonable security model for most threat levels, but it has a fundamental limitation: the key material exists as a file. A file can be copied. A compromised machine, a malicious process with access to your home directory, a poorly secured backup, any of these can result in a private key being exfiltrated silently. You might never know it happened.&lt;/p&gt;</description></item><item><title>OpenPGP Applications and Tools</title><link>https://blog.halleyadams.uk/posts/118/</link><pubDate>Fri, 01 May 2026 16:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/118/</guid><description>&lt;p&gt;GnuPG is a command line tool and always will be. Everything covered in the previous sections has been terminal-first, which is appropriate for a setup that involves servers, automation, and scripting. But day-to-day use of GPG on a desktop, managing keys, signing files, encrypting documents, is more comfortable with a graphical interface, and on Kubuntu there are good options that integrate properly with the KDE environment.&lt;/p&gt;
&lt;p&gt;This page covers the tools worth knowing about: graphical frontends, useful command line utilities, and how they fit together.&lt;/p&gt;</description></item><item><title>Using OpenPGP on Remote Systems</title><link>https://blog.halleyadams.uk/posts/117/</link><pubDate>Fri, 01 May 2026 13:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/117/</guid><description>&lt;p&gt;As the number of servers in this setup grows, you will increasingly find yourself needing GPG operations on remote machines. Signing Git commits. Decrypting configuration files. Running scripts that need access to encrypted credentials. The naive solution is to copy your private key to each server. This is the wrong solution. Every copy of a private key is an additional attack surface, and a server is inherently more exposed than a desktop that only you use.&lt;/p&gt;</description></item><item><title>SSH Authentication with OpenPGP</title><link>https://blog.halleyadams.uk/posts/115/</link><pubDate>Thu, 30 Apr 2026 22:40:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/115/</guid><description>&lt;p&gt;The GPG agent can act as an SSH agent, using your GPG authentication subkey to handle SSH connections. This means instead of managing separate SSH keys alongside your GPG keys, your authentication subkey covers both. When combined with a Yubikey, which stores the private key on hardware, this becomes a particularly clean arrangement: a single physical device handles GPG operations and SSH authentication across every server in your network.&lt;/p&gt;
&lt;p&gt;This page covers the software-only setup. The Yubikey integration is covered in the Yubikey section.&lt;/p&gt;</description></item><item><title>Distributing OpenPGP Keys</title><link>https://blog.halleyadams.uk/posts/114/</link><pubDate>Thu, 30 Apr 2026 22:20:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/114/</guid><description>&lt;p&gt;A public key that nobody can find is not very useful. The point of distributing your public key is to allow others to send you encrypted mail, verify your signatures, and establish trust relationships with you. There are several ways to do this, each with different trade-offs around privacy, reliability, and ease of discovery. This page covers them all, from the simplest to the most robust.&lt;/p&gt;
&lt;p&gt;Before distributing anything, make sure your key is in a clean state: correct UIDs, a photo if you want one, and subkeys properly configured. Once a key is on a public key server it is effectively permanent. You cannot delete it, only revoke it.&lt;/p&gt;</description></item><item><title>Managing OpenPGP Keys</title><link>https://blog.halleyadams.uk/posts/113/</link><pubDate>Thu, 30 Apr 2026 22:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/113/</guid><description>&lt;p&gt;This is the most important step in the GPG section. The decisions made here, key type, key size, subkey structure, expiry dates, have long-term consequences. A poorly generated key is not something you quietly fix later. It is something you either live with or revoke and start over. Take the time to understand what you are generating before generating it.&lt;/p&gt;
&lt;h2 id="before-you-start"&gt;Before you start&lt;/h2&gt;
&lt;p&gt;Two things need to be in place before generating your key.&lt;/p&gt;</description></item><item><title>OpenPGP Setup</title><link>https://blog.halleyadams.uk/posts/112/</link><pubDate>Thu, 30 Apr 2026 19:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/112/</guid><description>&lt;p&gt;GnuPG is installed by default on Kubuntu. The version shipped with Kubuntu 24.04 LTS is GnuPG 2.4.x. The source material this series draws on was written for GnuPG 2.2.x, and there are meaningful differences between the two worth knowing about before you start configuring anything. This page covers the current version throughout.&lt;/p&gt;
&lt;p&gt;Verify what you have installed:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;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;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gpg --version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The output should confirm GnuPG 2.4.x and list the supported algorithms. If it does not, check that your system is fully updated before continuing.&lt;/p&gt;</description></item><item><title>OpenPGP</title><link>https://blog.halleyadams.uk/posts/111/</link><pubDate>Thu, 30 Apr 2026 16:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/111/</guid><description>&lt;p&gt;OpenPGP is a standard for encrypting and signing data. GnuPG, usually called GPG, is the free and open source implementation of that standard that lives on your desktop. Together they give you the ability to encrypt files and messages so that only the intended recipient can read them, sign things so that recipients can verify they came from you, and authenticate to remote systems using your GPG key rather than a separate SSH key.&lt;/p&gt;</description></item><item><title>KeePassXC</title><link>https://blog.halleyadams.uk/posts/110/</link><pubDate>Thu, 30 Apr 2026 13:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/110/</guid><description>&lt;p&gt;KeePassXC is a password manager, but describing it that way undersells it. It is an encrypted personal database for anything that needs to stay private. Passwords, yes, but also SSH key passphrases, PIN codes, software licence keys, bank account details, API tokens, security questions, anything that would cause a problem if someone else had access to it.&lt;/p&gt;
&lt;p&gt;The underlying format is KDBX, an open standard that has been around long enough to have client applications on every platform worth naming. Your database is a single encrypted file. It can be backed up, synced, or handed to a trusted person for safekeeping, and none of that creates a security risk because the file is useless without the passphrase that unlocks it.&lt;/p&gt;</description></item><item><title>KWallet</title><link>https://blog.halleyadams.uk/posts/107/</link><pubDate>Wed, 29 Apr 2026 22:20:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/107/</guid><description>&lt;p&gt;KWallet is the KDE Plasma secrets store, and it is the Kubuntu equivalent of Gnome Keyring. The underlying job is the same: a system-level encrypted database that lives for the duration of your desktop session, storing credentials, keys, and other secrets so that applications can retrieve them without prompting you every time.&lt;/p&gt;
&lt;p&gt;The difference in practice is that KWallet integrates cleanly with the rest of the KDE application suite, handles modern SSH key formats without issue, and does not require the workarounds that the source material this series is based on spends an entire page describing for Gnome Keyring. On Kubuntu, this is the right tool for the job from the start.&lt;/p&gt;</description></item><item><title>Certificates</title><link>https://blog.halleyadams.uk/posts/106/</link><pubDate>Wed, 29 Apr 2026 22:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/106/</guid><description>&lt;p&gt;A certificate is a key with a signature attached. The signature comes from a Certificate Authority (CA), and it asserts that the public key inside the certificate genuinely belongs to the person or device named in it. Without that signature, a key is just a number. With it, other systems have a basis for trusting it.&lt;/p&gt;
&lt;p&gt;In the public internet, that trust chain runs back to a handful of root CAs whose certificates are baked into your operating system and browser. In this network, it runs back to a CA you control, built later in this series. The mechanics are identical. The difference is that you are the one doing the signing.&lt;/p&gt;</description></item><item><title>Keys</title><link>https://blog.halleyadams.uk/posts/105/</link><pubDate>Wed, 29 Apr 2026 19:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/105/</guid><description>&lt;p&gt;A key is, at its core, a very large number generated by a sophisticated mathematical process. Private keys are typically hundreds or thousands of bits long. Printed out, a single key would fill multiple pages of what looks like nonsense. No human is memorising that, which is why keys live in files rather than heads.&lt;/p&gt;
&lt;p&gt;The problem with keeping a secret in a file is that files can be copied, stolen, or accidentally committed to a git repository. The solution is to encrypt the key file itself with a passphrase, so that even if someone gets hold of the file, it is useless to them without the passphrase that unlocks it. This is why the passphrases section came first.&lt;/p&gt;</description></item><item><title>Passphrases</title><link>https://blog.halleyadams.uk/posts/104/</link><pubDate>Wed, 29 Apr 2026 16:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/104/</guid><description>&lt;p&gt;The word passphrase is used deliberately throughout this series. A single word is never a good passphrase. A short string of random characters looks secure but is harder to remember and type than something genuinely strong. The goal is secrets that are both resistant to attack and actually usable in practice, and those two things are less in tension than the conventional wisdom suggests.&lt;/p&gt;
&lt;h2 id="the-three-you-actually-need-to-remember"&gt;The three you actually need to remember&lt;/h2&gt;
&lt;p&gt;Most passphrases in this setup live in KeePassXC and never need to leave it. Your browser, mail client, and KWallet will handle credential entry on your behalf the vast majority of the time. That said, there are exactly three passphrases that need to live in your head rather than a database:&lt;/p&gt;</description></item><item><title>Passphrases, keys and certificates</title><link>https://blog.halleyadams.uk/posts/103/</link><pubDate>Wed, 29 Apr 2026 13:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/103/</guid><description>&lt;p&gt;Everything in this series depends on secrets. Passphrases that unlock encrypted storage. SSH keys that authenticate you to remote servers. TLS certificates that establish trust between services. A Yubikey that acts as a hardware root of trust for all of the above. Before any of the interesting infrastructure gets built, it is worth understanding what these things are, how they relate to each other, and how to manage them without either losing them or exposing them.&lt;/p&gt;</description></item><item><title>Desktop</title><link>https://blog.halleyadams.uk/posts/102/</link><pubDate>Wed, 29 Apr 2026 11:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/102/</guid><description>&lt;p&gt;The desktop section covers the computer you personally work from day to day. That might be a tower, a laptop, or something in between. The form factor does not matter much. What matters is the operating system, the configuration, and the habits you build around it.&lt;/p&gt;
&lt;p&gt;The source material this series draws on was written with Ubuntu Desktop in mind. This series diverges from that. Everything here is built on &lt;strong&gt;Kubuntu&lt;/strong&gt;, the Ubuntu LTS base with KDE Plasma as the desktop environment. The underlying system is identical: same repositories, same package manager, same terminal, same everything that matters for the technical content in this section. The difference is the desktop layer sitting on top of it.&lt;/p&gt;</description></item><item><title>Network Design</title><link>https://blog.halleyadams.uk/posts/101/</link><pubDate>Tue, 28 Apr 2026 22:20:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/101/</guid><description>&lt;p&gt;Before touching a single config file, it is worth spending time on paper. Network design decisions made early have a habit of becoming permanent and getting the addressing scheme, the VLAN structure, and the naming conventions right at the start saves a significant amount of pain later, especially when adding new services or troubleshooting something at 11pm.&lt;/p&gt;
&lt;p&gt;This network spans three physical locations, each with its own Ubiquiti UniFi router, its own subnet range, and its own VLAN structure. All three sites are connected via VPN, creating a single logical network across multiple physical locations. The design is consistent by intention: the same VLAN naming convention appears at every site, making it immediately obvious what a subnet does just from looking at its address.&lt;/p&gt;</description></item><item><title>About</title><link>https://blog.halleyadams.uk/posts/100/</link><pubDate>Tue, 28 Apr 2026 22:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/100/</guid><description>&lt;h2 id="what"&gt;What&lt;/h2&gt;
&lt;p&gt;This series covers the practical process of running your own digital infrastructure. Not in theory, not in a lab that gets torn down afterwards, but for real, day to day, for yourself.&lt;/p&gt;
&lt;p&gt;The things most people outsource to big tech without thinking about it and these are usually the kind of services that feel almost invisible until you start asking who actually owns the data behind them. However, these are all things you can run yourself. It takes more effort up front but it pays back over time.&lt;/p&gt;</description></item><item><title>Roll Your Own Network</title><link>https://blog.halleyadams.uk/posts/099/</link><pubDate>Tue, 28 Apr 2026 22:00:00 +0000</pubDate><guid>https://blog.halleyadams.uk/posts/099/</guid><description>&lt;p&gt;There is a version of the internet that most of us have quietly accepted without really agreeing to in which everything you do, and are, your entire digital presence, lives on someone else&amp;rsquo;s hardware. Your email lives on someone else&amp;rsquo;s server. Your files are synchronised through someone else&amp;rsquo;s infrastructure. Your calendar, your contacts, your messages, your photos, all of it sitting in a data centre owned by a company whose business model depends on knowing as much about you as possible.&lt;/p&gt;</description></item></channel></rss>