<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>KV Tech Tips</title>
  <subtitle>Troubleshooting and How-Tos.</subtitle>
  <link href="https://kvibber.com/tech-tips/feed.xml" rel="self"/>
  <link href="https://kvibber.com/tech-tips/"/>
  <icon>https://kvibber.com/tech-tips/images/keyboard-180.jpg</icon>
  <updated>2026-08-07T19:49:20Z</updated>
  <id>https://kvibber.com/tech-tips/</id>
  <author>
    <name>Kelson Vibber</name>
    <email>techtips@kvibber.com</email>
    <uri>https://kvibber.com</uri>
  </author>
  
  
  
  <entry>
    <title>Removing a Hybrid MBR from a GUID-Partitioned Disk</title>
    <link href="https://kvibber.com/tech-tips/hybrid-mbr/"/>
    <author>
      <name>Kelson Vibber</name>
      <email>techtips@kvibber.com</email>
      <uri>https://kvibber.com</uri>
    </author>
    <category term="computers"/>
    <category term="howto"/>
    <category term="techtips"/>
    <category term="hybrid-mbr"/><category term="compatibility"/><category term="windows"/><category term="macos"/><category term="linux"/><category term="dual-boot"/><category term="til"/><category term="blaugust"/><category term="100daystooffload"/>
    <summary>Today I learned about hybrid MBR/GUID partition tables, where the GPT on a disk is duplicated in an MBR table for (outdated) compatibility purposes. These days it can break compatibility with Windows if you have a partition MBR doesn&#39;t support, like a multi-terabyte filesystem.</summary>
    <updated>2026-08-07T19:49:20Z</updated>
    <id>https://kvibber.com/tech-tips/hybrid-mbr/</id>
    <content type="html">
    &lt;aside&gt;&lt;p&gt;I wasn’t planning to do all &lt;a href=&quot;https://kvibber.com/tag/blaugust/&quot;&gt;my Blaugust posts&lt;/a&gt; on tech stuff, but it seems to be working out that way this week. 🤷&lt;/p&gt;
&lt;/aside&gt;                       
&lt;p&gt;&lt;a name=&quot;top&quot;&gt;&lt;/a&gt;Today I learned about hybrid MBR/GUID partition tables, where the GPT (&lt;a href=&quot;https://kvibber.com/tech-tips/hybrid-mbr/#gpt&quot;&gt;not that kind&lt;/a&gt;) on a disk is duplicated in an MBR table for compatibility purposes. Those reasons are probably outdated, unless you’re running a really old operating system, because GUID partitions have been around a &lt;em&gt;very&lt;/em&gt; long time now.&lt;/p&gt;
&lt;aside&gt;&lt;p&gt;According to &lt;a href=&quot;https://en.wikipedia.org/wiki/GUID_Partition_Table#Windows:_32-bit_versions&quot;&gt;Wikipedia’s chart&lt;/a&gt; you have to go back to Windows 7 to find an OS that can access GPT disks but not boot from them, and Windows Server 2003 (fixed in SP1) to find one that doesn’t recognize them at all.&lt;/p&gt;
&lt;/aside&gt; 
&lt;p&gt;&lt;strong&gt;Fun fact 1:&lt;/strong&gt; This means a disk can have an MBR pointing to a partition that MBR doesn’t support, like a multi-terabyte filesystem!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fun fact 2:&lt;/strong&gt; If Windows sees an MBR, it doesn’t bother with the GUID table, and it insists that partition is invalid! (Yes, even if you specifically formatted it with exFAT so Windows could read it.)&lt;/p&gt;
&lt;p&gt;macOS at least used to automatically &lt;a href=&quot;https://superuser.com/questions/508026/windows-detects-gpt-disk-as-mbr-in-efi-boot&quot;&gt;create these when adding FAT filesystems&lt;/a&gt;, and if it still does, that probably explains how this external drive ended up with the hybrid MBR on it. Though if it does so when creating a &amp;gt;2TB system, I’d consider that a bug.&lt;/p&gt;
&lt;h2&gt;Repair by Removing the Extra MBR&lt;/h2&gt;
&lt;p&gt;Fortunately, it turned out to be easy to fix using the directions in this &lt;a href=&quot;https://superuser.com/questions/508026/windows-detects-gpt-disk-as-mbr-in-efi-boot&quot;&gt;SuperUser post from 2012&lt;/a&gt;, because we don’t need the duplicate MBR for anything here in 2026.&lt;/p&gt;
&lt;p&gt;We &lt;a href=&quot;https://www.rodsbooks.com/gdisk/repairing.html&quot;&gt;used &lt;code&gt;gdisk&lt;/code&gt;&lt;/a&gt; to create a new protective MBR table, leaving the GUID table unchanged. It fixed the problem and took less than a minute. We didn’t even have to jump through hoops to install it since we had a Fedora box handy and installing was just a matter of &lt;code&gt;dnf install gdisk&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here’s what gdisk’s author suggested for converting the hybrid MBR disk to a standard GPT disk:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Download GPT fdisk from &lt;a href=&quot;https://sourceforge.net/projects/gptfdisk/&quot;&gt;its Sourceforge page&lt;/a&gt; and install it. (Versions are available for Linux, OS X, and Windows. I’ll assume you’ll do this from OS X.) Alternatively, you could run it from a Linux emergency disc like Parted Magic. [&lt;strong&gt;KV Note:&lt;/strong&gt; If you have a Linux system already, check whether &lt;code&gt;gdisk&lt;/code&gt; or &lt;code&gt;gptdisk&lt;/code&gt; is available directly from your distro’s package manager.]&lt;/li&gt;
&lt;li&gt;Launch &lt;code&gt;gdisk&lt;/code&gt; on your disk by typing sudo gdisk /dev/disk1 in a Terminal window. (Change the device identifier if it’s not as you presented earlier or if you use another OS for the job.)&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;p&lt;/code&gt; to view the partition table to verify you’re working on the correct disk. If not, type q to quit without saving your changes and try again with another device.&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;x&lt;/code&gt; to enter the experts’ menu.&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;n&lt;/code&gt; to create a fresh protective MBR. Note that gdisk won’t confirm a change; it’ll just show you a new experts’ prompt.&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;w&lt;/code&gt; to save your changes. You’ll be asked to confirm this action. Do so.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With any luck this will fix the problem. If it doesn’t, though, you can use gdisk’s &lt;code&gt;v&lt;/code&gt; option (on any menu) to have gdisk look for partition table problems. It can fix some minor problems automatically, but other times you’ll need to make explicit changes. See the GPT fdisk documentation on GPT repairs for details.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I also looked up what a protective MBR is: On a regular GUID-partitioned disk, &lt;a href=&quot;https://learn.microsoft.com/en-us/troubleshoot/windows-server/backup-and-storage/guid-partitioning-table-disk-faq#what-is-a-protective-mbr&quot;&gt;a protective MBR covers the whole disk&lt;/a&gt; to prevent older tools that don’t know about GPTs from clobbering the &lt;em&gt;real&lt;/em&gt; partition table.&lt;/p&gt;
&lt;aside&gt;&lt;p&gt;&lt;a name=&quot;gpt&quot; href=&quot;https://kvibber.com/tech-tips/hybrid-mbr/#top&quot;&gt;↑&lt;/a&gt; It was really weird typing this up in 2026, because 99% of the time when someone is talking about “GPT” these days they mean ChatGPT (Generative pre-trained transformer) or something similar, not GUID Partition Table.&lt;/p&gt;
&lt;/aside&gt;
&lt;footer&gt;This post by &lt;a class=&quot;p-author h-card&quot; rel=&quot;author&quot; href=&quot;https://kvibber.com&quot;&gt;Kelson Vibber&lt;/a&gt; originally appeared &lt;a class=&quot;u-url&quot; href=&quot;https://kvibber.com/tech-tips/hybrid-mbr/&quot;&gt;on KV Tech Tips&lt;/a&gt;&lt;/footer&gt;</content>
  </entry>
  
  
  
  <entry>
    <title>What is rel=&amp;quot;bookmark&amp;quot;?</title>
    <link href="https://kvibber.com/tech-tips/rel-bookmark/"/>
    <author>
      <name>Kelson Vibber</name>
      <email>techtips@kvibber.com</email>
      <uri>https://kvibber.com</uri>
    </author>
    <category term="web-development"/>
    <category term="howto"/>
    <category term="techtips"/>
    <category term="web-standards"/><category term="html"/><category term="linking"/><category term="til"/><category term="blaugust"/><category term="100daystooffload"/>
    <summary>I stumbled on a reference to rel=&quot;bookmark&quot; being used for permalinks, but I don&#39;t think I&#39;ve ever seen it used that way.</summary>
    <updated>2026-08-07T04:50:09Z</updated>
    <id>https://kvibber.com/tech-tips/rel-bookmark/</id>
    <content type="html">
    &lt;p&gt;While looking for linking conventions regarding &lt;a href=&quot;https://journal.kvibber.com/2026/08/permalink-placement/&quot;&gt;permalink locations&lt;/a&gt;, I found a reference on Wikipedia to &lt;code&gt;&amp;lt;a rel=&amp;quot;bookmark&amp;quot; href=&amp;quot;...&amp;quot;&amp;gt;&lt;/code&gt; being used for permalinks. I’m not sure I’ve ever noticed or used that. I didn’t find any mentions of people actually &lt;em&gt;using&lt;/em&gt; it that way.&lt;/p&gt;
&lt;p&gt;Mostly I found secondhand references to other references to the HTML spec (yes, it’s official!), which has this &lt;a href=&quot;https://html.spec.whatwg.org/multipage/links.html#link-type-bookmark&quot;&gt;clear as mud description&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The bookmark keyword may be used with &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;area&lt;/code&gt; elements. This keyword creates a hyperlink.&lt;/p&gt;
&lt;p&gt;The bookmark keyword gives a permalink for the nearest ancestor &lt;code&gt;article&lt;/code&gt; element of the linking element in question, or of the section the linking element is most closely associated with, if there are no ancestor &lt;code&gt;article&lt;/code&gt; elements.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So, should I use this on the permalink, well, link on my post, or not? Should I use it only on multi-post pages like tag/category archives? Or in a table of contents?&lt;/p&gt;
&lt;p&gt;The best reference I found, &lt;a href=&quot;https://ws-dl.blogspot.com/2017/08/2017-08-26-relbookmark-also-does-not.html&quot;&gt;&lt;code&gt;rel=&amp;quot;bookmark&amp;quot;&lt;/code&gt; also does not mean what you think it means&lt;/a&gt; is more clear: It should go anywhere you would link to a permalink &lt;em&gt;from the same material&lt;/em&gt;. So:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Single blog post on a page, linking to itself? Yes!&lt;/li&gt;
&lt;li&gt;Time or category archive pages, with complete posts or significant chunks of them, linking to each individual post’s page? Yes!&lt;/li&gt;
&lt;li&gt;Table of contents listing just titles and summaries? NO!&lt;/li&gt;
&lt;li&gt;Long page with multiple posts, where the permanent versions of those posts are on that same page, each with a permalink pointing to that section on that page? Yes!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The same article also points out that it’s effectively obsolete, since just about everyone uses permalinks now, but hardly anyone puts &lt;code&gt;rel=&amp;quot;bookmark&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I decided to add it when I updated my templates, because why not?&lt;/p&gt;
&lt;footer&gt;This post by &lt;a class=&quot;p-author h-card&quot; rel=&quot;author&quot; href=&quot;https://kvibber.com&quot;&gt;Kelson Vibber&lt;/a&gt; originally appeared &lt;a class=&quot;u-url&quot; href=&quot;https://kvibber.com/tech-tips/rel-bookmark/&quot;&gt;on KV Tech Tips&lt;/a&gt;&lt;/footer&gt;</content>
  </entry>
  
  
  
  <entry>
    <title>Count Upgradable Packages on Alpine Linux</title>
    <link href="https://kvibber.com/tech-tips/apk-count-upgrades/"/>
    <author>
      <name>Kelson Vibber</name>
      <email>techtips@kvibber.com</email>
      <uri>https://kvibber.com</uri>
    </author>
    <category term="linux"/>
    <category term="howto"/>
    <category term="techtips"/>
    <category term="alpine-linux"/><category term="updates"/><category term="busybox"/><category term="shell-script"/><category term="100daystooffload"/>
    <summary>TLDR: doas apk update, then  apk list -uq | wc. You can automate checking for new packages, and you can use this shell script to report how many updates are available when you log in.</summary>
    <updated>2026-07-26T01:32:08Z</updated>
    <id>https://kvibber.com/tech-tips/apk-count-upgrades/</id>
    <content type="html">
    &lt;p&gt;I like to use &lt;a href=&quot;https://kvibber.com/reviews/software/alpine-linux/&quot;&gt;Alpine Linux&lt;/a&gt; for cloud servers because it’s extremely small, so I don’t have to add extra RAM just to run the operating system. But I like knowing when updated packages are available.&lt;/p&gt;
&lt;p&gt;You need root to update the local list of available packages first, but that can be automated safely (see below):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;doas apk update
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just asking Alpine is easy, and doesn’t require extra permissions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apk list -u
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That gives you a detailed list of upgradable packages. Or to get a &lt;strong&gt;q&lt;/strong&gt;uieter result with just the names:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apk list -uq
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this being Linux, it’s easy to get a word count with wc (yes, it’ll count hyphenated package names as a single word):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apk list -uq | wc -w
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Automating the List Updates&lt;/h2&gt;
&lt;p&gt;To get APK to update its package list daily, I’ve put the following in &lt;code&gt;/etc/periodic/daily/apk-update.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/sh
apk update
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That doesn’t install anything, so it’s safe to let it check on a daily basis. Of course that doesn’t &lt;em&gt;tell&lt;/em&gt; me anything, it just makes sure the list is relatively current when I ask it.&lt;/p&gt;
&lt;aside&gt;&lt;p&gt;I assume if you’re using Alpine you know how to set file ownership/permissions. Scripts in &lt;code&gt;/etc/periodic/daily&lt;/code&gt; should be owned by root, executable, and not writeable by other users. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;doas chown root:root apk-update.sh &amp;amp;&amp;amp; doas chmod 755 apk-update.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;/aside&gt;
&lt;h2&gt;Friendly Reports on Login&lt;/h2&gt;
&lt;p&gt;For the actual report, I’ve written a short shell script (kept simple so it’ll run in busybox) called &lt;code&gt;count-apk.sh&lt;/code&gt; to give me the count and names, but only if there aren’t too many of them:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/sh                                                                                      
UPGRADABLE=`apk list -qu`
COUNT=`echo &amp;quot;$UPGRADABLE&amp;quot; | wc -w`
if [ $COUNT -gt 9 ]; then
	echo $COUNT packages available to upgrade.
elif [ $COUNT -gt 1 ]; then
	echo $COUNT packages available to upgrade:
	printf &#39;%s&#92;n&#39; &amp;quot;$UPGRADABLE&amp;quot;
elif [ $COUNT -gt 0 ]; then
	echo 1 package available to upgrade:
	printf &#39;%s&#92;n&#39; &amp;quot;$UPGRADABLE&amp;quot;
else
	echo No packages available to upgrade.
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And to get that reported every time I log in, I’ve put it in my &lt;code&gt;~/.profile&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;~/bin/count-apk.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Meanwhile I’ve been trying to decide how much I want to automate maintenance on these systems. I don’t want to auto-update everything and risk breaking changes, but I don’t want to have to log into every internet-reachable virtual server on a weekly basis to keep it current either.&lt;/p&gt;
&lt;footer&gt;This post by &lt;a class=&quot;p-author h-card&quot; rel=&quot;author&quot; href=&quot;https://kvibber.com&quot;&gt;Kelson Vibber&lt;/a&gt; originally appeared &lt;a class=&quot;u-url&quot; href=&quot;https://kvibber.com/tech-tips/apk-count-upgrades/&quot;&gt;on KV Tech Tips&lt;/a&gt;&lt;/footer&gt;</content>
  </entry>
  
  
  
  <entry>
    <title>Hide Related Posts by Post Format</title>
    <link href="https://kvibber.com/tech-tips/hide-related-posts-by-format/"/>
    <author>
      <name>Kelson Vibber</name>
      <email>techtips@kvibber.com</email>
      <uri>https://kvibber.com</uri>
    </author>
    <category term="programming"/>
    <category term="howto"/>
    <category term="techtips"/>
    <category term="wordpress"/><category term="php"/><category term="classicpress"/><category term="blogging"/><category term="100daystooffload"/>
    <summary>Here&#39;s how you can hide related posts on Asides or Links when using Contextual Related Posts or Jetpack on your blog.</summary>
    <updated>2026-07-16T19:32:22Z</updated>
    <id>https://kvibber.com/tech-tips/hide-related-posts-by-format/</id>
    <content type="html">
    &lt;p&gt;I finally got around to re-customizing the related posts on &lt;a href=&quot;https://journal.kvibber.com/&quot;&gt;my blog&lt;/a&gt;. Specifically: &lt;strong&gt;hiding related posts on asides and links&lt;/strong&gt;.&lt;/p&gt;
&lt;aside&gt;&lt;p&gt;They’re short. A set of related thumbnails can be as long as (or longer than) the actual post.&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;Years ago I had this set up when the site was running WordPress and &lt;a href=&quot;https://kvibber.com/reviews/software/wordpress-plugins/#jetpack&quot;&gt;Jetpack&lt;/a&gt;. I’ve since moved it to &lt;a href=&quot;https://kvibber.com/reviews/software/wordpress-plugins/#contextual-related-posts&quot;&gt;Contextual Related Posts&lt;/a&gt; (in order to keep it local on my server), and later to &lt;a href=&quot;https://kvibber.com/reviews/software/classicpress/&quot;&gt;ClassicPress&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Neither plugin has features to skip based on post formats, but you can use hooks, actions and filters to write your own.&lt;/p&gt;
&lt;h2&gt;Contextual Related Posts&lt;/h2&gt;
&lt;p&gt;The plugin has options to exclude displaying by post &lt;em&gt;type&lt;/em&gt; (post, page, or media), by category, or on individual post IDs…but not by tag or post &lt;em&gt;format&lt;/em&gt;. So I dug around in the docs a bit, looking for ways to filter CRP’s input or output.&lt;/p&gt;
&lt;p&gt;The simplest approach I came up with was to turn off automatically adding it to any page/feed/archive type in the CRP plugin’s general tab, then manually add it to the post’s output when and only when I want it included.&lt;/p&gt;
&lt;h3&gt;Option 1: Modify Your Theme&lt;/h3&gt;
&lt;p&gt;The CRP admin file suggests putting &lt;code&gt;&amp;lt;?php if ( function_exists( &#39;echo_crp&#39; ) ) { echo_crp(); } ?&amp;gt;&lt;/code&gt; in your theme template where you want it to display. To make it only display under some conditions, you’ll extend the &lt;em&gt;if&lt;/em&gt; statement like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;? php
	if (
		get_post_format() != &#39;aside&#39;
		&amp;amp;&amp;amp; get_post_format() != &#39;link&#39;
		&amp;amp;&amp;amp; function_exists( &#39;echo_crp&#39; )
	) {
		echo_crp();
	}
 ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;echo_crp()&lt;/code&gt; sends the plugin’s output to straight to the page being rendered.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;get_post_format()&lt;/code&gt; is self-explanatory.&lt;/p&gt;
&lt;h3&gt;Option 2: Use a Plugin or Functions.php&lt;/h3&gt;
&lt;p&gt;I put a lot of my blog customizations in a local functionality plugin, so I can switch themes without losing my changes. (Admittedly, it’s been a long time since I last switched themes on my blog.) This should also work if you put it in a &lt;code&gt;functions.php&lt;/code&gt; file in your theme.&lt;/p&gt;
&lt;p&gt;The idea here is that you add a filter to &lt;code&gt;the_content&lt;/code&gt; and decide whether or not to add the related posts lists to the end.&lt;/p&gt;
&lt;aside&gt;&lt;p&gt;Remember to always put a unique prefix in any function names or global variables you put in a WordPress/ClassicPress plugin, to avoid collisions with other plugins’ names.&lt;/p&gt;
&lt;/aside&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;// Manually show Contextual Related Posts output, so we can skip displaying it on asides and linkposts
function exampleprefix_manual_crp($content) {
	if (
		in_the_loop()
		&amp;amp;&amp;amp; is_main_query()
		&amp;amp;&amp;amp; is_single()
		&amp;amp;&amp;amp; get_post_format() != &#39;aside&#39;
		&amp;amp;&amp;amp; get_post_format() != &#39;link&#39;
		&amp;amp;&amp;amp; function_exists( &#39;get_crp&#39; )
	) {
		$content .= get_crp();
	}
	return $content;
}
add_filter(&#39;the_content&#39;, &#39;exampleprefix_manual_crp&#39;, 999);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;in_the_loop()&lt;/code&gt; and &lt;code&gt;is_main_query()&lt;/code&gt; will ensure it’s only shown on the web view, not on an ActivityPub view from the Fediverse.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;is_single()&lt;/code&gt; will ensure it’s only shown on individual post views, not in feeds or category pages or the home page.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;get_crp()&lt;/code&gt; is similar to &lt;code&gt;echo_crp()&lt;/code&gt;, but instead of pushing the output to the page, it returns the output so you can work with it.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;999&lt;/code&gt; is the placement of this filter, and a convenient number to make sure it’s all the way at the end.&lt;/p&gt;
&lt;h2&gt;Jetpack Related Posts&lt;/h2&gt;
&lt;p&gt;I forget where I found this since it was probably a decade ago. Maybe on the Jetpack support forum, maybe on StackExchange.&lt;/p&gt;
&lt;p&gt;The approach I used was to check the post conditions, find Jetpack’s related posts filter, and then remove it from the active filters on &lt;code&gt;the_content&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;// Don&#39;t show Jetpack Related Posts on asides or links
function exampleprefix_hide_jetpack_related_on_asides() {
	if ( class_exists( &#39;Jetpack_RelatedPosts&#39; )
		&amp;amp;&amp;amp; (get_post_format() == &#39;aside&#39; || get_post_format() == &#39;link&#39;)
	) {
		$jprp = Jetpack_RelatedPosts::init();
		$callback = array( $jprp, &#39;filter_add_target_to_dom&#39; );
		remove_filter( &#39;the_content&#39;, $callback, 40 );
	}
}
add_action( &#39;wp&#39;, &#39;exampleprefix_hide_jetpack_related_on_asides&#39;, 20 );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It turns out there’s a simpler way to do it now. (Maybe there was back at the time, and I just found the complicated one first.) The &lt;a href=&quot;https://developer.jetpack.com/docs/customization/related-posts/&quot;&gt;Jetpack Related Posts Developer Reference&lt;/a&gt; has an example where you just turn off the feature when it checks the options:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;// Don&#39;t show Jetpack Related Posts on asides or links
function exampleprefix_hide_related_posts_jetpack( $options ) {
    if ( get_post_format() == &#39;aside&#39; || get_post_format() == &#39;link&#39; ) {
        $options[&#39;enabled&#39;] = false;
    }
    return $options;
}
add_filter( &#39;jetpack_relatedposts_filter_options&#39;, &#39;exampleprefix_hide_related_posts_jetpack&#39; );
&lt;/code&gt;&lt;/pre&gt;
&lt;aside&gt;&lt;p&gt;July 16: Added the in_the_loop() and is_main_query() conditions to the CRP function approach, to avoid showing the links in the ActivityPub view.&lt;/p&gt;
&lt;/aside&gt;
&lt;footer&gt;This post by &lt;a class=&quot;p-author h-card&quot; rel=&quot;author&quot; href=&quot;https://kvibber.com&quot;&gt;Kelson Vibber&lt;/a&gt; originally appeared &lt;a class=&quot;u-url&quot; href=&quot;https://kvibber.com/tech-tips/hide-related-posts-by-format/&quot;&gt;on KV Tech Tips&lt;/a&gt;&lt;/footer&gt;</content>
  </entry>
  
  
  
  <entry>
    <title>/dev/null: Permission denied on Alpine Linux</title>
    <link href="https://kvibber.com/tech-tips/dev-null-denied/"/>
    <author>
      <name>Kelson Vibber</name>
      <email>techtips@kvibber.com</email>
      <uri>https://kvibber.com</uri>
    </author>
    <category term="linux"/>
    <category term="howto"/>
    <category term="techtips"/>
    <category term="null"/><category term="updates"/><category term="permissions"/><category term="alpine-linux"/><category term="100daystooffload"/>
    <summary>One of my Alpine Linux VMs broke during an upgrade and started throwing errors about the permissions on /dev/null. Just fixing the permissions didn&#39;t persist across reboots, but resetting the device manager did.</summary>
    <updated>2026-06-10T19:34:35Z</updated>
    <id>https://kvibber.com/tech-tips/dev-null-denied/</id>
    <content type="html">
    &lt;p&gt;I upgraded two virtual machines to &lt;a href=&quot;https://kvibber.com/reviews/software/alpine-linux/&quot;&gt;Alpine Linux&lt;/a&gt; 3.24 last night. One worked perfectly. The other came back up after reboot, but the services it runs didn’t start up (reminder to self: this is why you don’t run upgrades late at night), and when I logged in (fortunately SSH was still working!) I saw that some of the scripts were throwing errors like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; -ash: can&#39;t create /dev/null: Permission denied
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wait, &lt;em&gt;what&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;Sure enough:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ls -l /dev/null
crw-rw----    1 root     root        1,   3 Jun 10 10:52 /dev/null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The obvious solution was to fix the permissions on /dev/null:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;doas chmod a+rw /dev/null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But that didn’t persist when I rebooted, because devtmpfs isn’t on disk, it’s in memory. Besides, I needed to check for other devices that might have broken permissions. I re-ran the upgrade, followed by repair…&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;doas apk upgrade --available
doas apk fix --directory-permissions
doas apk fix -x
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But that didn’t work either. Because /dev isn’t built from packages, it’s built by a device manager. I found a bunch of forum and Stack Exchange posts on how to fix the problem in &lt;em&gt;udev&lt;/em&gt; on distros like &lt;a href=&quot;https://kvibber.com/reviews/software/arch-linux/&quot;&gt;Arch&lt;/a&gt; or Gentoo, but &lt;a href=&quot;https://wiki.alpinelinux.org/wiki/Device_Manager&quot;&gt;Alpine doesn’t use udev&lt;/a&gt;, so none of those suggestions were even relevant.&lt;/p&gt;
&lt;h2&gt;The Fix&lt;/h2&gt;
&lt;p&gt;What &lt;em&gt;did&lt;/em&gt; fix the problem was just resetting Alpine’s device manager, in this case mdev:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;doas setup-devd mdev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Permissions stayed fixed when I rebooted it again, and everything started up correctly!&lt;/p&gt;
&lt;p&gt;I assume doing the same with mdevd or eudev would have worked if a system using one of those broke the same way.&lt;/p&gt;
&lt;h2&gt;Possible Cause&lt;/h2&gt;
&lt;p&gt;I’m not sure why one of them broke and the other was fine.&lt;/p&gt;
&lt;p&gt;It’s an older VM, and I started it on an older version of Alpine, so it may have been something left over that didn’t break on 3.23, but broke on 3.24.&lt;/p&gt;
&lt;p&gt;I also forgot to add &lt;code&gt;--available&lt;/code&gt; to the &lt;code&gt;apk upgrade&lt;/code&gt; command the first time through, and I don’t remember if I re-ran it before rebooting the first time or after. It may have broken during the reboot with the partial upgrade.&lt;/p&gt;
&lt;p&gt;It could be either of those, or something else entirely.&lt;/p&gt;
&lt;footer&gt;This post by &lt;a class=&quot;p-author h-card&quot; rel=&quot;author&quot; href=&quot;https://kvibber.com&quot;&gt;Kelson Vibber&lt;/a&gt; originally appeared &lt;a class=&quot;u-url&quot; href=&quot;https://kvibber.com/tech-tips/dev-null-denied/&quot;&gt;on KV Tech Tips&lt;/a&gt;&lt;/footer&gt;</content>
  </entry>
  
  
  
  <entry>
    <title>Tech Tips have moved to KVibber.com</title>
    <link href="https://kvibber.com/tech-tips/moved/"/>
    <author>
      <name>Kelson Vibber</name>
      <email>techtips@kvibber.com</email>
      <uri>https://kvibber.com</uri>
    </author>
    <category term="general"/>
    <category term="howto"/>
    <category term="techtips"/>
    <category term="site-updates"/>
    <summary>New site, new name. What used to be Hyperborea Tech Tips is now KV Tech Tips on a domain based on my name instead of a half-remembered old movie.</summary>
    <updated>2026-05-23T15:00:00Z</updated>
    <id>https://kvibber.com/tech-tips/moved/</id>
    <content type="html">
    &lt;p&gt;Last month I moved all the tech tips from my &lt;a href=&quot;https://hyperborea.org&quot;&gt;old site at Hyperborea.org&lt;/a&gt; to &lt;a href=&quot;https://kvibber.com&quot;&gt;KVibber.com&lt;/a&gt;. Along with the move, it needed a new name since it’s not at Hyperborea anymore. &lt;a href=&quot;https://kvibber.com/tech-tips/about/&quot;&gt;I went with “KV Tech Tips”&lt;/a&gt; because it’s both my initials and a common abbreviation for Key-Value, programming terms for a data structure that has a bunch of names (“keys”) each pointing to the real data (“values”).&lt;/p&gt;
&lt;p&gt;I chose the &lt;a href=&quot;https://hyperborea.org/whatisit.html&quot;&gt;old name from a mythical location&lt;/a&gt; in a half-remembered movie from the 1970s, back in 2000. The newer site has been an &lt;a href=&quot;https://indieweb.org&quot;&gt;IndieWeb&lt;/a&gt;-style profile for a few years, and I finally decided to move things over.&lt;/p&gt;
&lt;p&gt;Old links will keep redirecting to the new one as long as I hang onto the domain name.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you subscribe to the RSS feed, you may want to update it to &lt;a href=&quot;https://kvibber.com/tech-tips/feed.xml&quot;&gt;https://kvibber.com/tech-tips/feed.xml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;If you have a site that links here, I’d appreciate it if you update your links to point to the new location.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;footer&gt;This post by &lt;a class=&quot;p-author h-card&quot; rel=&quot;author&quot; href=&quot;https://kvibber.com&quot;&gt;Kelson Vibber&lt;/a&gt; originally appeared &lt;a class=&quot;u-url&quot; href=&quot;https://kvibber.com/tech-tips/moved/&quot;&gt;on KV Tech Tips&lt;/a&gt;&lt;/footer&gt;</content>
  </entry>
  
  
  
  <entry>
    <title>Adding External S3 Storage on Nextcloud Using OCC</title>
    <link href="https://kvibber.com/tech-tips/nextcloud-occ-s3/"/>
    <author>
      <name>Kelson Vibber</name>
      <email>techtips@kvibber.com</email>
      <uri>https://kvibber.com</uri>
    </author>
    <category term="servers"/>
    <category term="howto"/>
    <category term="techtips"/>
    <category term="nextcloud"/><category term="cloud-storage"/><category term="self-hosting"/><category term="s3"/><category term="amazon"/><category term="linux"/>
    <summary>It&#39;s easier to add a storage folder for all users or for your own user via the GUI. But it is possible to create a folder for a specific user using the commandline tool.</summary>
    <updated>2026-04-27T04:21:46Z</updated>
    <id>https://kvibber.com/tech-tips/nextcloud-occ-s3/</id>
    <content type="html">
    &lt;p&gt;Nextcloud can use external sources like S3 or SFTP (or other Nextcloud servers!) for additional storage, which you can mount as a folder for all users or for a specific user. These folders will work just like regular folders on your Nextcloud server, except the data is stored somewhere else, like a remote backup service or an object storage bucket. (Useful if you want to store large files or media libraries without filling up the server’s main drive.)&lt;/p&gt;
&lt;p&gt;For all users, or for your own login, it’s easier to &lt;a href=&quot;https://docs.nextcloud.com/server/stable/admin_manual/configuration_files/external_storage_configuration_gui.html&quot;&gt;set it up through the GUI&lt;/a&gt;. To set up an external folder for a specific user, you can use the &lt;a href=&quot;https://docs.nextcloud.com/server/stable/admin_manual/occ_command.html&quot;&gt;occ command-line tool&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;OCC is run on the server, as the webserver account. Depending on your setup you may need to use sudo, su, runuser, etc. and you may need to point it to the right php location. (See the previous link for reference.)&lt;/p&gt;
&lt;p&gt;Or if you’re using the Nextcloud Snap, it’ll just be this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nextcloud.occ
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Anyway, I cobbled together this config from an example that had the wrong authentication method, and extrapolating from a working config I’d set up for my own login using the GUI (checked via &lt;code&gt;occ files_external:list&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WRAPPER_TO_OCC files_external:create FOLDER_NAME &#92;
  amazons3 &#92;
  amazons3::accesskey &#92;
  --user=USER_NAME &#92;
  --config bucket=&amp;quot;YOUR_BUCKET&amp;quot; &#92;
  --config hostname=&amp;quot;YOUR_HOST&amp;quot; &#92;
  --config port=&amp;quot;443&amp;quot; &#92;
  --config use_ssl=&amp;quot;true&amp;quot; &#92;
  --config use_path_style=&amp;quot;true&amp;quot; &#92;
  --config key=&amp;quot;YOUR_KEY&amp;quot; &#92;
  --config secret=&amp;quot;YOUR_SECRET&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It worked just fine on &lt;a href=&quot;https://kvibber.com/reviews/linode/&quot;&gt;Linode&lt;/a&gt;’s object storage. And yes, it still has to say &lt;code&gt;amazons3&lt;/code&gt; even if you’re using a different S3 provider.&lt;/p&gt;
&lt;footer&gt;This post by &lt;a class=&quot;p-author h-card&quot; rel=&quot;author&quot; href=&quot;https://kvibber.com&quot;&gt;Kelson Vibber&lt;/a&gt; originally appeared &lt;a class=&quot;u-url&quot; href=&quot;https://kvibber.com/tech-tips/nextcloud-occ-s3/&quot;&gt;on KV Tech Tips&lt;/a&gt;&lt;/footer&gt;</content>
  </entry>
  
  
  
  <entry>
    <title>Minimal User Setup for Debian Cloud</title>
    <link href="https://kvibber.com/tech-tips/debian-nocloud-minimal-user-setup/"/>
    <author>
      <name>Kelson Vibber</name>
      <email>techtips@kvibber.com</email>
      <uri>https://kvibber.com</uri>
    </author>
    <category term="linux"/>
    <category term="howto"/>
    <category term="techtips"/>
    <category term="debian"/><category term="sudo"/><category term="permissions"/><category term="virtualization"/><category term="self-hosting"/><category term="cloud-hosting"/><category term="shell-script"/>
    <summary>The quickest way to set up a sudo-capable user on a Debian cloud (or nocloud) image with useradd, usermod and the sudo group.</summary>
    <updated>2026-04-12T16:28:00Z</updated>
    <id>https://kvibber.com/tech-tips/debian-nocloud-minimal-user-setup/</id>
    <content type="html">
    &lt;p&gt;Debian offers &lt;a href=&quot;https://cloud.debian.org/images/cloud/&quot;&gt;cloud images&lt;/a&gt; that you can plug into a hosting provider and run instead of spending time going through the installer. Most of them assume you’re going to use the host’s system to set up the your login credentials.&lt;/p&gt;
&lt;p&gt;There’s also a “nocloud” image intended to run on a local setup, so you can toss a qcow2 image onto Qemu (directly or with or your favorite GUI wrapper like &lt;a href=&quot;https://kvibber.com/reviews/software/utm/&quot;&gt;UTM&lt;/a&gt; or &lt;a href=&quot;https://kvibber.com/reviews/software/boxes/&quot;&gt;Boxes&lt;/a&gt;). It ships with a &lt;strong&gt;passwordless root user&lt;/strong&gt; to get you started quickly.&lt;/p&gt;
&lt;p&gt;You don’t want to &lt;em&gt;keep&lt;/em&gt; running it as root. &lt;em&gt;Especially&lt;/em&gt; without a password!&lt;/p&gt;
&lt;p&gt;So here’s a set of quick steps to set up a regular user for admin and put a password on root. (Why am I writing this down? Because I always forget which Linux distributions  use &lt;code&gt;useradd&lt;/code&gt; and which use &lt;code&gt;adduser&lt;/code&gt;.)&lt;/p&gt;
&lt;h2&gt;Optional: Set a Hostname&lt;/h2&gt;
&lt;p&gt;Debian 11 ships with the name set to “debian,” but it’s not set in the hosts file. Sudo will complain that it can’t find this “debian” machine when you run it, though it will run.&lt;/p&gt;
&lt;p&gt;So if you’re setting up a new oldoldstable for some reason, you probably want to start by setting a hostname and adding it to &lt;code&gt;/etc/hosts&lt;/code&gt; (on the same line as localhost).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hostname HOSTNAME
nano /etc/hostname
nano /etc/hosts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can of course use &lt;code&gt;vi&lt;/code&gt; instead, or install another text editor like &lt;code&gt;emacs&lt;/code&gt; using &lt;code&gt;apt&lt;/code&gt;, assuming the image connected to the network.&lt;/p&gt;
&lt;p&gt;Debian 12 doesn’t set a hostname in the cloud images, so it doesn’t have this problem, though of course you’ll probably want to set one anyway if you plan on keeping the machine around!&lt;/p&gt;
&lt;h2&gt;Set Up The User&lt;/h2&gt;
&lt;p&gt;Next, create the user, add them to the sudoers group, and set their password.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;useradd -m -s /bin/bash USERNAME
usermod -aG sudo        USERNAME
passwd                  USERNAME
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some notes on the options used here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can of course choose a different shell than bash here.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useradd -m&lt;/code&gt; will also create the home directory.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;usermod -aG&lt;/code&gt; will &lt;em&gt;add&lt;/em&gt; a group to the user’s list of groups, rather than moving them entirely.&lt;/li&gt;
&lt;li&gt;Debian pre-configures a “sudo” group and gives its members permission to run &lt;code&gt;sudo&lt;/code&gt; with their own password, which is exactly what I want here.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Other distros vary in whether they set up a group and what they call it (ex. Red Hat/Fedora call it “wheel”), or whether you need to set up access manually using &lt;code&gt;visudo&lt;/code&gt; or putting something in &lt;code&gt;/etc/sudoers.d&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now you can set a password for root.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;passwd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Congratulations! You should be able to log out of root now, log in with your regular user, and use sudo when needed.&lt;/p&gt;
&lt;h2&gt;Bonus: Lock Out Root&lt;/h2&gt;
&lt;p&gt;Once you’ve logged in with your regular user and verified that it can sudo, lock the root account so no one can log into it in the first place. It’ll still be there running things behind the scenes, but it won’t be &lt;em&gt;open&lt;/em&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo passwd -l root
&lt;/code&gt;&lt;/pre&gt;
&lt;footer&gt;This post by &lt;a class=&quot;p-author h-card&quot; rel=&quot;author&quot; href=&quot;https://kvibber.com&quot;&gt;Kelson Vibber&lt;/a&gt; originally appeared &lt;a class=&quot;u-url&quot; href=&quot;https://kvibber.com/tech-tips/debian-nocloud-minimal-user-setup/&quot;&gt;on KV Tech Tips&lt;/a&gt;&lt;/footer&gt;</content>
  </entry>
  
  
  
  <entry>
    <title>Connect macOS Calendar, Contacts and Reminders to NextCloud</title>
    <link href="https://kvibber.com/tech-tips/mac-nextcloud-cal-card/"/>
    <author>
      <name>Kelson Vibber</name>
      <email>techtips@kvibber.com</email>
      <uri>https://kvibber.com</uri>
    </author>
    <category term="apple-mac"/>
    <category term="howto"/>
    <category term="techtips"/>
    <category term="nextcloud"/><category term="macos"/><category term="calendar"/><category term="contacts"/><category term="to-do-list"/><category term="productivity"/><category term="self-hosting"/>
    <summary>Current versions of macOS hide the options to add a CalDAV or CardDAV account behind a couple of &#39;more...&#39; links, and it still asks for an email address when you get there. Or you can download a Device Management profile if you know where to look.</summary>
    <updated>2026-03-13T00:39:43Z</updated>
    <id>https://kvibber.com/tech-tips/mac-nextcloud-cal-card/</id>
    <content type="html">
    &lt;p&gt;&lt;a href=&quot;https://kvibber.com/reviews/software/nextcloud-calendar/&quot;&gt;Nextcloud Calendar&lt;/a&gt; and Contacts are solid cross-platform alternatives to iCloud’s equivalents, and Tasks is good enough I’ve been using it for several years.&lt;/p&gt;
&lt;aside&gt;&lt;p&gt;The main problem with Nextcloud Tasks is its &lt;a href=&quot;https://kvibber.com/tech-tips/nextcloud-tasks/&quot;&gt;flaky handling of recurring tasks&lt;/a&gt;, though if you use an app like Reminders that &lt;em&gt;does&lt;/em&gt; understand repeating, the server can (mostly) handle it.&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;&lt;strong&gt;On macOS, System Settings &amp;gt; Internet Accounts&lt;/strong&gt; manages logins for all the built-in applications like &lt;a href=&quot;https://kvibber.com/reviews/software/apple-mail/&quot;&gt;Apple Mail&lt;/a&gt;, Calendar, Reminders and Contacts. Setting these up with Nextcloud is easy, but it’s not &lt;em&gt;intuitive&lt;/em&gt;, because Apple has been optimizing the setup process for all-in-one services like iCloud, Google or Microsoft.&lt;/p&gt;
&lt;aside&gt;&lt;p&gt;The Nextcloud desktop app isn’t used for this, since it mainly handles file sync.&lt;/p&gt;
&lt;/aside&gt;
&lt;h2&gt;Option 1: Download a Device Profile&lt;/h2&gt;
&lt;p&gt;On your Nextcloud server’s website, open your account settings, then go to the &lt;strong&gt;Mobile &amp;amp; Desktop&lt;/strong&gt; section. Below the app store links, look for &lt;strong&gt;“Download macOS/iOS configuration profile.”&lt;/strong&gt; That link will download a config with all the info except your password. (You &lt;a href=&quot;https://kvibber.com/tech-tips/nextcloud-sync-token/&quot;&gt;should generate an app-specific one&lt;/a&gt;, but it’s not necessary.)&lt;/p&gt;
&lt;p&gt;On your Mac, open &lt;strong&gt;System Settings&lt;/strong&gt;, go to &lt;strong&gt;General &amp;gt; Device Management&lt;/strong&gt; and approve the Nextcloud profile. It’ll ask for the password twice because you’re still technically logging into two services, one for calendar and one for contacts.&lt;/p&gt;
&lt;h2&gt;Option 2: Add the Accounts Manually&lt;/h2&gt;
&lt;p&gt;If you don’t want to use Device Management, or run into trouble getting it to work, you can still set up the accounts in &lt;strong&gt;System Settings &amp;gt; Internet Accounts&lt;/strong&gt;. The process is simple, if a little misleading.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First:&lt;/strong&gt; Starting with macOS 26 (Tahoe), it only asks you for an &lt;em&gt;email address&lt;/em&gt; when you press Add Account. To add a contacts/calendar account:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Click on &lt;strong&gt;“choose from a list.”&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Scroll down and click on &lt;strong&gt;“Add other account…”&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Now&lt;/em&gt; you can add a &lt;strong&gt;CalDAV&lt;/strong&gt; (Calendar/Reminders) or &lt;strong&gt;CardDAV&lt;/strong&gt; (Contacts) account.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Alternatively:&lt;/strong&gt; go through the Apple Calendar or Contacts apps’ settings and add the account there. It’ll still get added to the central set of accounts.&lt;/p&gt;
&lt;aside&gt;&lt;p&gt;This is &lt;em&gt;not&lt;/em&gt; the Accounts item on the application menu, which just brings up System Settings/Internet Accounts and puts you right back where we started, but the Accounts tab inside the applications’ Settings window.&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;&lt;strong&gt;Second:&lt;/strong&gt; It &lt;em&gt;still&lt;/em&gt; asks you for an email address when you tell it to add one of those account types!&lt;/p&gt;
&lt;p&gt;You can switch from &lt;strong&gt;Automatic to Manual&lt;/strong&gt; in the dropdown, and it’ll ask for a username and server… or you can just enter the equivalent of &lt;code&gt;username@server.example.com&lt;/code&gt; (even if it isn’t a real email address) and your (&lt;a href=&quot;https://kvibber.com/tech-tips/nextcloud-sync-token/&quot;&gt;preferably app-specific&lt;/a&gt;) password, and it’ll find the right details for your server.&lt;/p&gt;
&lt;p&gt;Easy, but not obvious!&lt;/p&gt;
&lt;aside&gt;&lt;p&gt;The last time I did this was a few macOS versions back, and I vaguely recall having to look up at least one of the specific URLs. Though it’s possible I’m just getting that mixed up with old versions of &lt;a href=&quot;https://kvibber.com/reviews/software/thunderbird/&quot;&gt;Thunderbird&lt;/a&gt;.&lt;/p&gt;
&lt;/aside&gt;
&lt;footer&gt;This post by &lt;a class=&quot;p-author h-card&quot; rel=&quot;author&quot; href=&quot;https://kvibber.com&quot;&gt;Kelson Vibber&lt;/a&gt; originally appeared &lt;a class=&quot;u-url&quot; href=&quot;https://kvibber.com/tech-tips/mac-nextcloud-cal-card/&quot;&gt;on KV Tech Tips&lt;/a&gt;&lt;/footer&gt;</content>
  </entry>
  
  
  
  <entry>
    <title>Using Unicode Half-Stars Symbols in Ratings</title>
    <link href="https://kvibber.com/tech-tips/half-stars/"/>
    <author>
      <name>Kelson Vibber</name>
      <email>techtips@kvibber.com</email>
      <uri>https://kvibber.com</uri>
    </author>
    <category term="web-development"/>
    <category term="howto"/>
    <category term="techtips"/>
    <category term="css"/><category term="fonts"/><category term="unicode"/><category term="typography"/>
    <summary>Filled and outlined stars have been in Unicode forever. Half-stars have been available since Unicode 11 (2018), but font support is still spotty. Fortunately you can embed a font on your website that *does* support the symbols and rate something four and a half stars.</summary>
    <updated>2026-03-03T01:01:17Z</updated>
    <id>https://kvibber.com/tech-tips/half-stars/</id>
    <content type="html">
    &lt;p&gt;When I started &lt;a href=&quot;https://kvibber.com/reviews/&quot;&gt;collecting reviews in one section&lt;/a&gt; of my website, I decided to stick with the goals I had here on the Tech Tips section: Keep the pages small, avoid loading scripts or extra images where styles or font symbols would do. So I kept using emoji for category icons, and used the outlined and filled star symbols (☆★) for ratings. They’ve been in Unicode forever, so they’re supported almost everywhere.&lt;/p&gt;
&lt;h2&gt;Halfway There&lt;/h2&gt;
&lt;p&gt;Every once in a while, I really want to rate something &lt;em&gt;between&lt;/em&gt; a whole number of stars. I let it round down for a while until I went looking to see if there were half-filled star symbols. And there are! Half-stars and half-filled stars (in both directions) were &lt;a href=&quot;https://www.righto.com/2016/10/inspired-by-hn-comment-four-half-star.html&quot;&gt;proposed in 2016&lt;/a&gt; and &lt;a href=&quot;https://www.unicode.org/charts/PDF/Unicode-11.0/U110-2B00.pdf&quot;&gt;added to Unicode 11 in 2018&lt;/a&gt;. You can see them here:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;⯨⯩⯪⯫&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;…or maybe you can’t, because even after eight years, font support for them is &lt;em&gt;still&lt;/em&gt; spotty.&lt;/p&gt;
&lt;p&gt;As of March 2026:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows 11 shows the symbols.&lt;/li&gt;
&lt;li&gt;Linux distros might, depending on what fonts are installed. Modern distros at least &lt;em&gt;have&lt;/em&gt; fonts that support these symbols, in packages like noto-fonts (Arch), fonts-noto-core (Debian) or google-noto-sans-symbols-2-fonts (Fedora), but whether they’re installed by default seems to vary.&lt;/li&gt;
&lt;li&gt;Windows 10, macOS/iOS 26 and Android don’t, even though they include newer emoji than Unicode 11.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So if you’re trying to use these on a web page, it’s really hit and miss whether your readers will be able to see the half-star symbols or not.&lt;/p&gt;
&lt;aside&gt;&lt;p&gt;It would be nice if you could, in HTML, &lt;a href=&quot;https://notes.kvibber.com/@kelson/statuses/01JRKBPXDMXTDKRREVBXKWAKAK&quot;&gt;provide a fallback for font glyphs&lt;/a&gt; that aren’t available (like how &lt;code&gt;alt&lt;/code&gt; was originally supposed to replace an image entirely if the image couldn’t be loaded), but as near as I can that’s never been seriously proposed.&lt;/p&gt;
&lt;/aside&gt; 
&lt;p&gt;The good news is, you can embed a font on your page (or app) that &lt;em&gt;does&lt;/em&gt; support the missing symbols! &lt;a href=&quot;https://fonts.google.com/noto/specimen/Noto+Sans+Symbols+2/&quot;&gt;Noto Sans Symbols 2&lt;/a&gt; contains the full set, and can be used freely. Embedding that font and using it for the ratings solves the problem.&lt;/p&gt;
&lt;blockquote class=&quot;stars&quot;&gt; ⯨⯩⯪⯫&lt;/blockquote&gt;
&lt;p&gt;That said, it &lt;em&gt;is&lt;/em&gt; an extra 200 KB or so, which is a bit overkill for adding one very simple bit of line art. (See the &lt;a href=&quot;https://kvibber.com/tech-tips/half-stars/#subset&quot;&gt;Subset&lt;/a&gt; and &lt;a href=&quot;https://kvibber.com/tech-tips/half-stars/#alternatives&quot;&gt;Alternatives&lt;/a&gt; sections below.)&lt;/p&gt;
&lt;h2&gt;Adding Font Support&lt;/h2&gt;
&lt;p&gt;The simplest thing to do, if you’re not worried about Google potentially tracking visitors to your site (see &lt;a href=&quot;https://kvibber.com/tech-tips/half-stars/#self-hosting&quot;&gt;self-hosting&lt;/a&gt; below for an alternative), is just embed the font from Google by putting this in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section of your page:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;
&amp;lt;link rel=&amp;quot;preconnect&amp;quot; href=&amp;quot;https://fonts.googleapis.com&amp;quot;&amp;gt;
&amp;lt;link rel=&amp;quot;preconnect&amp;quot; href=&amp;quot;https://fonts.gstatic.com&amp;quot; crossorigin&amp;gt;
&amp;lt;link href=&amp;quot;https://fonts.googleapis.com/css2?family=Noto+Sans+Symbols+2&amp;amp;display=swap&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then in your stylesheet, write something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.stars {font-family: &amp;quot;Noto Sans Symbols 2&amp;quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you can write your star ratings like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;span class=&quot;stars&quot;&amp;gt;&lt;span class=&quot;stars&quot;&gt;★★★⯪☆&lt;/span&gt;&amp;lt/span&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;span class=&quot;stars&quot;&gt;★★★⯪☆&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;I actually go a bit further on mine for accessibility and &lt;a href=&quot;https://microformats.org/wiki/h-review&quot;&gt;microformats&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;abbr class=&quot;stars p-rating&quot;
  title=&quot;3.5 stars out of 5&quot;
  aria-label=&quot;3.5 stars out of 5.&quot;
  value=&quot;3.5&quot;&amp;gt;&lt;span class=&quot;stars&quot;&gt;★★★⯪☆&lt;/span&gt;&amp;lt;/abbr&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;abbr class=&quot;stars p-rating&quot; value=&quot;3.5&quot; title=&quot;3.5 stars out of 5&quot; aria-label=&quot;3.5 stars out of 5.&quot;&gt;★★★⯪☆&lt;/abbr&gt;&lt;/blockquote&gt;
&lt;h2&gt;&lt;a name=&quot;html-codes&quot;&gt;&lt;/a&gt;HTML Codes&lt;/h2&gt;
&lt;p&gt;If you’re writing on a system that doesn’t support all the star characters natively, you can use numeric HTML entities. It’ll be just as unreadable as the tofu, but you’ll be able to tell them apart from each other.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;amp;#9733;&lt;/code&gt; or &lt;code&gt;&amp;amp;#x2605;&lt;/code&gt;	&lt;span class=&quot;stars&quot;&gt;★&lt;/span&gt;	Black star&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;amp;#9734;&lt;/code&gt; or &lt;code&gt;&amp;amp;#x2606;&lt;/code&gt;	&lt;span class=&quot;stars&quot;&gt;☆&lt;/span&gt;	White Star&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;amp;#11240;&lt;/code&gt; or &lt;code&gt;&amp;amp;#x2BE8;&lt;/code&gt;	&lt;span class=&quot;stars&quot;&gt;⯨&lt;/span&gt;	Left half black star&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;amp;#11241;&lt;/code&gt; or &lt;code&gt;&amp;amp;#x2BE9;&lt;/code&gt;	&lt;span class=&quot;stars&quot;&gt;⯩&lt;/span&gt;	Right half black star&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;amp;#11242;&lt;/code&gt; or &lt;code&gt;&amp;amp;#x2BEA;&lt;/code&gt;	&lt;span class=&quot;stars&quot;&gt;⯪&lt;/span&gt;	Star with left half black&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;amp;#11243;&lt;/code&gt; or &lt;code&gt;&amp;amp;#x2BEB;&lt;/code&gt;	&lt;span class=&quot;stars&quot;&gt;⯫&lt;/span&gt;	Star with right half black&lt;/li&gt;
&lt;/ul&gt;
&lt;aside&gt;&lt;p&gt;Unicode naming schemes are rooted in print, so a filled-in star is a black star, and an outline is a white star, assuming black ink on a white page.&lt;/p&gt;
&lt;/aside&gt;
&lt;h2&gt;&lt;a name=&quot;self-hosting&quot;&gt;&lt;/a&gt;Self-Hosting the Font&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://developers.google.com/fonts/faq/privacy&quot;&gt;Google Fonts Privacy Policy&lt;/a&gt; states that they don’t do any tracking on the font requests and keep them completely separate from any account-related traffic. But if you want to be &lt;em&gt;absolutely certain&lt;/em&gt;, or need to comply with regulations like GDPR, you can host your own copy.&lt;/p&gt;
&lt;aside&gt;&lt;p&gt;The Noto fonts are licenced for free reuse and embedding, and the Google Fonts site explicitly says you can self-host (but would rather you didn’t).&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;They don’t make it &lt;em&gt;easy&lt;/em&gt;, though.&lt;/p&gt;
&lt;h3&gt;Option 1: Download and Convert from TTF&lt;/h3&gt;
&lt;p&gt;If you download the font from Google Fonts, you get the complete TrueType font, which in this case is about 6 times as big as the WOFF2 font for web embedding. The .zip at &lt;a href=&quot;https://github.com/notofonts/symbols&quot;&gt;the font’s GitHub page&lt;/a&gt; also includes smaller versions, closer to 400KB, in the “hinted” and “unhinted” folders. (In this case they’re the same, but for other fonts you’ll probably want the version with hinting.)&lt;/p&gt;
&lt;p&gt;You can use an online TrueType to WOFF2 converter, or you can use an offline tool.&lt;/p&gt;
&lt;p&gt;The command-line &lt;a href=&quot;https://github.com/google/woff2&quot;&gt;woff2_compress&lt;/a&gt; tool is available in Homebrew for macOS and various Linux distributions in the woff2 (&lt;a href=&quot;https://kvibber.com/reviews/software/debian/&quot;&gt;Debian&lt;/a&gt;, &lt;a href=&quot;https://kvibber.com/reviews/software/alpine-linux/&quot;&gt;Alpine&lt;/a&gt;, Homebrew, etc) or woff2-tools (&lt;a href=&quot;https://kvibber.com/reviews/software/fedora/&quot;&gt;Fedora&lt;/a&gt;) packages.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;woff2_compress NotoSansSymbols2-Regular.ttf
&lt;/code&gt;&lt;/pre&gt;
&lt;aside&gt;&lt;p&gt;These days, &lt;a href=&quot;https://caniuse.com/woff2&quot;&gt;WOFF2 is effectively baseline&lt;/a&gt; across modern web browsers, so you don’t need to include any fallback formats anymore.&lt;/p&gt;
&lt;/aside&gt;
&lt;h3&gt;&lt;a name=&quot;subset&quot;&gt;&lt;/a&gt;Option 1A: Create a Subset First&lt;/h3&gt;
&lt;p&gt;If you’re &lt;em&gt;only&lt;/em&gt; going to use this font for the star glyphs, you can create a subset of the font, which can be &lt;em&gt;much&lt;/em&gt; smaller. Again, there are plenty of online tools, but I used a Node-based commandline tool called glyphhanger:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir fonts
cd fonts
cp /path/to/NotoSansSymbols2-Regular.ttf .
npm install glyphhanger
npx glyphhanger --whitelist=&amp;quot;&lt;span class=&quot;stars&quot;&gt;⯨⯩⯪⯫☆★&lt;/span&gt;&amp;quot; --subset=NotoSansSymbols2-Regular.ttf
mv NotoSansSymbols2-Regular-subset.ttf noto-stars-subset.ttf
woff2_compress noto-stars-subset.ttf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates a font with &lt;em&gt;only&lt;/em&gt; the characters in the whitelist. The TrueType file is only 1.6KB, and the WOFF2 is only 780 bytes!&lt;/p&gt;
&lt;p&gt;Then I copied the files out, and deleted the whole folder because glyphhanger seems to depend on some vulnerable packages.&lt;/p&gt;
&lt;h3&gt;Option 2: Find a Pre-Built WOFF2 file&lt;/h3&gt;
&lt;p&gt;Or you can grab a copy of the WOFF2 file from Google Fonts. The simplest way is to open their font stylesheet (the one in the third &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; above) directly, scroll down to the &lt;code&gt;/* symbols */&lt;/code&gt; section, copy the URL from that, and save the .woff2 file. Preferably giving it a name you can recognize later on.&lt;/p&gt;
&lt;aside&gt;&lt;p&gt;All the sections point to the same font file right now, but if they ever split up the hosted file, you’ll want to make sure you get the one with general symbols.&lt;/p&gt;
&lt;/aside&gt;
&lt;h3&gt;Embedding Your Copy&lt;/h3&gt;
&lt;p&gt;You won’t need the entire set of font definitions and Unicode ranges, because you’re using it in narrow circumstances. And you won’t need the &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; elements pointing to Google. Though you may need to keep the &lt;code&gt;&amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;/code&gt; depending on your hosting setup. In any case, you’ll want to add this to your stylesheet:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@font-face {
  font-family: &amp;quot;Noto Sans Symbols 2&amp;quot;;
  font-display: swap;
  src: url(/path/to/your/copy/of/NotoSansSymbols2-Regular.woff2);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;font-display: swap&lt;/code&gt; tells browsers to keep rendering the page if it takes a while to load the font, and swap it in when it’s ready.&lt;/p&gt;
&lt;p&gt;On larger pages especially, you may also want to &lt;a href=&quot;https://kvibber.com/tech-tips/webfont-doubled/&quot;&gt;preload the font&lt;/a&gt; so the browser will start downloading it as soon as it can, instead of waiting until it’s downloaded and parsed the stylesheet.&lt;/p&gt;
&lt;h2&gt;&lt;a name=&quot;alternatives&quot;&gt;&lt;/a&gt;Alternatives&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Use images instead of font symbols. (PNGs or SVGs would be tiny.)&lt;/li&gt;
&lt;li&gt;Use an icon font like &lt;a href=&quot;https://fontawesome.com/icons/star-half-stroke?f=classic&amp;amp;s=solid&quot;&gt;Font Awesome&lt;/a&gt; that provides its own symbols. (Download size is comparable to embedding Noto Sans Symbol 2, but you can also use their icons as SVGs, which can be a &lt;em&gt;lot&lt;/em&gt; smaller if you only need a handful.)&lt;/li&gt;
&lt;li&gt;Embed the font, but only on the individual pages that need it. (That’s what I’ve done for this page, even before subsetting just the stars.)&lt;/li&gt;
&lt;li&gt;Use a more widely-supported symbol like ½ instead. (This is what I initially did on my reviews.)&lt;/li&gt;
&lt;li&gt;Use a fraction symbol, and then use JavaScript to check whether the half-star is supported in the current font, and swap it in if it’s available. (Or the other way around, but this is more backward compatible.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then again, ~200 KB is a fraction of the &lt;a href=&quot;https://www.pingdom.com/blog/webpages-are-getting-larger-every-year-and-heres-why-it-matters/&quot;&gt;average page size&lt;/a&gt; these days. More than a drop in the bucket, but hardly a dealbreaker. Even if it is 10 times the size &lt;em&gt;this&lt;/em&gt; page would be without it.&lt;/p&gt;
&lt;h2&gt;Notes&lt;/h2&gt;
&lt;aside&gt;&lt;p&gt;&lt;strong&gt;2026-03-02:&lt;/strong&gt; Initial write-up.&lt;br&gt;
&lt;strong&gt;2026-05-05:&lt;/strong&gt; Add section on creating a subset using glyphhanger.&lt;/p&gt;
&lt;/aside&gt;
&lt;footer&gt;This post by &lt;a class=&quot;p-author h-card&quot; rel=&quot;author&quot; href=&quot;https://kvibber.com&quot;&gt;Kelson Vibber&lt;/a&gt; originally appeared &lt;a class=&quot;u-url&quot; href=&quot;https://kvibber.com/tech-tips/half-stars/&quot;&gt;on KV Tech Tips&lt;/a&gt;&lt;/footer&gt;</content>
  </entry>
</feed>
