<?xml version="1.0" encoding="utf-8" standalone="yes"?><?xml-stylesheet type="text/xsl" href="/pretty-feed-v3.xsl"?>
<rss
  version="2.0"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:source="https://source.scripting.com/"
>
  <channel>
    <title>Paul Tibbetts</title>
    <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/</link>
    <description>Recent posts by Paul Tibbetts</description>
    <generator>Hugo</generator>
    <language>en-gb</language>
    <lastBuildDate>Tue, 07 Jul 2026 15:08:33 +0100</lastBuildDate>
    <atom:link href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/feed.xml" rel="self" type="application/rss+xml" /><item>
      <title>Cooked Pulled pork</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/cooks/2026-07-03-pulled-pork/</link>
      <pubDate>Fri, 03 Jul 2026 13:00:00 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/cooks/2026-07-03-pulled-pork/</guid>
      <description>Pulled pork Duration 9h 34m Time 12:51-22:26 Pulled pork -5C 36C 78C 119C 160C 12:51 16:03 19:14 22:26 Grill Pork Probe First Last Min Max Grill 26.8C 25.9C 25.7C 146C Pork 28.4C 29.7C 9.4C 114.4C</description>
      <content:encoded><![CDATA[<section class="fireboard-archive not-prose max-w-none">
    <div class="fireboard-archive-header">
      <h2>Pulled pork</h2>
      <dl style="display: flex">
        <div>
          <dt>Duration</dt>
          <dd>9h 34m</dd>
        </div>
        <div>
          <dt>Time</dt>
          <dd>12:51-22:26</dd>
        </div>
      </dl>
    </div>

    <figure>
      <svg
        aria-labelledby="fireboard-archive-title-0"
        class="fireboard-archive-chart"
        role="img"
        viewBox="0 0 800 320"
      >
        <title id="fireboard-archive-title-0">Pulled pork</title>
          <line
            class="fireboard-archive-grid"
            x1="48"
            x2="784"
            y1="284"
            y2="284"
          />
          <text class="fireboard-archive-axis" x="0" y="288">-5C</text>
          <line
            class="fireboard-archive-grid"
            x1="48"
            x2="784"
            y1="217.4"
            y2="217.4"
          />
          <text class="fireboard-archive-axis" x="0" y="221.4">36C</text>
          <line
            class="fireboard-archive-grid"
            x1="48"
            x2="784"
            y1="149.2"
            y2="149.2"
          />
          <text class="fireboard-archive-axis" x="0" y="153.2">78C</text>
          <line
            class="fireboard-archive-grid"
            x1="48"
            x2="784"
            y1="82.6"
            y2="82.6"
          />
          <text class="fireboard-archive-axis" x="0" y="86.6">119C</text>
          <line
            class="fireboard-archive-grid"
            x1="48"
            x2="784"
            y1="16"
            y2="16"
          />
          <text class="fireboard-archive-axis" x="0" y="20">160C</text>
          <text
            class="fireboard-archive-axis"
            text-anchor="middle"
            x="48"
            y="312"
          >
            12:51
          </text>
          <text
            class="fireboard-archive-axis"
            text-anchor="middle"
            x="293.3"
            y="312"
          >
            16:03
          </text>
          <text
            class="fireboard-archive-axis"
            text-anchor="middle"
            x="538.7"
            y="312"
          >
            19:14
          </text>
          <text
            class="fireboard-archive-axis"
            text-anchor="middle"
            x="784"
            y="312"
          >
            22:26
          </text>
          <polyline
            class="fireboard-archive-line"
            fill="none"
            points="48,232.3 48.1,232.3 49.4,190.8 49.5,190.8 50.7,167.4 50.8,167.4 52.1,155.5 52.2,155.5 53.4,150.3 53.6,150.3 54.7,147.6 54.8,147.6 56.1,145.6 56.2,145.6 57.4,145 57.5,145 58.8,159.6 58.9,159.6 60.1,187.5 60.2,187.5 61.3,203.4 61.5,203.4 62.7,205.4 62.8,205.4 64.1,193.2 64.2,193.2 65.5,180.5 65.6,180.5 66.8,173.6 66.9,173.6 67,172.3 67.1,172.3 68.1,181.8 68.2,181.8 69.5,185.1 69.6,185.1 70.7,97 70.8,97 72.1,85.2 72.2,85.2 73.4,42.5 73.5,42.5 74.8,53.5 74.9,53.5 76.2,65.7 76.3,65.7 77.6,73 77.7,73 78.9,77.1 79,77.1 80.2,78.9 80.4,78.9 81.6,78.2 81.7,78.2 83,77.7 83.1,77.7 84.3,76.6 84.4,76.6 85.7,76.3 85.8,76.3 87,75.1 87.1,75.1 88.4,73.5 88.5,73.5 89.6,68 89.7,68 90.9,66.4 91,66.4 92.3,65.5 92.4,65.5 93.7,64.2 93.8,64.2 95.1,65.1 95.2,65.1 96.4,65.2 96.5,65.2 97.8,66.2 97.9,66.2 99,65.4 99.1,65.4 100.4,66 100.5,66 101.7,83.6 101.8,83.6 103,172.3 103.1,172.3 104.3,176.6 104.4,176.6 105.7,176 105.8,176 106.9,174.7 107,174.7 108.2,173.4 108.3,173.4 109.5,172.1 109.6,172.1 110.9,171.1 111,171.1 112.2,176.6 112.3,176.6 113.5,121.9 113.6,121.9 114.7,93.3 114.8,93.3 116.1,82.3 116.2,82.3 117.4,81.5 117.5,81.5 118.4,80.5 118.5,80.5 118.7,80.6 118.8,80.6 120,80.2 120.1,80.2 121.2,80.5 121.4,80.5 122.6,80 122.7,80 123.9,79.2 124,79.2 125.2,80 125.3,80 126.5,81.9 126.6,81.9 127.8,84.1 127.9,84.1 129.1,84.9 129.3,84.9 130.4,85.8 130.5,85.8 131.7,88.3 131.8,88.3 133,89.4 133.1,89.4 134.4,91.7 134.5,91.7 135.7,91.5 135.8,91.5 136.9,92.8 137.1,92.8 138.2,95.1 138.3,95.1 139.5,96.2 139.6,96.2 140.9,98.7 141,98.7 142.2,100.6 142.3,100.6 143.5,104 143.6,104 144.7,107.9 144.8,107.9 146.1,110.2 146.2,110.2 147.5,111.8 147.6,111.8 148.8,122.1 148.9,122.1 150.1,137 150.2,137 151.4,166.2 151.5,166.2 152.7,167.7 152.9,167.7 154,125.1 154.1,125.1 155.3,145.1 155.4,145.1 156.6,98.5 156.7,98.5 157.9,96.2 158,96.2 159.3,95.8 159.4,95.8 160.5,96.9 160.6,96.9 161.8,97.4 161.9,97.4 163.1,97.7 163.2,97.7 164.4,98 164.5,98 165.8,99 165.9,99 167.1,97.9 167.2,97.9 168.3,67.2 168.4,67.2 169.6,73.2 169.7,73.2 171,78.5 171.1,78.5 172.3,82.1 172.4,82.1 173.6,84.9 173.7,84.9 174.8,87.5 175,87.5 176,89.3 176.1,89.3 176.2,89.6 176.3,89.6 177.5,102.2 177.6,102.2 178.9,100.9 179,100.9 180.2,101.8 180.3,101.8 181.5,98.8 181.6,98.8 182.8,97.4 182.9,97.4 184.1,96.7 184.2,96.7 185.4,98.3 185.5,98.3 186.7,100 186.8,100 188,103.1 188.1,103.1 189.3,104.8 189.4,104.8 190.7,106.1 190.8,106.1 191.9,111.2 192,111.2 193.3,110.9 193.4,110.9 194.6,118 194.7,118 195.9,119 196,119 197.2,119.5 197.3,119.5 198.6,119.3 198.7,119.3 199.9,111.8 200,111.8 201.2,112.2 201.3,112.2 202.5,106.3 202.6,106.3 203.9,101.9 204,101.9 205.2,96.4 205.3,96.4 206.5,91.9 206.6,91.9 207.7,91.2 207.8,91.2 209,91.7 209.1,91.7 210.4,91.9 210.5,91.9 211.7,93.6 211.8,93.6 213.1,93.2 213.2,93.2 214.4,96.7 214.5,96.7 215.6,97.7 215.7,97.7 216.9,97.5 217,97.5 218.3,98.5 218.4,98.5 219.6,101.1 219.7,101.1 220.9,103.5 221,103.5 222.2,105.5 222.3,105.5 222.5,105.7 222.6,105.7 223.5,106.8 223.6,106.8 224.8,117.8 224.9,117.8 226.1,118.3 226.2,118.3 227.4,117.4 227.5,117.4 228.7,117.4 228.8,117.4 230.1,117 230.2,117 231.3,117 231.4,117 232.6,117.4 232.7,117.4 233.9,116.9 234,116.9 235.3,116.5 235.4,116.5 236.6,116.1 236.7,116.1 238,114.9 238.1,114.9 239.3,114.9 239.4,114.9 240.6,113.9 240.7,113.9 241.9,113.9 242,113.9 243.3,113.1 243.4,113.1 244.6,125.3 244.7,125.3 245.9,102.9 246,102.9 247.2,107 247.3,107 248.5,109.7 248.6,109.7 249.8,110.2 249.9,110.2 251.1,110 251.2,110 252.5,111.3 252.6,111.3 253.8,112.2 253.9,112.2 255.1,111.8 255.3,111.8 256.4,113.3 256.5,113.3 257.7,113.9 257.8,113.9 259,114.9 259.1,114.9 260.4,114.6 260.5,114.6 261.7,114.9 261.8,114.9 262.9,114.6 263,114.6 264.3,116.7 264.4,116.7 265.6,118.5 265.7,118.5 266.9,120.9 267,120.9 268.2,136.8 268.3,136.8 269.6,132.9 269.7,132.9 270.8,130.3 270.9,130.3 272.1,127.7 272.2,127.7 272.4,127.1 272.5,127.1 273.4,125 273.5,125 274.7,123.2 274.8,123.2 276.1,120.6 276.2,120.6 277.4,118.7 277.5,118.7 278.6,116.7 278.7,116.7 279.9,114.6 280,114.6 281.4,112.5 281.5,112.5 282.7,110.4 282.8,110.4 284,110 284.1,110 285.4,110 285.5,110 286.6,111.2 286.7,111.2 287.9,94 288,94 289.2,84.5 289.3,84.5 290.5,84.4 290.6,84.4 291.9,81.1 292,81.1 293.2,74.6 293.3,74.6 294.4,78.7 294.5,78.7 295.7,77.7 295.8,77.7 297,84.7 297.1,84.7 298.4,86.3 298.5,86.3 299.7,87.8 299.8,87.8 300.9,89.6 301.1,89.6 302.3,90.9 302.4,90.9 303.6,99.8 303.7,99.8 305,98.3 305.1,98.3 306.4,98.2 306.5,98.2 307.7,98.2 307.8,98.2 309,97.2 309.1,97.2 310.2,97.5 310.3,97.5 311.5,99 311.6,99 312.9,99.6 313,99.6 314.2,99.2 314.3,99.2 315.5,101.3 315.6,101.3 316.8,102.6 316.9,102.6 318,104.4 318.1,104.4 319.4,104.8 319.5,104.8 320.7,104.8 320.8,104.8 322,103.9 322.1,103.9 323.3,103.5 323.4,103.5 324.5,103.4 324.7,103.4 325.9,103.2 326,103.2 327.2,103.2 327.3,103.2 328.5,102.9 328.6,102.9 329.8,103.1 329.9,103.1 331.1,104.4 331.2,104.4 332.4,107.3 332.6,107.3 333.7,110 333.8,110 335,112.2 335.1,112.2 336.4,113.6 336.5,113.6 337.7,115.4 337.8,115.4 339.1,116.5 339.2,116.5 340.5,117.2 340.6,117.2 341.7,136.5 341.8,136.5 343,133.4 343.1,133.4 344.3,132.3 344.4,132.3 345.5,135.9 345.6,135.5 345.7,135.5 347,133.3 347.1,133.3 348.3,131.6 348.4,131.6 349.5,136.2 349.6,136.2 350.8,125.5 350.9,125.5 352.1,123.2 352.2,123.2 353.5,122.4 353.6,122.4 354.8,121.1 354.9,121.1 356,119.6 356.2,119.6 357.3,117.5 357.4,117.5 358.7,114.6 358.8,114.6 360,112 360.1,112 361.3,106 361.4,106 362.6,103.2 362.7,103.2 364.1,101.3 364.2,101.3 365.4,100.1 365.5,100.1 366.8,104.4 366.9,104.4 368.2,88 368.3,88 369.5,83.7 369.6,83.7 370.9,87.1 371,87.1 372.3,88.1 372.4,88.1 373.6,88.3 373.7,88.3 374.8,85.5 374.9,85.5 376.1,73 376.2,73 377.4,72 377.5,72 378.8,74 378.9,74 380.1,74.8 380.2,74.8 381.4,73.8 381.5,73.8 382.6,72.5 382.7,72.5 383.9,72.2 384,72.2 385.3,72.8 385.4,72.8 386.6,73.3 386.7,73.3 387.9,74.1 388,74.1 389.1,75.1 389.3,75.1 390.4,76.3 390.5,76.3 391.8,75.8 391.9,75.8 393.2,75.1 393.3,75.1 394.5,75.4 394.6,75.4 395.8,77.2 395.9,77.2 397,79.2 397.2,79.2 398.3,79.7 398.4,79.7 399.7,80 399.8,80 401,81 401.1,81 402.4,82.6 402.5,82.6 403.7,83.1 403.8,83.1 404.9,83.4 405.1,83.4 406.2,83.6 406.3,83.6 407.5,84.7 407.6,84.7 408.9,85.7 409,85.7 410.2,86.5 410.3,86.5 411.6,88 411.7,88 412.9,89.3 413,89.3 414.1,90.7 414.2,90.7 415.4,93.2 415.5,93.2 416.8,96.6 416.9,96.6 418.1,98 418.2,98 419.4,99.5 419.5,99.5 420.6,101.3 420.8,101.3 421.9,102.6 422,102.6 423.3,102.7 423.4,102.7 424.6,103.5 424.7,103.5 425.9,103.9 426,103.9 427.2,104.7 427.3,104.7 428.4,105.8 428.5,105.8 429.8,107.3 429.9,107.3 431.1,107.4 431.2,107.4 432.4,108.9 432.5,108.9 433.7,114.1 433.8,114.1 435,110.4 435.1,110.4 436.3,108.9 436.4,108.9 437.6,107.9 437.7,107.9 438.9,108.3 439,108.3 440.3,108.7 440.4,108.7 441.6,109.9 441.7,109.9 442.9,112 443,112 444.1,114.3 444.2,114.3 445.3,126.6 445.4,126.6 445.5,130 445.6,130 446.8,121.4 446.9,121.4 448.1,117 448.2,117 449.5,153.9 449.6,153.9 450.8,65.2 450.9,65.2 452,49.9 452.1,49.9 453.3,47 453.4,47 454.7,56.1 454.8,56.1 456,59 456.1,59 457.4,48.2 457.5,48.2 458.7,53.2 458.8,53.2 459.9,61.3 460,61.3 461.2,52.4 461.3,52.4 462.5,49.1 462.6,49.1 463.9,56 464,56 465.2,62.3 465.3,62.3 466.6,49 466.7,49 467.9,52.5 468.1,52.5 469.2,60 469.3,60 470.5,56.4 470.6,56.4 471.8,48.8 471.9,48.8 473.1,54.3 473.2,54.3 474.5,62.3 474.6,62.3 475.7,52.5 475.8,52.5 477,49.8 477.1,49.8 478.4,56.3 478.5,56.3 479.7,63.3 479.8,63.3 481,50.3 481.1,50.3 482.4,50.9 482.5,50.9 483.6,58.2 483.7,58.2 484.9,62.5 485,62.5 486.3,48.6 486.4,48.6 488,54.5 488.1,54.5 489.3,62.5 489.4,62.5 490.7,69.8 490.8,69.8 492.1,74.5 492.2,74.5 493.4,60.7 493.5,60.7 494.6,62.3 494.7,62.3 496,69.3 496.1,69.3 497.3,75.4 497.4,75.4 498.6,61.5 498.7,61.5 500,60.7 500.1,60.7 501.3,66.7 501.4,66.7 502.6,75 502.8,75 503.9,64.2 504,64.2 505.3,61.3 505.4,61.3 506.6,67.5 506.7,67.5 507.9,74.5 508,74.5 509.4,64.9 509.5,64.9 510.8,62 510.9,62 512.2,68.5 512.3,68.5 513.4,74.5 513.5,74.5 514.5,67.2 514.6,67.2 514.7,64.4 514.8,64.4 516.1,61 516.2,61 517.4,67.2 517.5,67.2 518.9,74.8 519,75.6 519.1,75.6 520.3,62.9 520.4,62.9 521.5,58.9 521.7,58.9 522.8,64.9 522.9,64.9 524.2,72.8 524.3,72.8 525.5,70.6 525.6,70.6 526.8,59.2 526.9,59.2 528.2,63.4 528.3,63.4 529.4,70.4 529.6,70.4 530.8,74.1 530.9,74.1 532.1,61 532.2,61 533.4,64.1 533.5,64.1 534.7,71.1 534.8,71.1 536.1,72.8 536.2,72.8 537.5,60.8 537.6,60.8 538.7,64.2 538.8,64.2 540,70.9 540.1,70.9 541.4,72.2 541.5,72.2 542.7,60.7 542.8,60.7 544,64.7 544.1,64.7 545.5,72.4 545.6,72.4 546.9,69.6 547,69.6 548.1,60.8 548.2,60.8 549.4,65.5 549.5,65.5 550.8,73.3 550.9,73.3 552.1,68 552.2,68 553.4,60.7 553.5,60.7 554.8,66.4 554.9,66.4 556.1,73.5 556.2,73.5 557.4,68.1 557.5,68.1 558.8,60 558.9,60 560.1,65.7 560.2,65.7 561.4,73 561.5,73 562.7,70.2 562.8,70.2 564,60.7 564.2,60.7 565.4,65.7 565.5,65.7 566.8,73.5 566.9,73.5 568.2,67 568.3,67 569.5,61.3 569.6,61.3 570.8,66.8 570.9,66.8 572.2,74.8 572.3,74.8 573.5,64.9 573.7,64.9 574.9,61.6 575,61.6 576.2,68 576.3,68 577.5,75 577.6,75 578.8,65.7 578.9,65.7 580.2,61.8 580.3,61.8 581.4,67.7 581.6,67.7 582.8,74.6 582.9,74.6 584.1,64.9 584.2,64.9 585.4,62 585.5,62 586.7,68 586.8,68 588.1,75.6 588.2,75.6 589.3,64.1 589.5,64.1 590.7,60.8 590.8,60.8 592,67 592.1,67 593.4,74.8 593.5,74.8 594.8,64.7 594.9,64.7 596.1,61 596.2,61 597.4,67 597.5,67 598.9,74.6 599,74.6 600.2,64.7 600.3,64.7 601.5,61.6 601.6,61.6 602.8,67.5 602.9,67.5 604.1,74.8 604.2,74.8 605.5,65.1 605.6,65.1 606.8,61.5 606.9,61.5 608.2,67.8 608.4,67.8 609.6,75.4 609.7,75.4 610.9,65.2 611,65.2 612.2,61.6 612.3,61.6 613.6,68.1 613.7,68.1 614.9,75.1 615,75.1 616.1,65.5 616.3,65.5 617.5,61.8 617.6,61.8 618.9,68.1 619,68.1 620.2,75.1 620.3,75.1 621.5,66.2 621.6,66.2 622.9,61.6 623,61.6 624.2,67.5 624.3,67.5 625.4,74.5 625.5,74.5 626.9,65.1 627,65.1 628.2,54.8 628.3,54.8 628.4,54.8 629.6,60.3 629.7,60.3 630.9,68 631,68 632.3,75.6 632.4,75.6 633.6,63.4 633.7,63.4 634.8,61 634.9,61 636.2,66.8 636.3,66.8 637.5,73.8 637.6,73.8 638.9,68.3 639,68.3 640.2,61.2 640.3,61.2 641.5,66.2 641.6,66.2 642.8,73.7 643,73.7 644.1,70.6 644.2,70.6 645.5,61 645.6,61 646.9,66.4 647,66.4 648.3,73.7 648.4,73.7 649.6,69.8 649.7,69.8 650.9,61.2 651,61.2 652.2,66.5 652.3,66.5 653.5,73.3 653.6,73.3 654.9,71.2 655,71.2 656.2,61 656.3,61 657.5,65.5 657.6,65.5 658.8,72.7 658.9,72.7 660.1,72.8 660.2,72.8 661.4,61.2 661.5,61.2 662.8,64.7 662.9,64.7 664.1,71.9 664.2,71.9 665.4,74.8 665.5,74.8 666.8,61.5 666.9,61.5 668,63.6 668.1,63.6 669.3,70.4 669.4,70.4 670.7,76.4 670.8,76.4 672,62.3 672.1,62.3 673.3,62.5 673.4,62.5 674.6,68.6 674.7,68.6 675.9,75.8 676.1,75.8 677.2,65.1 677.3,65.1 678.5,61.8 678.6,61.8 679.8,67.5 679.9,67.5 681.2,74.6 681.3,74.6 682.5,68.5 682.6,68.5 683.7,61 683.8,61 685,66.2 685.1,66.2 686.4,73.2 686.5,73.2 687.7,64.2 687.8,64.2 689,52.9 689.1,52.9 690.4,57.7 690.5,57.7 691.6,65.2 691.7,65.2 692.9,69.4 693,69.4 694.2,47.7 694.3,47.7 695.6,43 695.7,43 697,50.6 697.1,50.6 698.3,45.4 698.4,45.4 699.2,38.7 699.3,38.7 699.5,39.1 699.6,39.1 700.8,46 700.9,46 702.2,51.6 702.3,51.6 703.5,68.8 703.6,68.8 704.8,156.5 704.9,156.5 706.2,192.9 706.3,192.9 707.4,210.9 707.5,210.9 708.7,219.5 708.8,219.5 710,224.6 710.1,224.6 711.4,227.2 711.5,227.2 712.7,228.3 712.8,228.3 714,229.6 714.1,229.6 715.2,229.9 715.3,229.9 716.6,230.2 716.7,230.2 717.9,229.8 718,229.8 719.2,230.2 719.3,230.2 720.5,231.2 720.6,231.2 721.9,231.5 722,231.5 723.1,230.1 723.2,230.1 724.4,229.6 724.5,229.6 725.7,230.7 725.8,230.7 727.1,229.9 727.2,229.9 728.4,230.6 728.5,230.6 729.7,229.9 729.8,229.9 731,230.2 731.1,230.2 732.3,229.9 732.4,229.9 733.6,231.4 733.7,231.4 734.9,229.6 735,229.6 736.9,230.9 737,230.9 738.2,231.5 738.3,231.5 739.5,231.7 739.6,231.7 740.8,232.2 740.9,232.2 742,232.3 742.1,232.3 743.4,232.8 743.5,232.8 744.7,232.8 744.8,232.8 746,233 746.1,233 747.3,232.7 747.4,232.7 748.7,232.8 748.8,232.8 749.9,232.8 750,232.8 751.2,233.2 751.3,233.2 752.5,233.2 752.6,233.2 753.8,233 753.9,233 755.2,233 755.3,233 756.5,233.2 756.6,233.2 757.7,233.3 757.8,233.3 759,233.5 759.1,233.5 760.4,233.3 760.5,233.3 761.7,233.3 761.8,233.3 762.5,233.3 762.6,233.3 763,233.3 763.1,233.3 764.2,233.5 764.4,233.5 765.6,233.3 765.7,233.3 766.9,233.5 767,233.5 768.2,233.5 768.3,233.5 769.5,233.5 769.6,233.5 770.9,233.6 771,233.6 772.1,233.6 772.3,233.6 773.4,234 773.5,234 774.7,233.8 774.8,233.8 776.1,233.8 776.2,233.8 777.4,233.8 777.5,233.8 778.7,233.8 778.8,233.8 779.9,234.1 780,234.1 781.2,234 781.3,234 782.6,234.1 782.7,234.1 783.9,233.8 784,233.8"
            stroke="#ffb86c"
          />
          <polyline
            class="fireboard-archive-line"
            fill="none"
            points="101.4,229.8 101.5,229.8 102.7,260.6 102.8,260.6 104,260.6 104.1,260.6 105.3,260.6 105.4,260.6 106.6,260.4 106.7,260.4 107.9,260.4 108,260.4 109.2,260.3 109.3,260.3 110.6,260.3 110.7,260.3 111.9,260.1 112,260.1 113.1,260.1 113.2,260.1 114.4,260 114.5,260 115.7,259.8 115.8,259.8 117.1,259.6 117.2,259.6 118.4,259.5 118.5,259.5 119.6,259.3 119.8,259.3 120.9,259.1 121,259.1 122.3,259 122.4,259 123.6,258.8 123.7,258.8 124.7,258.7 124.8,258.7 124.9,258.5 125,258.5 126.2,258.2 126.3,258.2 127.4,257.8 127.5,257.8 128.8,257.5 128.9,257.5 130.1,257.2 130.2,257.2 131.4,256.9 131.5,256.9 132.7,256.4 132.8,256.4 134,255.9 134.1,255.9 135.3,255.4 135.4,255.4 136.6,254.9 136.7,254.9 137.9,254.3 138,254.3 139.2,253.6 139.3,253.6 140.6,253.1 140.7,253.1 141.9,252.5 142,252.5 143.1,251.8 143.2,251.8 144.4,251.2 144.5,251.2 145.7,250.5 145.8,250.5 147.1,249.7 147.2,249.7 148.4,249.1 148.5,249.1 149.6,248.4 149.8,248.4 150.9,247.6 151,247.6 152.2,247 152.3,247 153.6,246.3 153.7,246.3 154.9,245.5 155,245.5 156.2,244.9 156.3,244.9 157.6,244 157.7,244 158.9,243.4 159,243.4 160.2,242.6 160.3,242.6 161.5,241.9 161.6,241.9 162.8,241.3 162.9,241.3 164.1,240.6 164.2,240.6 165.5,239.8 165.6,239.8 166.7,239.2 166.8,239.2 168,238.5 168.1,238.5 169.4,237.7 169.5,237.7 170.8,237.1 170.9,237.1 171.3,236.9 171.4,236.9 172.1,236.4 172.2,236.4 173.4,235.8 173.5,235.8 174.6,235.1 174.7,235.1 175.9,234.5 176,234.5 177.3,233.8 177.4,233.8 178.7,233 178.8,233 180,232.3 180.1,232.3 181.3,231.7 181.4,231.7 182.5,230.9 182.6,230.9 183.8,230.1 183.9,230.1 185.2,229.3 185.3,229.3 186.5,228.5 186.6,228.5 187.8,227.6 187.9,227.6 189,226.8 189.2,226.8 190.4,225.9 190.5,225.9 191.7,224.9 191.8,224.9 193.1,223.9 193.2,223.9 194.4,222.9 194.5,222.9 195.7,222.1 195.8,222.1 197,221.1 197.1,221.1 198.3,220.2 198.4,220.2 199.7,219.2 199.8,219.2 201,218.4 201.1,218.4 202.3,217.6 202.4,217.6 203.7,216.8 203.8,216.8 205,215.9 205.1,215.9 206.2,215.1 206.3,215.1 207.5,214.3 207.6,214.3 208.8,213.5 208.9,213.5 210.2,212.7 210.3,212.7 211.5,212 211.6,212 212.9,211.2 213,211.2 214.1,210.4 214.2,210.4 215.4,209.8 215.5,209.8 216.7,209 216.8,209 218.1,208.3 218.2,208.3 219.4,207.7 219.5,207.7 220.7,206.8 220.8,206.8 221.9,206.2 222,206.2 223.3,205.4 223.4,205.4 224.6,204.7 224.7,204.7 225.9,204.1 226,204.1 227.2,203.3 227.3,203.3 227.4,203.3 227.5,203.3 228.4,202.6 228.6,202.6 229.8,202 229.9,202 231.1,201.3 231.2,201.3 232.4,200.7 232.5,200.7 233.7,200 233.8,200 235.1,199.2 235.2,199.2 236.4,198.6 236.5,198.6 237.7,198.1 237.8,198.1 239.1,197.4 239.2,197.4 240.4,196.8 240.5,196.8 241.7,196.1 241.8,196.1 243.1,195.6 243.2,195.6 244.4,195 244.5,195 245.6,194.5 245.7,194.5 247,193.9 247.1,193.9 248.3,193.4 248.4,193.4 249.6,192.9 249.7,192.9 250.9,192.2 251,192.2 252.3,191.7 252.4,191.7 253.5,191.3 253.6,191.3 254.9,190.8 255,190.8 256.2,190.3 256.3,190.3 257.5,189.8 257.6,189.8 258.8,189.3 258.9,189.3 260.2,188.8 260.3,188.8 261.4,188.3 261.5,188.3 262.7,187.8 262.8,187.8 264.1,187.4 264.2,187.4 265.4,186.9 265.5,186.9 266.5,186.7 266.6,186.7 266.7,186.5 266.8,186.5 268,186.1 268.1,186.1 269.2,185.6 269.3,185.6 270.6,185.2 270.7,185.2 271.9,184.8 272,184.8 273.2,184.3 273.3,184.3 274.5,183.9 274.6,183.9 275.9,183.6 276,183.6 277.1,183.1 277.2,183.1 278.4,182.8 278.5,182.8 279.7,182.5 279.8,182.5 281.4,182 281.5,182 282.7,181.7 282.8,181.7 284,181.3 284.1,181.3 285.4,181 285.5,181 286.6,180.7 286.7,180.7 287.9,180.4 288,180.4 289.2,180 289.3,180 290.5,179.7 290.6,179.7 291.9,179.6 292,179.6 293.2,179.2 293.3,179.2 294.4,178.9 294.5,178.9 295.7,178.6 295.8,178.6 297,178.4 297.1,178.4 298.4,178.1 298.5,178.1 299.7,177.8 299.8,177.8 300.9,177.4 301.1,177.4 302.3,177.1 302.4,177.1 303.6,177 303.7,177 305,176.6 305.1,176.6 306.4,176.3 306.5,176.3 307.7,176.2 307.8,176.2 309,175.8 309.1,175.8 310.2,175.5 310.3,175.5 311.5,175.2 311.6,175.2 312.9,174.9 313,174.9 314.2,174.5 314.3,174.5 315.5,174.2 315.6,174.2 316.8,173.9 316.9,173.9 318,173.6 318.1,173.6 319.4,173.2 319.5,173.2 320.7,172.9 320.8,172.9 322,172.6 322.1,172.6 323.3,172.3 323.4,172.3 324.5,171.9 324.7,171.9 325.9,171.8 326,171.8 327.2,171.6 327.3,171.6 328.5,171.4 328.6,171.4 329.8,171.1 329.9,171.1 331.1,171 331.2,171 332.4,170.8 332.6,170.8 333.7,170.6 333.8,170.6 335,170.3 335.1,170.3 336.4,170.1 336.5,170.1 337.7,170.1 337.8,170.1 339.1,170 339.2,170 339.5,170 339.6,170 340.5,169.8 340.6,169.8 341.7,169.7 341.8,169.7 343,169.5 343.1,169.5 344.3,169.3 344.4,169.3 345.6,169.2 345.7,169.2 347,169 347.1,169 348.3,169 348.4,169 349.5,168.8 349.6,168.8 350.8,168.7 350.9,168.7 352.1,168.7 352.2,168.7 353.5,168.5 353.6,168.5 354.8,168.4 354.9,168.4 356,168.4 356.2,168.4 357.3,168.4 357.4,168.4 358.7,168.2 358.8,168.2 360,168.2 360.1,168.2 361.3,168 361.4,168 362.6,168 362.7,168 364.1,167.9 364.2,167.9 365.4,167.9 365.5,167.9 366.8,167.9 366.9,167.9 368.2,167.7 368.3,167.7 369.5,167.7 369.6,167.7 370.9,167.5 371,167.5 372.3,167.5 372.4,167.5 373.6,167.5 373.7,167.5 374.8,167.4 374.9,167.4 376.1,167.2 376.2,167.2 377.4,167.2 377.5,167.2 378.8,167.1 378.9,167.1 380.1,166.9 380.2,166.9 381.4,166.9 381.5,166.9 382.6,166.7 382.7,166.7 383.9,166.6 384,166.6 385.3,166.4 385.4,166.4 386.6,166.4 386.7,166.4 387.9,166.2 388,166.2 389.1,166.1 389.3,166.1 390.4,165.9 390.5,165.9 391.8,165.8 391.9,165.8 393.2,165.6 393.3,165.6 394.5,165.4 394.6,165.4 395.8,165.3 395.9,165.3 397,165.1 397.2,165.1 398.3,164.9 398.4,164.9 399.7,164.8 399.8,164.8 401,164.5 401.1,164.5 402.4,164.3 402.5,164.3 403.3,164.3 403.5,164.3 403.6,164.3 403.7,164.1 403.8,164.1 404.9,164 405.1,164 406.2,163.8 406.3,163.8 407.5,163.6 407.6,163.6 408.9,163.5 409,163.5 410.2,163.2 410.3,163.2 411.6,163 411.7,163 412.9,162.8 413,162.8 414.1,162.7 414.2,162.7 415.4,162.5 415.5,162.5 416.8,162.5 416.9,162.5 418.1,162.3 418.2,162.3 419.4,162.2 419.5,162.2 420.6,162 420.8,162 421.9,161.9 422,161.9 423.3,161.7 423.4,161.7 424.6,161.5 424.7,161.5 425.9,161.5 426,161.5 427.2,161.4 427.3,161.4 428.4,161.4 428.5,161.4 429.8,161.2 429.9,161.2 431.1,161 431.2,161 432.4,161 432.5,161 433.7,160.9 433.8,160.9 435,160.9 435.1,160.9 436.3,160.7 436.4,160.7 437.6,160.7 437.7,160.7 438.9,160.6 439,160.6 440.3,160.6 440.4,160.6 440.8,160.6 440.9,160.6 441.6,160.4 441.7,160.4 442.9,160.4 443,160.4 444.1,160.4 444.2,160.4 445.4,136.7 445.5,136.7 446.8,98.8 446.9,98.8 448.1,90.1 448.2,90.1 449.5,216.6 449.6,216.6 450.8,165.1 450.9,165.1 452,164.3 452.1,164.3 453.3,164.1 453.4,164.1 454.7,164 454.8,164 456,163.8 456.1,163.8 457.4,163.5 457.5,163.5 458.7,163.3 458.8,163.3 459.9,163.2 460,163.2 461.2,163 461.3,163 462.5,162.7 462.6,162.7 463.9,162.5 464,162.5 465.2,162.3 465.3,162.3 466.6,162.2 466.7,162.2 467.9,161.9 468.1,161.9 469.2,161.7 469.3,161.7 470.5,161.4 470.6,161.4 471.8,161.2 471.9,161.2 473.1,161 473.2,161 474.5,160.7 474.6,160.7 475.7,160.6 475.8,160.6 477,160.2 477.1,160.2 478.4,160.1 478.5,160.1 479.7,159.7 479.8,159.7 481,159.6 481.1,159.6 482.4,159.3 482.5,159.3 483.6,159.1 483.7,159.1 484.9,158.8 485,158.8 486.3,158.6 486.4,158.6 488,157.8 488.1,157.8 489.3,158 489.4,158 490.7,157.8 490.8,157.8 492.1,157.3 492.2,157.3 493.4,157.1 493.5,157.1 494.6,156.7 494.7,156.7 496,156 496.1,156 497.3,155.7 497.4,155.7 498.6,155.8 498.7,155.8 500,155.2 500.1,155.2 501.3,154.7 501.4,154.7 502.6,154.5 502.8,154.5 503.9,154.5 504,154.5 505.3,153.9 505.4,153.9 506.6,153.6 506.7,153.6 507.9,153.4 508,153.4 509.4,153.1 509.5,153.1 510.8,152.6 510.9,152.6 512.2,152.6 512.3,152.6 513.4,151.9 513.5,151.9 514.5,151.6 514.6,151.6 514.7,151.6 514.8,151.6 516.1,151.1 516.2,151.1 517.4,151.3 517.5,151.3 518.9,150.5 519,151.1 519.1,151.1 520.3,150.5 520.4,150.5 521.5,150.3 521.7,150.3 522.8,150 522.9,150 524.2,149.4 524.3,149.4 525.5,149 525.6,149 526.8,148.7 526.9,148.7 528.2,148.4 528.3,148.4 529.4,148.1 529.6,148.1 530.8,148.1 530.9,148.1 532.1,147.6 532.2,147.6 533.4,147.6 533.5,147.6 534.7,147.1 534.8,147.1 536.1,146.4 536.2,146.4 537.5,146.4 537.6,146.4 538.7,146.1 538.8,146.1 540,145.8 540.1,145.8 541.4,145.6 541.5,145.6 542.7,145.1 542.8,145.1 544,145 544.1,145 545.5,144.6 545.6,144.6 546.9,144.5 547,144.5 548.1,144.2 548.2,144.2 549.4,143.8 549.5,143.8 550.8,143.3 550.9,143.3 552.1,143.2 552.2,143.2 553.4,142.9 553.5,142.9 554.8,142.7 554.9,142.7 556.1,142.4 556.2,142.4 557.4,142 557.5,142 558.8,141.7 558.9,141.7 560.1,141.6 560.2,141.6 561.4,141.4 561.5,141.4 562.7,141.2 562.8,141.2 564,140.9 564.2,140.9 565.4,140.4 565.5,140.4 566.8,140.3 566.9,140.3 568.2,140.1 568.3,140.1 569.5,139.8 569.6,139.8 570.8,139.6 570.9,139.6 572.2,139.4 572.3,139.4 573.5,139.1 573.7,139.1 574.9,139 575,139 576.2,138.8 576.3,138.8 577.5,138.5 577.6,138.5 578.8,138.3 578.9,138.3 580.2,138.3 580.3,138.3 581.4,137.7 581.6,137.7 582.8,137.5 582.9,137.5 584.1,137.5 584.2,137.5 585.4,137.2 585.5,137.2 586.7,137.2 586.8,137.2 588.1,137 588.2,137 589.3,136.8 589.5,136.8 590.7,136.7 590.8,136.7 592,136.4 592.1,136.4 593.4,136.4 593.5,136.4 594.8,136 594.9,136 596.1,135.9 596.2,135.9 597.4,135.7 597.5,135.7 598.9,135.5 599,135.5 600.2,135.4 600.3,135.4 601.5,135.2 601.6,135.2 602.8,135.1 602.9,135.1 604.1,135.1 604.2,135.1 605.5,134.9 605.6,134.9 606.8,134.7 606.9,134.7 608.2,134.6 608.4,134.6 609.6,134.2 609.7,134.2 610.9,134.2 611,134.2 612.2,134.1 612.3,134.1 613.6,133.9 613.7,133.9 614.9,133.9 615,133.9 616.1,133.8 616.3,133.8 617.5,133.8 617.6,133.8 618.9,133.6 619,133.6 620.2,133.4 620.3,133.4 621.5,133.4 621.6,133.4 622.9,133.1 623,133.1 624.2,133.1 624.3,133.1 625.4,132.9 625.5,132.9 626.9,132.9 627,132.9 628.3,132.8 628.4,132.8 629.6,132.8 629.7,132.8 630.9,132.6 631,132.6 632.3,132.6 632.4,132.6 633.6,132.5 633.7,132.5 634.5,132.3 634.6,132.3 634.8,132.3 634.9,132.3 636.2,132.1 636.3,132.1 637.5,132.1 637.6,132.1 638.9,132 639,132 640.2,131.8 640.3,131.8 641.5,131.8 641.6,131.8 642.8,131.6 643,131.6 644.1,131.8 644.2,131.8 645.5,131.6 645.6,131.6 646.9,131.5 647,131.5 648.3,131.5 648.4,131.5 649.6,131.3 649.7,131.3 650.9,131.2 651,131.2 652.2,131.3 652.3,131.3 653.5,131.2 653.6,131.2 654.9,131 655,131 656.2,131 656.3,131 657.5,130.8 657.6,130.8 658.8,130.8 658.9,130.8 660.1,130.7 660.2,130.7 661.4,130.7 661.5,130.7 662.8,130.7 662.9,130.7 664.1,130.7 664.2,130.7 665.4,130.5 665.5,130.5 666.8,130.3 666.9,130.3 668,130.3 668.1,130.3 669.3,130.3 669.4,130.3 670.7,130.3 670.8,130.3 672,130.2 672.1,130.2 673.3,130 673.4,130 674.6,130.2 674.7,130.2 675.9,130 676.1,130 677.2,130 677.3,130 678.5,130 678.6,130 679.8,129.9 679.9,129.9 681.2,129.9 681.3,129.9 682.5,129.9 682.6,129.9 683.7,129.7 683.8,129.7 685,129.7 685.1,129.7 686.4,129.7 686.5,129.7 687.7,129.5 687.8,129.5 689,129.5 689.1,129.5 690.4,129.5 690.5,129.5 691.6,129.4 691.7,129.4 692.9,129.4 693,129.4 694.2,129.4 694.3,129.4 695.6,129.2 695.7,129.2 697,129.2 697.1,129.2 698.3,129.2 698.4,129.2 699.2,129.2 699.3,129.2 699.5,129.2 699.6,129.2 700.8,129 700.9,129 702.2,128.9 702.3,128.9 703.5,128.7 703.6,128.7 704.8,128.7 704.9,128.7 705.1,128.7 705.2,128.7 706.2,128.4 706.3,128.4 707.4,128.4 707.5,128.4 708.7,128.4 708.8,128.4 710,128.2 710.1,128.2 711.4,128.4 711.5,128.4 712.7,128.2 712.8,128.2 714,128.4 714.1,128.4 715.2,128.4 715.3,128.4 716.6,128.2 716.7,128.2 717.9,128.6 718,128.6 719.2,128.4 719.3,128.4 720.5,128.6 720.6,128.6 721.9,128.7 722,128.7 723.1,129 723.2,129 724.4,129 724.5,129 725.7,129.4 725.8,129.4 727.1,129.5 727.2,129.5 728.4,130.2 728.5,130.2 729.7,130.5 729.8,130.5 731,130.7 731.1,130.7 732.3,130.8 732.4,130.8 733.6,131.3 733.7,131.3 734.9,131.5 735,131.5 736.9,204.4 737,204.4 738.2,218.4 738.3,218.4 739.5,221.1 739.6,221.1 740.8,222.4 740.9,222.4 742,222.8 742.1,222.8 743.4,223.3 743.5,223.3 744.7,223.4 744.8,223.4 746,223.6 746.1,223.6 747.3,224.1 747.4,224.1 748.7,224.2 748.8,224.2 749.9,224.6 750,224.6 751.2,224.6 751.3,224.6 752.5,224.6 752.6,224.6 753.8,224.7 753.9,224.7 755.2,224.9 755.3,224.9 756.5,224.7 756.6,224.7 757.7,225.4 757.8,225.4 759,225.4 759.1,225.4 760.4,225.4 760.5,225.4 761.7,225.5 761.8,225.5 762.5,225.9 762.6,225.9 763,225.7 763.1,225.7 764.2,226 764.4,226 765.6,225.9 765.7,225.9 766.9,226 767,226 768.2,226.2 768.3,226.2 769.5,226.3 769.6,226.3 770.9,226.5 771,226.5 772.1,226.7 772.3,226.7 773.4,226.7 773.5,226.7 774.7,227 774.8,227 776.1,226.8 776.2,226.8 777.4,227.2 777.5,227.2 778.7,227.2 778.8,227.2 779.9,227.5 780,227.5 781.2,227.3 781.3,227.3 782.6,227.6 782.7,227.6 783.9,227.6 784,227.6"
            stroke="#f51a6f"
          />
      </svg>
      <figcaption>
          <span>
            <span class="fireboard-archive-swatch" style="background-color: #ffb86c"></span>
            Grill
          </span>
          <span>
            <span class="fireboard-archive-swatch" style="background-color: #f51a6f"></span>
            Pork
          </span>
      </figcaption>
    </figure>

    <table>
      <thead>
        <tr>
          <th>Probe</th>
          <th>First</th>
          <th>Last</th>
          <th>Min</th>
          <th>Max</th>
        </tr>
      </thead>
      <tbody>
          <tr>
            <th>Grill</th>
            <td>26.8C</td>
            <td>25.9C</td>
            <td>25.7C</td>
            <td>146C</td>
          </tr>
          <tr>
            <th>Pork</th>
            <td>28.4C</td>
            <td>29.7C</td>
            <td>9.4C</td>
            <td>114.4C</td>
          </tr>
      </tbody>
    </table>
  </section>]]></content:encoded><source:markdown>
{{&lt; fireboard-archive &gt;}}

</source:markdown></item><item>
      <title>HomeOps Tour: Hardware</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/05/21/homeops-tour-hardware/</link>
      <pubDate>Thu, 21 May 2026 13:35:06 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/05/21/homeops-tour-hardware/</guid>
      <category>homelab</category>
      <category>homeops</category>
      <description>The first stop on my HomeOps Tour covers the hardware I use.</description>
      <content:encoded><![CDATA[<p>The first stop on my HomeOps Tour covers the hardware I use.</p>
<p>It&rsquo;s a mix of &ldquo;too expensive&rdquo; and &ldquo;I should have spent more on this&rdquo;.</p>
<p>None of this was designed all at once. It&rsquo;s the result of buying things for one reason, repurposing them for another, and slowly discovering a stable setup.</p>
<h2 id="what-its-for">What it&rsquo;s for</h2>
<p>HomeOps is the boring half of my homelab: the part that runs the services I actually depend on.</p>
<p>It started as an all-in-one server, but I eventually moved away from that. It was convenient until it wasn&rsquo;t; every change was risky.</p>
<p>Splitting it up gives me more to manage, but also a smaller blast radius when something goes wrong.</p>
<h2 id="overview">Overview</h2>
<table>
	<thead>
			<tr>
					<th>Device</th>
					<th>Role</th>
					<th>Purpose</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>NAS</td>
					<td>Storage</td>
					<td>archiving, media, network shares</td>
			</tr>
			<tr>
					<td>NUC</td>
					<td>Server</td>
					<td>apps, databases, services</td>
			</tr>
			<tr>
					<td>VPS</td>
					<td>Tunnel</td>
					<td>external entry point for tunnel access</td>
			</tr>
			<tr>
					<td>Pi</td>
					<td>Monitoring</td>
					<td>monitors everything else</td>
			</tr>
	</tbody>
</table>
<p>Each of these devices could do more than they currently do, but I&rsquo;ve tried to keep their roles separate.</p>
<p>They are more like pets than cattle: the software is reproducible but the machines are not disposable.</p>
<h2 id="nas">NAS</h2>
<p>I have a dedicated machine for Network Attached Storage. I built it myself; the specs are <a href="https://github.com/paultibbetts/homeops/tree/main/hardware/nas">in the repo</a>
. It runs <a href="https://www.truenas.com/">TrueNAS</a>
 and is the central place for storage, backups, archiving, and media.</p>
<p>Storage is shared over NFS for Mac and Linux, and SMB for Windows. The only app the NAS runs is <a href="https://www.min.io/">minIO</a>
, which provides S3-compatible storage. minIO changed its license recently so I am eventually going to swap it for something else.</p>
<p>It has 4TB of NVMe storage, which is the pool I use most often. My NAS and PC are connected through a small 10GbE switch, so from my PC it feels like the drives are plugged in directly. These drives are <strong>not</strong> set up in a RAID array, so if one failed I would lose data.</p>
<p>For archiving and longer-term storage it has 4 x 6TB HDD set up in a RAIDZ2 array, which means 2 of the drives could fail before I lost data. Anything important is copied to the RAIDZ2 pool, which is why I&rsquo;m comfortable running the NVMe drives without redundancy. <a href="https://www.raidisnotabackup.com/">RAID is not a backup</a>
, so I <em>should</em> back this data up to an external location, but I&rsquo;m yet to set this up.</p>
<p>It was originally made up of spare parts from an old gaming PC but I decided I wanted <abbr title="Error Correcting Code">ECC</abbr> memory. That meant replacing the CPU and motherboard with parts that supported it, and the affordable options were older server components. At this point the only gaming PC parts still in use are the case and the PSU.</p>
<h3 id="review">Review</h3>
<p>Those older server components are less efficient and more power-hungry than newer ones would be.</p>
<p>It was expensive. I made the decision that storage was important enough to justify ECC memory, but plenty of people choose to go without it. ECC is there to protect against memory-related corruption, and I don&rsquo;t know whether it&rsquo;s ever actually done that.</p>
<p>It&rsquo;s overpowered and underutilised for its current role, mostly because it used to do everything and now it just does storage.</p>
<p>However, it plays a major part in my HomeOps setup, so it&rsquo;s definitely worth its place.</p>
<p>If you have a homelab, or even just multiple computers in one household, you would benefit from a NAS. It doesn&rsquo;t need to be an expensive one like mine though.</p>
<h2 id="nuc">NUC</h2>
<p>I use an Intel NUC as my server. Specs are <a href="https://github.com/paultibbetts/homeops/tree/main/hardware/nuc">in the repo</a>
.</p>
<p>Originally I used my NAS as a server, until I found that messing with it made it unstable, so I stopped doing that and looked for something else. I bought something from Kickstarter, and in true Kickstarter fashion it was delayed multiple times, and eventually I got impatient and bought the NUC.</p>
<p>It&rsquo;s modern and pretty power efficient, which means running it is much cheaper than the NAS made out of older components.</p>
<p>The CPU has 12 cores and 16 threads, which gives me plenty of headroom for virtualisation. I have <a href="https://www.proxmox.com/en/products/proxmox-virtual-environment/overview">Proxmox</a>
 installed and use it to run 9 virtual machines and 1 LXC. This lets me split services across separate environments instead of installing everything directly on one host, so a problem with one service is less likely to affect the others.</p>
<p>The LXC is for Jellyfin, which is self-hosted Netflix for movies I store on my NAS. The NUC has an integrated Intel GPU that supports <em>Quick Sync</em>, which Jellyfin uses for hardware-accelerated transcoding. This means I can watch my movies from any device and let the GPU do the transcoding, leaving the CPU available for the other services hosted by the NUC.</p>
<h3 id="review-1">Review</h3>
<p>The NUC is an excellent home server. It&rsquo;s not enterprise hardware with redundant power supplies and room for expansion, but its size and efficiency make it a great fit at home.</p>
<p>The trade-off is that it&rsquo;s not a box for lots of disks. Mine has room for an SSD boot drive and the NVMe I use for local data. After that I needed to pair it with my NAS for extra storage.</p>
<p>It has a 2.5GbE NIC. That is not as fast as the 10GbE link to the NAS, but is more than enough for what I use it for.</p>
<p>If I started again and didn&rsquo;t need much local storage, a machine like this might be all I needed.</p>
<p>Unfortunately Intel has stopped making these but places like <a href="https://www.minisforum.uk/">Minisforum</a>
 make ones that are similar.</p>
<h2 id="vps">VPS</h2>
<p>The VPS is the only machine in this setup that I don&rsquo;t physically own. It&rsquo;s rented from <a href="https://www.hetzner.com/">Hetzner</a>
.</p>
<p>It is the public side of my &ldquo;tunnel&rdquo; setup, which I&rsquo;ll explain in a future post. The services I run are hosted at home and this VPS gives me an entry point without directly exposing my home network.</p>
<p>There isn&rsquo;t much to say about it. Hetzner is some of the cheapest European hosting I could find, and even though the VPS is located in Germany it doesn&rsquo;t add any real delay to my connection.</p>
<h2 id="monpi">MonPi</h2>
<p>The final part of the setup is the Raspberry Pi that monitors everything else.</p>
<p>The reason it exists is separation. I didn&rsquo;t want to run monitoring on the same machines that host the services. If the NAS or the NUC has a problem, a separate device has a better chance of catching it.</p>
<p>It runs two types of monitoring: Uptime Kuma for service uptime checks, and Beszel for host metrics. I&rsquo;ll cover these in more detail later in the series.</p>
<h3 id="review-2">Review</h3>
<p>The Pi works great for monitoring, but for this particular model that&rsquo;s about all it can do.</p>
<p>It was originally called &ldquo;DeskPi&rdquo; and was plugged into a screen to show a live output, but because it has only 2GB of RAM it struggled to run the desktop environment and I quickly gave up on that idea.</p>
<h2 id="whats-missing">What&rsquo;s missing</h2>
<p>There are two pieces of hardware people might expect to see listed here that I don&rsquo;t yet have: a custom router and a <abbr title="Uninterrupted Power Supply">UPS</abbr>.</p>
<p>I still use the router my ISP gave me, instead of something like OPNsense or pfSense. I&rsquo;ve worked around some of its limitations by letting Pi-hole handle DHCP - more details on that in the Pi-hole post. I will be moving soon, and when I get settled I&rsquo;ll start working on the router.</p>
<p>The UPS will come after. It is risky running a NAS without one, but I live in the UK, and in the five years I&rsquo;ve lived in this house there&rsquo;s been exactly one power cut, so I haven&rsquo;t felt the need to prioritise it.</p>
<h2 id="not-highly-available">Not Highly Available</h2>
<p>The obvious limitation of my setup is that it&rsquo;s not highly available.</p>
<p>Splitting storage, compute and monitoring across different machines gives me cleaner roles and a smaller blast radius than the all-in-one server I started with. It does not mean the setup can survive any of those machines breaking.</p>
<p>If the NUC dies, my services go offline. If the NAS fails, the storage layer is unavailable. If the VPS has a problem, friends and family can&rsquo;t get through the tunnel. MonPi monitors the setup, but nothing is monitoring MonPi.</p>
<p>There is an old redundancy saying that &ldquo;two is one and one is none&rdquo;. That applies here, but in different ways at different layers. The NAS has redundant disks, but it is still a single machine with one motherboard, one power supply and one network path. The NUC is a single host, so the VMs and containers running on it have nowhere else to fail over to.</p>
<p>Making the NUC highly available would mean running multiple Proxmox nodes. That means not just two machines, but three, so Proxmox can <a href="https://pve.proxmox.com/wiki/High_Availability">maintain quorum</a>
.</p>
<p>That would be a better <abbr title="High Availability">HA</abbr> design, but it would also mean more hardware and more power usage.</p>
<p>For now, I have accepted that my setup might have downtime because that is the trade-off that keeps it affordable and manageable at home.</p>
<h2 id="next">Next</h2>
<p>That&rsquo;s the hardware covered for my setup.</p>
<p>The important thing is not that any individual machine is special, it&rsquo;s that each one has a clear job. The NAS stores things, the NUC runs things, the VPS provides the public entry point, and the Pi monitors it all.</p>
<p>It&rsquo;s not perfect, and it&rsquo;s not highly available, but it&rsquo;s much easier to work with than the all-in-one server I started with.</p>
<p>Next I&rsquo;ll look at the storage layer in more detail: how I use TrueNAS for network shares, archives, and the storage services that support the rest of the setup.</p>]]></content:encoded><source:markdown>
The first stop on my HomeOps Tour covers the hardware I use.

&lt;!--more--&gt;

It&#39;s a mix of &#34;too expensive&#34; and &#34;I should have spent more on this&#34;.

None of this was designed all at once. It&#39;s the result of buying things for one reason, repurposing them for another, and slowly discovering a stable setup.

## What it&#39;s for

HomeOps is the boring half of my homelab: the part that runs the services I actually depend on.

It started as an all-in-one server, but I eventually moved away from that. It was convenient until it wasn&#39;t; every change was risky.

Splitting it up gives me more to manage, but also a smaller blast radius when something goes wrong.

## Overview

| Device | Role       | Purpose                                |
| ------ | ---------- | -------------------------------------- |
| NAS    | Storage    | archiving, media, network shares       |
| NUC    | Server     | apps, databases, services              |
| VPS    | Tunnel     | external entry point for tunnel access |
| Pi     | Monitoring | monitors everything else               |

Each of these devices could do more than they currently do, but I&#39;ve tried to keep their roles separate.

They are more like pets than cattle: the software is reproducible but the machines are not disposable.

## NAS

I have a dedicated machine for Network Attached Storage. I built it myself; the specs are [in the repo](https://github.com/paultibbetts/homeops/tree/main/hardware/nas). It runs [TrueNAS](https://www.truenas.com/) and is the central place for storage, backups, archiving, and media.

Storage is shared over NFS for Mac and Linux, and SMB for Windows. The only app the NAS runs is [minIO](https://www.min.io/), which provides S3-compatible storage. minIO changed its license recently so I am eventually going to swap it for something else.

It has 4TB of NVMe storage, which is the pool I use most often. My NAS and PC are connected through a small 10GbE switch, so from my PC it feels like the drives are plugged in directly. These drives are **not** set up in a RAID array, so if one failed I would lose data.

For archiving and longer-term storage it has 4 x 6TB HDD set up in a RAIDZ2 array, which means 2 of the drives could fail before I lost data. Anything important is copied to the RAIDZ2 pool, which is why I&#39;m comfortable running the NVMe drives without redundancy. [RAID is not a backup](https://www.raidisnotabackup.com/), so I _should_ back this data up to an external location, but I&#39;m yet to set this up.

It was originally made up of spare parts from an old gaming PC but I decided I wanted &lt;abbr title=&#34;Error Correcting Code&#34;&gt;ECC&lt;/abbr&gt; memory. That meant replacing the CPU and motherboard with parts that supported it, and the affordable options were older server components. At this point the only gaming PC parts still in use are the case and the PSU.

### Review

Those older server components are less efficient and more power-hungry than newer ones would be.

It was expensive. I made the decision that storage was important enough to justify ECC memory, but plenty of people choose to go without it. ECC is there to protect against memory-related corruption, and I don&#39;t know whether it&#39;s ever actually done that.

It&#39;s overpowered and underutilised for its current role, mostly because it used to do everything and now it just does storage.

However, it plays a major part in my HomeOps setup, so it&#39;s definitely worth its place.

If you have a homelab, or even just multiple computers in one household, you would benefit from a NAS. It doesn&#39;t need to be an expensive one like mine though.

## NUC

I use an Intel NUC as my server. Specs are [in the repo](https://github.com/paultibbetts/homeops/tree/main/hardware/nuc).

Originally I used my NAS as a server, until I found that messing with it made it unstable, so I stopped doing that and looked for something else. I bought something from Kickstarter, and in true Kickstarter fashion it was delayed multiple times, and eventually I got impatient and bought the NUC.

It&#39;s modern and pretty power efficient, which means running it is much cheaper than the NAS made out of older components.

The CPU has 12 cores and 16 threads, which gives me plenty of headroom for virtualisation. I have [Proxmox](https://www.proxmox.com/en/products/proxmox-virtual-environment/overview) installed and use it to run 9 virtual machines and 1 LXC. This lets me split services across separate environments instead of installing everything directly on one host, so a problem with one service is less likely to affect the others.

The LXC is for Jellyfin, which is self-hosted Netflix for movies I store on my NAS. The NUC has an integrated Intel GPU that supports _Quick Sync_, which Jellyfin uses for hardware-accelerated transcoding. This means I can watch my movies from any device and let the GPU do the transcoding, leaving the CPU available for the other services hosted by the NUC.

### Review

The NUC is an excellent home server. It&#39;s not enterprise hardware with redundant power supplies and room for expansion, but its size and efficiency make it a great fit at home.

The trade-off is that it&#39;s not a box for lots of disks. Mine has room for an SSD boot drive and the NVMe I use for local data. After that I needed to pair it with my NAS for extra storage.

It has a 2.5GbE NIC. That is not as fast as the 10GbE link to the NAS, but is more than enough for what I use it for.

If I started again and didn&#39;t need much local storage, a machine like this might be all I needed.

Unfortunately Intel has stopped making these but places like [Minisforum](https://www.minisforum.uk/) make ones that are similar.

## VPS

The VPS is the only machine in this setup that I don&#39;t physically own. It&#39;s rented from [Hetzner](https://www.hetzner.com/).

It is the public side of my &#34;tunnel&#34; setup, which I&#39;ll explain in a future post. The services I run are hosted at home and this VPS gives me an entry point without directly exposing my home network.

There isn&#39;t much to say about it. Hetzner is some of the cheapest European hosting I could find, and even though the VPS is located in Germany it doesn&#39;t add any real delay to my connection.

## MonPi

The final part of the setup is the Raspberry Pi that monitors everything else.

The reason it exists is separation. I didn&#39;t want to run monitoring on the same machines that host the services. If the NAS or the NUC has a problem, a separate device has a better chance of catching it.

It runs two types of monitoring: Uptime Kuma for service uptime checks, and Beszel for host metrics. I&#39;ll cover these in more detail later in the series.

### Review

The Pi works great for monitoring, but for this particular model that&#39;s about all it can do.

It was originally called &#34;DeskPi&#34; and was plugged into a screen to show a live output, but because it has only 2GB of RAM it struggled to run the desktop environment and I quickly gave up on that idea.

## What&#39;s missing

There are two pieces of hardware people might expect to see listed here that I don&#39;t yet have: a custom router and a &lt;abbr title=&#34;Uninterrupted Power Supply&#34;&gt;UPS&lt;/abbr&gt;.

I still use the router my ISP gave me, instead of something like OPNsense or pfSense. I&#39;ve worked around some of its limitations by letting Pi-hole handle DHCP - more details on that in the Pi-hole post. I will be moving soon, and when I get settled I&#39;ll start working on the router.

The UPS will come after. It is risky running a NAS without one, but I live in the UK, and in the five years I&#39;ve lived in this house there&#39;s been exactly one power cut, so I haven&#39;t felt the need to prioritise it.

## Not Highly Available

The obvious limitation of my setup is that it&#39;s not highly available.

Splitting storage, compute and monitoring across different machines gives me cleaner roles and a smaller blast radius than the all-in-one server I started with. It does not mean the setup can survive any of those machines breaking.

If the NUC dies, my services go offline. If the NAS fails, the storage layer is unavailable. If the VPS has a problem, friends and family can&#39;t get through the tunnel. MonPi monitors the setup, but nothing is monitoring MonPi.

There is an old redundancy saying that &#34;two is one and one is none&#34;. That applies here, but in different ways at different layers. The NAS has redundant disks, but it is still a single machine with one motherboard, one power supply and one network path. The NUC is a single host, so the VMs and containers running on it have nowhere else to fail over to.

Making the NUC highly available would mean running multiple Proxmox nodes. That means not just two machines, but three, so Proxmox can [maintain quorum](https://pve.proxmox.com/wiki/High_Availability).

That would be a better &lt;abbr title=&#34;High Availability&#34;&gt;HA&lt;/abbr&gt; design, but it would also mean more hardware and more power usage.

For now, I have accepted that my setup might have downtime because that is the trade-off that keeps it affordable and manageable at home.

## Next

That&#39;s the hardware covered for my setup.

The important thing is not that any individual machine is special, it&#39;s that each one has a clear job. The NAS stores things, the NUC runs things, the VPS provides the public entry point, and the Pi monitors it all.

It&#39;s not perfect, and it&#39;s not highly available, but it&#39;s much easier to work with than the all-in-one server I started with.

Next I&#39;ll look at the storage layer in more detail: how I use TrueNAS for network shares, archives, and the storage services that support the rest of the setup.
</source:markdown></item><item>
      <title>Liked: a quote reposted by Ricardo Chávez</title>
      <link>https://www.ricardochavezt.com/2026/05/14/816614305386708992/</link>
      <pubDate>Thu, 21 May 2026 10:35:23 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/05/21/a-quote-reposted-by-ricardo-chavez/</guid>
      <description></description>
      <content:encoded><![CDATA[]]></content:encoded></item><item>
      <title>Replied to: paulopinto.xyz</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/05/20/obsidian-paulo-pinto/</link>
      <pubDate>Wed, 20 May 2026 22:01:06 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/05/20/obsidian-paulo-pinto/</guid>
      <description>This captures what makes these tools appealing to me too. I renamed my “notes” directory to “thoughts” because it’s where I now do all of my thinking.
Logseq seems to suit my brain more than Obsidian, but with either tool the best feature is the cross-linking of ideas. I will sometimes forget where I wrote a note, but I remember that it linked to other ideas, so I can go to them to find the one I am looking for.</description>
      <content:encoded><![CDATA[<p>This captures what makes these tools appealing to me too. I renamed my &ldquo;notes&rdquo; directory to &ldquo;thoughts&rdquo; because it&rsquo;s where I now do all of my thinking.</p>
<p><a href="https://logseq.com/">Logseq</a>
 seems to suit my brain more than Obsidian, but with either tool the best feature is the cross-linking of ideas. I will sometimes forget where I wrote a note, but I remember that it linked to other ideas, so I can go to them to find the one I am looking for.</p>
<p>And I especially like finding out I have written about an idea before, and that I can see how my thinking has evolved since then.</p>]]></content:encoded><source:markdown>
This captures what makes these tools appealing to me too. I renamed my &#34;notes&#34; directory to &#34;thoughts&#34; because it&#39;s where I now do all of my thinking.

[Logseq](https://logseq.com/) seems to suit my brain more than Obsidian, but with either tool the best feature is the cross-linking of ideas. I will sometimes forget where I wrote a note, but I remember that it linked to other ideas, so I can go to them to find the one I am looking for.

And I especially like finding out I have written about an idea before, and that I can see how my thinking has evolved since then.
</source:markdown></item><item>
      <title>Liked: On the quiet pleasures of the small web — Paulo Pinto</title>
      <link>https://paulopinto.xyz/notes/2026/05/2026-05-17-on-the-small-web/</link>
      <pubDate>Wed, 20 May 2026 21:42:11 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/05/20/on-the-quiet-pleasures-of-the-small-web-paulo-pinto/</guid>
      <category>IndieWeb</category>
      <description></description>
      <content:encoded><![CDATA[]]></content:encoded></item><item>
      <title>HomeOps Tour: Overview</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/05/06/homeops-tour-overview/</link>
      <pubDate>Wed, 06 May 2026 21:05:00 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/05/06/homeops-tour-overview/</guid>
      <category>homelab</category>
      <category>homeops</category>
      <description>Welcome to my HomeOps Tour, where I’ll be your guide to the stable part of my Homelab.</description>
      <content:encoded><![CDATA[<p>Welcome to my HomeOps Tour, where I&rsquo;ll be your guide to the stable part of my Homelab.</p>
<h2 id="what-is-homeops">What is HomeOps?</h2>
<p>HomeOps isn&rsquo;t a widely used term, but I have seen a few people use it. To explain what I mean by it I should first define &ldquo;Homelab&rdquo;.</p>
<p>A homelab is a setup of computers you control and use for experiments - like a laboratory. It&rsquo;s usually at home, hence the name, but not always. You might own all the equipment, rent it, or even send your own machines to a provider so they can run them for you - called &ldquo;colocation&rdquo;.</p>
<p>The problem is that homelab seems to cover experimentation as well as self-hosting services you use every day, which have different needs.</p>
<p>HomeOps, short for &ldquo;Home Operations&rdquo;, is my attempt at labelling the stable part of my homelab.</p>
<h2 id="purpose">Purpose</h2>
<p>My HomeOps setup is used to host the apps and services a few people depend on, so I can&rsquo;t be breaking it every five minutes.</p>
<p>Some people call this &ldquo;HomeProd&rdquo;, short for &ldquo;Home Production&rdquo;, which separates it from the &ldquo;development&rdquo; or &ldquo;staging&rdquo; environment you use for experiments.</p>
<p>As such I treat it like a real production system: stable, reproducible, and intentionally boring to operate.</p>
<h2 id="architecture">Architecture</h2>
<p>The system is split over different machines, each with a different purpose.</p>
<ul>
<li>a <abbr title="Network Attached Storage">NAS</abbr> provides storage over the network</li>
<li>a server hosts all the apps and services</li>
<li>a rented <abbr title="Virtual Private Server">VPS</abbr> lets external users in securely through a &ldquo;tunnel&rdquo;</li>
<li>a Raspberry Pi monitors it all</li>
</ul>
<p>The whole thing is written down as Infrastructure as Code, with Terraform defining the infrastructure and Ansible for configuring it. This would let me reproduce it if a machine breaks and I need to set up a new one.</p>
<p>The server uses a hypervisor as an operating system and services generally get their own virtual machine. One of those VMs uses Docker to run most of the apps.</p>
<h2 id="key-decisions">Key Decisions</h2>
<p>Each of these deserve their own post later in the series.</p>
<h3 id="separating-homeops-from-homelab">Separating HomeOps from Homelab</h3>
<p>I made this split after <em>the data loss incident</em> which taught me not to let others depend on apps I&rsquo;m only trialing in my lab, especially without working backups.</p>
<h3 id="infrastructure-as-code">Infrastructure as code</h3>
<p>Everything is written down as code, so there&rsquo;s as little manual setup as possible. This means I can see what&rsquo;s going on by inspecting the code, and I can automate configuration.</p>
<h3 id="terraform-and-ansible-not-kubernetes">Terraform and Ansible, not Kubernetes</h3>
<p>When I started I wanted to compare Terraform and Ansible against Kubernetes for running a homelab.</p>
<p>I thought I&rsquo;d prove that Kubernetes was obviously the better choice, but two years later I&rsquo;ve found the Terraform and Ansible setup works just fine, so that&rsquo;s what powers my HomeOps.</p>
<p>I use Kubernetes in its own environment where I run experiments.</p>
<h3 id="not-highly-available">Not highly available</h3>
<p>One downside to my setup is that it&rsquo;s not highly available. If a machine breaks then there&rsquo;s nothing to fill in for it until I replace it. I could fix this by running more machines, but that increases the cost.</p>
<p>For now I can live with the downtime. So far nothing&rsquo;s broken, although one day something will.</p>
<h2 id="what-this-series-will-cover">What this series will cover</h2>
<p>In this series I&rsquo;ll go over each part of the system in more detail, including the hardware, IaC, services that run on top, and how I let people access it from outside my home.</p>
<p>The aim is not to present this as the correct way to run a homelab, but to explain the choices I made, how they fit together, and why I use the apps that I do.</p>
<p>It will be split up so you can dive in and out at any point to read just the parts that interest you, with links to anything written earlier that provides necessary context.</p>
<p>It might be awkward in that the system builds up in layers, and things may only make sense for my particular setup, but I&rsquo;ll try my best to generalise each part so it&rsquo;s relevant on its own.</p>
<h2 id="next">Next</h2>
<p>Coming up first is the hardware I use.</p>
<p>Check back soon for the link, or subscribe to the <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/series/homeops-tour/feed.xml">feed for this series</a>
 in your feed reader.</p>
<p>Or jump straight into the code, which is now on <a href="https://github.com/paultibbetts/homeops">GitHub</a>
.</p>]]></content:encoded><source:markdown>
Welcome to my HomeOps Tour, where I&#39;ll be your guide to the stable part of my Homelab.

&lt;!--more--&gt;

## What is HomeOps?

HomeOps isn&#39;t a widely used term, but I have seen a few people use it. To explain what I mean by it I should first define &#34;Homelab&#34;.

A homelab is a setup of computers you control and use for experiments - like a laboratory. It&#39;s usually at home, hence the name, but not always. You might own all the equipment, rent it, or even send your own machines to a provider so they can run them for you - called &#34;colocation&#34;.

The problem is that homelab seems to cover experimentation as well as self-hosting services you use every day, which have different needs.

HomeOps, short for &#34;Home Operations&#34;, is my attempt at labelling the stable part of my homelab.

## Purpose

My HomeOps setup is used to host the apps and services a few people depend on, so I can&#39;t be breaking it every five minutes.

Some people call this &#34;HomeProd&#34;, short for &#34;Home Production&#34;, which separates it from the &#34;development&#34; or &#34;staging&#34; environment you use for experiments.

As such I treat it like a real production system: stable, reproducible, and intentionally boring to operate.

## Architecture

The system is split over different machines, each with a different purpose.

- a &lt;abbr title=&#34;Network Attached Storage&#34;&gt;NAS&lt;/abbr&gt; provides storage over the network
- a server hosts all the apps and services
- a rented &lt;abbr title=&#34;Virtual Private Server&#34;&gt;VPS&lt;/abbr&gt; lets external users in securely through a &#34;tunnel&#34;
- a Raspberry Pi monitors it all

The whole thing is written down as Infrastructure as Code, with Terraform defining the infrastructure and Ansible for configuring it. This would let me reproduce it if a machine breaks and I need to set up a new one.

The server uses a hypervisor as an operating system and services generally get their own virtual machine. One of those VMs uses Docker to run most of the apps.

## Key Decisions

Each of these deserve their own post later in the series.

### Separating HomeOps from Homelab

I made this split after _the data loss incident_ which taught me not to let others depend on apps I&#39;m only trialing in my lab, especially without working backups.

### Infrastructure as code

Everything is written down as code, so there&#39;s as little manual setup as possible. This means I can see what&#39;s going on by inspecting the code, and I can automate configuration.

### Terraform and Ansible, not Kubernetes

When I started I wanted to compare Terraform and Ansible against Kubernetes for running a homelab.

I thought I&#39;d prove that Kubernetes was obviously the better choice, but two years later I&#39;ve found the Terraform and Ansible setup works just fine, so that&#39;s what powers my HomeOps.

I use Kubernetes in its own environment where I run experiments.

### Not highly available

One downside to my setup is that it&#39;s not highly available. If a machine breaks then there&#39;s nothing to fill in for it until I replace it. I could fix this by running more machines, but that increases the cost.

For now I can live with the downtime. So far nothing&#39;s broken, although one day something will.

## What this series will cover

In this series I&#39;ll go over each part of the system in more detail, including the hardware, IaC, services that run on top, and how I let people access it from outside my home.

The aim is not to present this as the correct way to run a homelab, but to explain the choices I made, how they fit together, and why I use the apps that I do.

It will be split up so you can dive in and out at any point to read just the parts that interest you, with links to anything written earlier that provides necessary context.

It might be awkward in that the system builds up in layers, and things may only make sense for my particular setup, but I&#39;ll try my best to generalise each part so it&#39;s relevant on its own.

## Next

Coming up first is the hardware I use.

Check back soon for the link, or subscribe to the [feed for this series](/series/homeops-tour/feed.xml) in your feed reader.

Or jump straight into the code, which is now on [GitHub](https://github.com/paultibbetts/homeops).
</source:markdown></item><item>
      <title>Turning a page into a feed</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/30/turning-a-page-into-a-feed/</link>
      <pubDate>Thu, 30 Apr 2026 16:46:04 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/30/turning-a-page-into-a-feed/</guid>
      <category>IndieWebCamp</category>
      <category>RSS</category>
      <description>Why and how I added a feed to my indie web directories directory.</description>
      <content:encoded><![CDATA[<p>During a session on feeds at <a href="https://events.indieweb.org/2026/04/-indiewebcamp-d%C3%BCsseldorf-ewHCZehNA3gg">IndieWebCamp Düsseldorf</a>
 a question came up: how do you signal updates to a page?</p>
<p>For pages that are really just lists you can treat them like posts on a blog.</p>
<p>My <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/indieweb-directories">directory of indie web directories</a>
 is one of those, it&rsquo;s a collection of links that grows over time.</p>
<p>Since my site is built with Hugo, I:</p>
<ul>
<li>changed the page into a <a href="https://gohugo.io/content-management/sections/">section</a>
</li>
<li>made a file for each entry</li>
</ul>
<p>and Hugo made a feed for it.</p>
<p>The only tweak it needed was in the RSS template, so that for this new section the <code>&lt;item&gt;&lt;link&gt;</code> would point to the directory instead of my own site, which for me looked a bit like:</p>
<pre><code class="language-go">{{- $itemLink := .Permalink -}}
{{- if eq .Section &#34;indieweb-directories&#34; -}}
	{{- with .Params.link -}}
		{{- $itemLink = . -}}
	{{- end -}}
{{- end -}}</code></pre>
<pre><code class="language-xml">&lt;item&gt;
  &lt;title&gt;{{ .Title }}&lt;/title&gt;
  &lt;link&gt;{{ $itemLink }}&lt;/link&gt;</code></pre>
<p>This works well for pages where updates are incremental additions, such as changelogs or collections.</p>
<p>It works less well for pages that are edited in place. You&rsquo;d need to post what changed, which means keeping a history of revisions - something wikis do by publishing diffs.</p>
<p>Those updates can be harder to read, and while you could do it with Hugo, you&rsquo;d have to write them manually. There&rsquo;s no built-in way of creating them.</p>
<p>In my case it works, not because I&rsquo;ve solved how to signal updates to a page, but because it turns out my page wasn&rsquo;t even a page in the first place.</p>]]></content:encoded><source:markdown>
During a session on feeds at [IndieWebCamp Düsseldorf](https://events.indieweb.org/2026/04/-indiewebcamp-d%C3%BCsseldorf-ewHCZehNA3gg) a question came up: how do you signal updates to a page?

For pages that are really just lists you can treat them like posts on a blog.

My [directory of indie web directories](/indieweb-directories) is one of those, it&#39;s a collection of links that grows over time.

Since my site is built with Hugo, I:

- changed the page into a [section](https://gohugo.io/content-management/sections/)
- made a file for each entry

and Hugo made a feed for it.

The only tweak it needed was in the RSS template, so that for this new section the `&lt;item&gt;&lt;link&gt;` would point to the directory instead of my own site, which for me looked a bit like:

```go
{{- $itemLink := .Permalink -}}
{{- if eq .Section &#34;indieweb-directories&#34; -}}
	{{- with .Params.link -}}
		{{- $itemLink = . -}}
	{{- end -}}
{{- end -}}
```

```xml
&lt;item&gt;
  &lt;title&gt;{{ .Title }}&lt;/title&gt;
  &lt;link&gt;{{ $itemLink }}&lt;/link&gt;
```

This works well for pages where updates are incremental additions, such as changelogs or collections.

It works less well for pages that are edited in place. You&#39;d need to post what changed, which means keeping a history of revisions - something wikis do by publishing diffs.

Those updates can be harder to read, and while you could do it with Hugo, you&#39;d have to write them manually. There&#39;s no built-in way of creating them.

In my case it works, not because I&#39;ve solved how to signal updates to a page, but because it turns out my page wasn&#39;t even a page in the first place.
</source:markdown></item><item>
      <title>Ghostty Is Leaving GitHub – Mitchell Hashimoto I too have been annoyed by …</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/30/2026-04-30-142715-ghostty-is-leaving-github-mitchell-hashimoto/</link>
      <pubDate>Thu, 30 Apr 2026 14:27:15 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/30/2026-04-30-142715-ghostty-is-leaving-github-mitchell-hashimoto/</guid>
      <category>GitHub</category>
      <description>Ghostty Is Leaving GitHub – Mitchell Hashimoto I too have been annoyed by GitHub’s performance lately, and that’s just from using it personally.
I can’t imagine what’s it’s like for those relying on it for paid work right now.
Curious to see where Ghostty ends up, and what impact this has on its contributions.
Related:
The Missing GitHub Status Page Before GitHub</description>
      <content:encoded><![CDATA[<p><a href="https://mitchellh.com/writing/ghostty-leaving-github">Ghostty Is Leaving GitHub – Mitchell Hashimoto</a>
</p>
<p>I too have been annoyed by GitHub&rsquo;s performance lately, and that&rsquo;s just from using it personally.</p>
<p>I can&rsquo;t imagine what&rsquo;s it&rsquo;s like for those relying on it for paid work right now.</p>
<p>Curious to see where Ghostty ends up, and what impact this has on its contributions.</p>
<p>Related:</p>
<ul>
<li><a href="https://mrshu.github.io/github-statuses/">The Missing GitHub Status Page</a>
</li>
<li><a href="https://lucumr.pocoo.org/2026/4/28/before-github/">Before GitHub</a>
</li>
</ul>]]></content:encoded><source:markdown>
[Ghostty Is Leaving GitHub – Mitchell Hashimoto](https://mitchellh.com/writing/ghostty-leaving-github)

I too have been annoyed by GitHub&#39;s performance lately, and that&#39;s just from using it personally.

I can&#39;t imagine what&#39;s it&#39;s like for those relying on it for paid work right now.

Curious to see where Ghostty ends up, and what impact this has on its contributions.

Related:

- [The Missing GitHub Status Page](https://mrshu.github.io/github-statuses/)
- [Before GitHub](https://lucumr.pocoo.org/2026/4/28/before-github/)
</source:markdown></item><item>
      <title>My IndieWebCamp Düsseldorf Demo:  Extended Edition</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/27/my-indiewebcamp-dusseldorf-demo-extended-edition/</link>
      <pubDate>Mon, 27 Apr 2026 22:30:42 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/27/my-indiewebcamp-dusseldorf-demo-extended-edition/</guid>
      <category>IndieWebCamp</category>
      <category>Micropub</category>
      <description>Featuring never-before-seen coverage and a look behind the scenes.</description>
      <content:encoded><![CDATA[<p>The second part of <a href="https://events.indieweb.org/2026/04/-indiewebcamp-d%C3%BCsseldorf-ewHCZehNA3gg">IndieWebCamp Düsseldorf 2026</a>
 was &ldquo;create day&rdquo; where you take your inspiration from day one, make it a reality, and show it to everyone else.</p>
<p>My inspiration was from a few weeks ago when <a href="https://ricardochavezt.com">Ricardo</a>
 demonstrated his new setup at <a href="https://hwclondon.co.uk/">Homebrew Website Club London</a>
. I guess I was prompted into action by the <strong>How to unblock yourself and actually post more often</strong> session this weekend, but I&rsquo;m trying to give credit where it&rsquo;s due, since my demo was basically the same as his.</p>
<p>What I didn&rsquo;t show, however, was all the work that&rsquo;s been happening over the last few months to make my demo possible, which is why I&rsquo;m posting this.</p>
<p>Also, I wanted to regain some nerd-cred because my demo was followed by <a href="https://burgeonlab.com/">Naty</a>
 who showed us an updated <a href="https://burgeonlab.com/stats/">stats page</a>
 , which looks awesome and is something I&rsquo;m going to have to steal.</p>
<aside>
<p><small>Hi Naty 👋, after we spoke at IWC I went on your site and realised who you were. Your site was a big inspiration for mine when I was redesigning it. <em>That&rsquo;s</em> why they look similar!</small></p>
</aside>
<h2 id="intro">Intro</h2>
<p>My site&rsquo;s powered by Hugo, which is a static site generator that turns files into a website.</p>
<p>I write posts on my laptop and push them up to GitHub, where the site gets generated with the new content and then deployed to my server. I&rsquo;ve written about that <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/19/moved-my-website-from-github-pages-to-a-raspberry-pi/">before</a>
, which you don&rsquo;t need to read for this, I&rsquo;m just setting the scene.</p>
<p>With that setup I couldn&rsquo;t publish from my phone, which is sometimes all I have on me.</p>
<p>The indie web has a solution for this called <a href="https://indieweb.org/micropub">Micropub</a>
, which is an API for creating posts on your site, even if you use a static site generator like I do.</p>
<h2 id="indiekit">IndieKit</h2>
<p>As mentioned, a few weeks ago at HWC Ricardo showed us his new setup, to which he&rsquo;d added a Micropub server called <a href="https://getindiekit.com">IndieKit</a>
.</p>
<p>It was created by <a href="https://paulrobertlloyd.com/">Paul Robert Lloyd</a>
, who I met (briefly) at an IndieWebCamp <strong>eleven</strong> years ago.</p>
<p>Side note - isn&rsquo;t it strange that there&rsquo;s millions of things on the web but you prefer using the things made by people you&rsquo;ve met in real life?</p>
<p>IndieKit works by connecting to the place you store your posts, which for me is GitHub, and lets you write new ones from the web:</p>
<img
      src="https://paultibbetts.uk/media/2026/04/27/my-indiewebcamp-dusseldorf-demo-extended-edition/indiekit.png"
      width="1200"
      height="802"
      alt="IndieKit user interface"
      loading="lazy"
      decoding="async"
      class="w-full h-auto rounded-lg"
    />
<h2 id="-sparkles-">✨ Sparkles ✨</h2>
<p>The other thing a Micropub server lets you do is use any Micropub client you want. You don&rsquo;t have to use the one in Indiekit - which is perfectly fine by the way - you can use a different one if you like.</p>
<p>Or you could use multiple. <a href="https://epilogue.micro.blog/">Epilogue</a>
 and <a href="https://indiebookclub.biz/">indiebookclub</a>
 both let you post about books you&rsquo;ve read.</p>
<p>Or you could post from a native app, like <a href="https://ia.net/writer/">iA Writer</a>
, a markdown editor that lets you post to WordPress, Ghost, and with Micropub: your own website.</p>
<p>The one Ricardo showed us in his demo, because it&rsquo;s got a good interface for posting about songs and albums, was <a href="https://sparkles.sploot.com/">Sparkles</a>
.</p>
<p>I don&rsquo;t post about songs or albums, but I do like how it looks, so I included it in my demo:</p>
<img
      src="https://paultibbetts.uk/media/2026/04/27/my-indiewebcamp-dusseldorf-demo-extended-edition/sparkles.png"
      width="1200"
      height="749"
      alt="Sparkles user interface"
      loading="lazy"
      decoding="async"
      class="w-full h-auto rounded-lg"
    />
<h2 id="bonus-content">Bonus content</h2>
<p>And that&rsquo;s where my demo ended.</p>
<p>Here&rsquo;s the extra footage.</p>
<h3 id="home-server">Home server</h3>
<p>I first tried running IndieKit on the Raspberry Pi that hosts my site.</p>
<p>The Pi doesn&rsquo;t have any direct storage attached. That means I can&rsquo;t reliably run a database on it and IndieKit needs one for some of the features I want from it.</p>
<p>I didn&rsquo;t want to rent another server. I already have one at home, so I installed IndieKit there instead.</p>
<p>It works, I can access it from my phone, but only on my home network. Not much of an upgrade.</p>
<h3 id="tunnel">Tunnel</h3>
<p>The next step then was to add it to the &ldquo;tunnel&rdquo; setup I have.</p>
<p>I rent a VPS, which lives in Nürnberg by the way, and have <a href="https://pangolin.net/">Pangolin</a>
 running on it, which is a reverse proxy that sends traffic down a Wireguard tunnel that ends in my home.</p>
<img
      src="https://paultibbetts.uk/media/2026/04/27/my-indiewebcamp-dusseldorf-demo-extended-edition/indiekit-phone.png"
      width="1179"
      height="2556"
      alt="IndieKit mobile interface"
      loading="lazy"
      decoding="async"
      class="rounded-lg"
    />
<p>and now I can post to my site, wherever I am 🎉.</p>
<h2 id="deleted-scenes">Deleted scenes</h2>
<p>Dan&rsquo;s indie feed reader <a href="https://feed.city/">feed.city</a>
 - which I discovered through IWC this weekend - caught me testing all of this and <a href="https://feed.city/feed?url=https://paultibbetts.uk/feed.xml#946987">recorded my test posts</a>
.</p>
<p>Sometimes deleted scenes are deleted for a reason. They&rsquo;re boring.</p>
<p>I only included them here as part of the Extended Edition theme I&rsquo;ve chosen to use. Let&rsquo;s skip forward.</p>
<h2 id="behind-the-scenes">Behind the scenes</h2>
<p>When I use Sparkles to post to my site:</p>
<ul>
<li>I go to <a href="https://sparkles.sploot.com/">sparkles.sploot.com</a>
 and enter my website&rsquo;s address</li>
<li>Sparkles sees the address of the Micropub endpoint I declare in the <code>&lt;head&gt;</code> of my site
<ul>
<li>which points at IndieKit</li>
</ul>
</li>
<li>Sparkles connects to IndieKit
<ul>
<li>by going to the VPS in Germany</li>
<li>through the tunnel into my home in the UK</li>
<li>and finds IndieKit on my home server</li>
</ul>
</li>
<li>I write a new post</li>
<li>IndieKit sends it to GitHub
<ul>
<li>GitHub creates a new markdown file</li>
</ul>
</li>
<li>the change triggers my deployment pipeline
<ul>
<li>which builds the site</li>
<li>and pushes it up to the Raspberry Pi</li>
</ul>
</li>
</ul>
<p>This is more complex than a typical hosted solution but it means I can self-host IndieKit at home and access it from anywhere.</p>
<p>If you&rsquo;re into that sort of stuff, stick around, I&rsquo;ll be posting about the setup and sharing the code soon.</p>
<h2 id="sequel">Sequel?</h2>
<p>If you&rsquo;re not, then I might see you at a future IndieWebCamp, hopefully in-person that time.</p>
<p>Maybe that&rsquo;ll be <a href="https://events.indieweb.org/2026/06/-indiewebcamp-nuremberg-7EIKg0lqfg93">IndieWebCamp Nürnberg 2026</a>
 and I&rsquo;ll get to meet my VPS for the first time, as well as some of the wonderful people who made this weekend possible.</p>]]></content:encoded><source:markdown>
The second part of [IndieWebCamp Düsseldorf 2026](https://events.indieweb.org/2026/04/-indiewebcamp-d%C3%BCsseldorf-ewHCZehNA3gg) was &#34;create day&#34; where you take your inspiration from day one, make it a reality, and show it to everyone else.

My inspiration was from a few weeks ago when [Ricardo](https://ricardochavezt.com) demonstrated his new setup at [Homebrew Website Club London](https://hwclondon.co.uk/). I guess I was prompted into action by the **How to unblock yourself and actually post more often** session this weekend, but I&#39;m trying to give credit where it&#39;s due, since my demo was basically the same as his.

What I didn&#39;t show, however, was all the work that&#39;s been happening over the last few months to make my demo possible, which is why I&#39;m posting this.

Also, I wanted to regain some nerd-cred because my demo was followed by [Naty](https://burgeonlab.com/) who showed us an updated [stats page](https://burgeonlab.com/stats/) , which looks awesome and is something I&#39;m going to have to steal.

&lt;aside&gt;

&lt;small&gt;Hi Naty 👋, after we spoke at IWC I went on your site and realised who you were. Your site was a big inspiration for mine when I was redesigning it. &lt;em&gt;That&#39;s&lt;/em&gt; why they look similar!&lt;/small&gt;

&lt;/aside&gt;

## Intro

My site&#39;s powered by Hugo, which is a static site generator that turns files into a website.

I write posts on my laptop and push them up to GitHub, where the site gets generated with the new content and then deployed to my server. I&#39;ve written about that [before](/2026/02/19/moved-my-website-from-github-pages-to-a-raspberry-pi/), which you don&#39;t need to read for this, I&#39;m just setting the scene.

With that setup I couldn&#39;t publish from my phone, which is sometimes all I have on me.

The indie web has a solution for this called [Micropub](https://indieweb.org/micropub), which is an API for creating posts on your site, even if you use a static site generator like I do.

## IndieKit

As mentioned, a few weeks ago at HWC Ricardo showed us his new setup, to which he&#39;d added a Micropub server called [IndieKit](https://getindiekit.com).

It was created by [Paul Robert Lloyd](https://paulrobertlloyd.com/), who I met (briefly) at an IndieWebCamp **eleven** years ago.

Side note - isn&#39;t it strange that there&#39;s millions of things on the web but you prefer using the things made by people you&#39;ve met in real life?

IndieKit works by connecting to the place you store your posts, which for me is GitHub, and lets you write new ones from the web:

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/04/27/my-indiewebcamp-dusseldorf-demo-extended-edition/indiekit.png&#34; alt=&#34;IndieKit user interface&#34; class=&#34;w-full h-auto rounded-lg&#34; width=&#34;1200&#34; height=&#34;802&#34; &gt;}}

## ✨ Sparkles ✨

The other thing a Micropub server lets you do is use any Micropub client you want. You don&#39;t have to use the one in Indiekit - which is perfectly fine by the way - you can use a different one if you like.

Or you could use multiple. [Epilogue](https://epilogue.micro.blog/) and [indiebookclub](https://indiebookclub.biz/) both let you post about books you&#39;ve read.

Or you could post from a native app, like [iA Writer](https://ia.net/writer/), a markdown editor that lets you post to WordPress, Ghost, and with Micropub: your own website.

The one Ricardo showed us in his demo, because it&#39;s got a good interface for posting about songs and albums, was [Sparkles](https://sparkles.sploot.com/).

I don&#39;t post about songs or albums, but I do like how it looks, so I included it in my demo:

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/04/27/my-indiewebcamp-dusseldorf-demo-extended-edition/sparkles.png&#34; alt=&#34;Sparkles user interface&#34; class=&#34;w-full h-auto rounded-lg&#34; width=&#34;1200&#34; height=&#34;749&#34; &gt;}}

## Bonus content

And that&#39;s where my demo ended.

Here&#39;s the extra footage.

### Home server

I first tried running IndieKit on the Raspberry Pi that hosts my site.

The Pi doesn&#39;t have any direct storage attached. That means I can&#39;t reliably run a database on it and IndieKit needs one for some of the features I want from it.

I didn&#39;t want to rent another server. I already have one at home, so I installed IndieKit there instead.

It works, I can access it from my phone, but only on my home network. Not much of an upgrade.

### Tunnel

The next step then was to add it to the &#34;tunnel&#34; setup I have.

I rent a VPS, which lives in Nürnberg by the way, and have [Pangolin](https://pangolin.net/) running on it, which is a reverse proxy that sends traffic down a Wireguard tunnel that ends in my home.

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/04/27/my-indiewebcamp-dusseldorf-demo-extended-edition/indiekit-phone.png&#34; alt=&#34;IndieKit mobile interface&#34; class=&#34;rounded-lg&#34; width=&#34;1179&#34; height=&#34;2556&#34; &gt;}}

and now I can post to my site, wherever I am 🎉.

## Deleted scenes

Dan&#39;s indie feed reader [feed.city](https://feed.city/) - which I discovered through IWC this weekend - caught me testing all of this and [recorded my test posts](https://feed.city/feed?url=https://paultibbetts.uk/feed.xml#946987).

Sometimes deleted scenes are deleted for a reason. They&#39;re boring.

I only included them here as part of the Extended Edition theme I&#39;ve chosen to use. Let&#39;s skip forward.

## Behind the scenes

When I use Sparkles to post to my site:

- I go to [sparkles.sploot.com](https://sparkles.sploot.com/) and enter my website&#39;s address
- Sparkles sees the address of the Micropub endpoint I declare in the `&lt;head&gt;` of my site
  - which points at IndieKit
- Sparkles connects to IndieKit
  - by going to the VPS in Germany
  - through the tunnel into my home in the UK
  - and finds IndieKit on my home server
- I write a new post
- IndieKit sends it to GitHub
  - GitHub creates a new markdown file
- the change triggers my deployment pipeline
  - which builds the site
  - and pushes it up to the Raspberry Pi

This is more complex than a typical hosted solution but it means I can self-host IndieKit at home and access it from anywhere.

If you&#39;re into that sort of stuff, stick around, I&#39;ll be posting about the setup and sharing the code soon.

## Sequel?

If you&#39;re not, then I might see you at a future IndieWebCamp, hopefully in-person that time.

Maybe that&#39;ll be [IndieWebCamp Nürnberg 2026](https://events.indieweb.org/2026/06/-indiewebcamp-nuremberg-7EIKg0lqfg93) and I&#39;ll get to meet my VPS for the first time, as well as some of the wonderful people who made this weekend possible.
</source:markdown></item><item>
      <title>Guten Morgen IndieWebCamp Düsseldorf. Today I’m working on: setting up IndieKit …</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/26/iwc-dus-sun/</link>
      <pubDate>Sun, 26 Apr 2026 09:43:42 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/26/iwc-dus-sun/</guid>
      <category>IndieWebCamp</category>
      <description>Guten Morgen IndieWebCamp Düsseldorf.
Today I’m working on:
setting up IndieKit so I can post from my phone changing my /links page so posts tagged as link and links are also included I’ll hopefully have at least one ready for demos later.</description>
      <content:encoded><![CDATA[<p>Guten Morgen IndieWebCamp Düsseldorf.</p>
<p>Today I&rsquo;m working on:</p>
<ul>
<li>setting up <a href="https://getindiekit.com">IndieKit</a>
 so I can post from my phone</li>
<li>changing my <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/links">/links</a>
 page so posts tagged as <code>link</code> and <code>links</code> are also included</li>
</ul>
<p>I&rsquo;ll hopefully have at least one ready for demos later.</p>]]></content:encoded><source:markdown>
Guten Morgen IndieWebCamp Düsseldorf.

Today I&#39;m working on:

- setting up [IndieKit](https://getindiekit.com) so I can post from my phone
- changing my [/links](/links) page so posts tagged as `link` and `links` are also included

I&#39;ll hopefully have at least one ready for demos later.
</source:markdown></item><item>
      <title>An IndieWebCamp Düsseldorf 2026 Saturday Summary</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/25/an-indiewebcamp-dusseldorf-2026-saturday-summary/</link>
      <pubDate>Sat, 25 Apr 2026 20:58:04 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/25/an-indiewebcamp-dusseldorf-2026-saturday-summary/</guid>
      <category>IndieWebCamp</category>
      <description>Feeling inspired after day one of IndieWebCamp Düsseldorf 2026 .</description>
      <content:encoded><![CDATA[<p>Feeling inspired after day one of <a href="https://events.indieweb.org/2026/04/-indiewebcamp-d%C3%BCsseldorf-ewHCZehNA3gg">IndieWebCamp Düsseldorf 2026</a>
.</p>
<h2 id="sessions">Sessions</h2>
<p>The first session of the day was <strong>Running your own LLM to get out of big tech</strong>. The word was inevitably going to come up, so it was good to hear an indie approach that lets you keep your privacy whilst using it.</p>
<p>The second session was <strong>Private posts</strong>, which didn&rsquo;t have a microphone nearby, so it wasn&rsquo;t recorded.</p>
<p>After a long lunch the third and fourth sessions were about <strong>Feeds</strong> and <strong>Canvas animations</strong>. It was good to hear creative talks happening, especially about boring tech like RSS.</p>
<aside>
<p>Did you know you can <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/follow">follow my blog</a>
?</p>
</aside>
<p>The last sessions of the day were <strong>Organising data</strong> and <strong>Homepage design</strong>. It turns out nobody really likes their current homepage, which was weirdly reassuring.</p>
<h2 id="bonus-session">Bonus session</h2>
<p>I say they were the last sessions, but there was actually a secret session right at the end of the day on <strong>How to unblock yourself and actually post more often</strong>. There were lots of good ideas mentioned, like <a href="https://polaroids.danielpietzsch.com/">Dan&rsquo;s Polaroids project</a>
 where he takes and publishes a Polaroid every day.</p>
<p>The one that stuck with me is the idea that you can write a brief summary about something that happened and then hit publish without spending too much time on it.</p>]]></content:encoded><source:markdown>
Feeling inspired after day one of [IndieWebCamp Düsseldorf 2026](https://events.indieweb.org/2026/04/-indiewebcamp-d%C3%BCsseldorf-ewHCZehNA3gg).

&lt;!--more--&gt;

## Sessions

The first session of the day was **Running your own LLM to get out of big tech**. The word was inevitably going to come up, so it was good to hear an indie approach that lets you keep your privacy whilst using it.

The second session was **Private posts**, which didn&#39;t have a microphone nearby, so it wasn&#39;t recorded.

After a long lunch the third and fourth sessions were about **Feeds** and **Canvas animations**. It was good to hear creative talks happening, especially about boring tech like RSS.

&lt;aside&gt;

Did you know you can [follow my blog](/follow)?

&lt;/aside&gt;

The last sessions of the day were **Organising data** and **Homepage design**. It turns out nobody really likes their current homepage, which was weirdly reassuring.

## Bonus session

I say they were the last sessions, but there was actually a secret session right at the end of the day on **How to unblock yourself and actually post more often**. There were lots of good ideas mentioned, like [Dan&#39;s Polaroids project](https://polaroids.danielpietzsch.com/) where he takes and publishes a Polaroid every day.

The one that stuck with me is the idea that you can write a brief summary about something that happened and then hit publish without spending too much time on it.
</source:markdown></item><item>
      <title>Hallo IndieWebCamp Düsseldorf! 👋 I’ll be joining in remotely this weekend. I do …</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/25/iwc-dus-sat-hallo/</link>
      <pubDate>Sat, 25 Apr 2026 09:30:54 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/25/iwc-dus-sat-hallo/</guid>
      <category>IndieWebCamp</category>
      <description>Hallo IndieWebCamp Düsseldorf! 👋
I’ll be joining in remotely this weekend. I do wish I was there with you all, maybe next time!</description>
      <content:encoded><![CDATA[<p>Hallo IndieWebCamp Düsseldorf! 👋</p>
<p>I&rsquo;ll be joining in remotely this weekend. I do wish I was there with you all, maybe next time!</p>]]></content:encoded><source:markdown>
Hallo IndieWebCamp Düsseldorf! 👋

I&#39;ll be joining in remotely this weekend. I do wish I was there with you all, maybe next time!
</source:markdown></item><item>
      <title>Liked: The Colonization of Confidence., Sightless Scribbles</title>
      <link>https://sightlessscribbles.com/posts/the-colonization-of-confidence/</link>
      <pubDate>Thu, 23 Apr 2026 11:58:50 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/23/2026-04-23-the-colonization-of-confidence-sightless-scribbles/</guid>
      <category>ai</category>
      <category>writing</category>
      <category>nonfiction</category>
      <description>“They are selling you a solution to a problem they created. They want you to feel insecure. If you feel insecure, you pay the subscription.”
“You are not bad. You are a jazz musician in a world trying to sell ringtones.”</description>
      <content:encoded><![CDATA[<blockquote>
<p>&ldquo;They are selling you a solution to a problem they created. They want you to feel insecure. If you feel insecure, you pay the subscription.&rdquo;</p>
</blockquote>
<blockquote>
<p>&ldquo;You are not bad. You are a jazz musician in a world trying to sell ringtones.&rdquo;</p>
</blockquote>]]></content:encoded><source:markdown>
&gt; &#34;They are selling you a solution to a problem they created. They want you to feel insecure. If you feel insecure, you pay the subscription.&#34;

&gt; &#34;You are not bad. You are a jazz musician in a world trying to sell ringtones.&#34;
</source:markdown></item><item>
      <title>Bookmarked: The internet used to be fun</title>
      <link>https://projects.kwon.nyc/internet-is-fun/</link>
      <pubDate>Wed, 22 Apr 2026 16:42:53 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/22/2026-04-22-the-internet-used-to-be-fun/</guid>
      <category>IndieWeb</category>
      <description>here’s a collection of articles that to some degree answer the question “Why have a personal website?” with “Because it’s fun, and the internet used to be fun.&amp;#34;
(It still is, but it used to be, too)</description>
      <content:encoded><![CDATA[<blockquote>
<p>here&rsquo;s a collection of articles that to some degree answer the question &ldquo;Why have a personal website?&rdquo; with “Because it&rsquo;s fun, and the internet used to be fun.&quot;</p>
</blockquote>
<blockquote>
<p>(It still is, but it used to be, too)</p>
</blockquote>]]></content:encoded><source:markdown>
&gt; here&#39;s a collection of articles that to some degree answer the question &#34;Why have a personal website?&#34; with “Because it&#39;s fun, and the internet used to be fun.&#34;

&gt; (It still is, but it used to be, too)
</source:markdown></item><item>
      <title>Liked: Open Tabs - Ricardo Chávez</title>
      <link>https://www.ricardochavezt.com/open_tabs</link>
      <pubDate>Mon, 20 Apr 2026 14:13:31 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/20/2026-04-20-open-tabs-ricardo-chavez/</guid>
      <category>digital garden</category>
      <description>Every time I visit Ricardo’s garden his open tabs become my open tabs.</description>
      <content:encoded><![CDATA[<p>Every time I visit Ricardo&rsquo;s garden his open tabs become my open tabs.</p>]]></content:encoded><source:markdown>
Every time I visit Ricardo&#39;s garden his open tabs become my open tabs.
</source:markdown></item><item>
      <title>I’ve seen a few indie web directories popping up lately, so in an attempt to …</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/16/2026-04-16-151643-indie-web-directories/</link>
      <pubDate>Thu, 16 Apr 2026 15:16:43 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/16/2026-04-16-151643-indie-web-directories/</guid>
      <category>IndieWeb</category>
      <category>Homebrew Website Club</category>
      <description>I’ve seen a few indie web directories popping up lately, so in an attempt to keep track of them all I’ve started on a directory of indie web directories .
Then I was introduced to Russel’s paradox , which asks if a directory should include itself?
The answer is no, you make another one .</description>
      <content:encoded><![CDATA[<p>I&rsquo;ve seen a few indie web directories popping up lately, so in an attempt to keep track of them all I&rsquo;ve started on a <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/indieweb-directories">directory of indie web directories</a>
.</p>
<p>Then I was introduced to <a href="https://en.wikipedia.org/wiki/Russell%27s_paradox">Russel&rsquo;s paradox</a>
, which asks if a directory should include itself?</p>
<p>The answer is <strong>no</strong>, you make <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/directory-of-indieweb-directories">another one</a>
.</p>]]></content:encoded><source:markdown>
I&#39;ve seen a few indie web directories popping up lately, so in an attempt to keep track of them all I&#39;ve started on a [directory of indie web directories](/indieweb-directories).

Then I was introduced to [Russel&#39;s paradox](https://en.wikipedia.org/wiki/Russell&#39;s_paradox), which asks if a directory should include itself?

The answer is **no**, you make [another one](/directory-of-indieweb-directories).
</source:markdown></item><item>
      <title>Bookmarked: Endgame for the Open Web - Anil Dash</title>
      <link>https://www.anildash.com/2026/03/27/endgame-open-web/</link>
      <pubDate>Thu, 16 Apr 2026 13:17:46 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/04/16/2026-04-16-endgame-for-the-open-web-anil-dash/</guid>
      <category>ai</category>
      <description>all the signs are pointing to the fact that we might be in endgame for “open” as we’ve known it on the Internet over the last few decades
See also:
Prepping for the endgame of the open web Some Thoughts on the Open Web</description>
      <content:encoded><![CDATA[<blockquote>
<p>all the signs are pointing to the fact that we might be in endgame for &ldquo;open&rdquo; as we&rsquo;ve known it on the Internet over the last few decades</p>
</blockquote>
<p>See also:</p>
<ul>
<li><a href="https://thehistoryoftheweb.com/prepping-for-the-endgame/">Prepping for the endgame of the open web</a>
</li>
<li><a href="https://www.mnot.net/blog/2026/01/20/open_web">Some Thoughts on the Open Web</a>
</li>
</ul>]]></content:encoded><source:markdown>
&gt; all the signs are pointing to the fact that we might be in endgame for &#34;open&#34; as we&#39;ve known it on the Internet over the last few decades

See also:

- [Prepping for the endgame of the open web](https://thehistoryoftheweb.com/prepping-for-the-endgame/)
- [Some Thoughts on the Open Web](https://www.mnot.net/blog/2026/01/20/open_web)
</source:markdown></item><item>
      <title>Yes, I&#39;m going to Homebrew Website Club London tonight. I get inspired by many …</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/18/2026-03-18-153902-i-m-going-to-homebrew-website-club-london-tonight/</link>
      <pubDate>Wed, 18 Mar 2026 16:00:00 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/18/2026-03-18-153902-i-m-going-to-homebrew-website-club-london-tonight/</guid>
      <category>Homebrew Website Club</category>
      <description>Yes, I&amp;#39;m going to Homebrew Website Club London tonight.</description>
      <content:encoded><![CDATA[<p class="p-summary">
	<span class="hidden"><span class="p-rsvp">Yes</span>, </span>I'm going to  
	<a href="https://events.indieweb.org/2026/03/homebrew-website-club-europe-london-0EMPkdPMzZKX" class="u-in-reply-to">Homebrew Website Club London tonight</a>.
</p>
<p>I get inspired by many things mentioned at <a href="https://indieweb.org/Homebrew_Website_Club">HWC</a>
, but somehow the first to make it to my blog, based on a comment from the last meetup, is this:</p>
<style>
  .h-entry.notes [data-morse-start],
  .h-entry.notes [data-morse-stop] {
    cursor: pointer;
  }

  .h-entry.notes [data-morse-start]:disabled,
  .h-entry.notes [data-morse-stop]:disabled {
    cursor: default;
    opacity: 0.5;
  }

  .h-entry.notes pre:has(code.language-morse),
  .h-entry.notes pre:has(code[data-lang="morse"]) {
    overflow-x: hidden;
  }

  .h-entry.notes pre code.language-morse,
  .h-entry.notes pre code[data-lang="morse"] {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    word-break: break-word;
  }
</style>
<div class="mt-2">
    <div class="flex items-center rounded-md bg-white/5 pl-3 outline-1 -outline-offset-1 outline-gray-600 has-[input:focus-within]:outline-2 has-[input:focus-within]:-outline-offset-2 has-[input:focus-within]:outline-indigo-500">
		<textarea
		    id="morse-input"
		    data-morse-input
		    rows="2"
		    style="width: 100%; margin-top: 0.25rem;"
		    class="block min-w-0 grow py-1.5 pr-3 pl-1 text-base text-white placeholder:text-gray-500 focus:outline-none sm:text-sm/6"
	>Hello, World!</textarea>
	</div>
</div>
<pre><code class="language-morse"></code></pre>
<p>
  <button type="button" data-morse-start>📡 Broadcast</button>
</p>
<p>
  <button type="button" data-morse-stop>⏹️ Stop</button>
</p>
<script type="text/javascript">
// It would be cool to say I wrote all of this myself
// but I asked an LLM to do it
// and then I realised it could have been a fun little project.
//
// I will learn from this, and choose my LLM usage
// more carefully in the future.
//
// This is the LLM code.
// I take no credit for writing it, other than
// writing the prompts that resulted in this.
//
// It seems to have come from one or two examples
// that one or two people have made in the past.
// There is one line where the LLM seems to argue with
// itself in the comments (#315).
(() => {
  // International Morse (letters+digits; extend as needed)
  const MORSE = {
    A: ".-",
    B: "-...",
    C: "-.-.",
    D: "-..",
    E: ".",
    F: "..-.",
    G: "--.",
    H: "....",
    I: "..",
    J: ".---",
    K: "-.-",
    L: ".-..",
    M: "--",
    N: "-.",
    O: "---",
    P: ".--.",
    Q: "--.-",
    R: ".-.",
    S: "...",
    T: "-",
    U: "..-",
    V: "...-",
    W: ".--",
    X: "-..-",
    Y: "-.--",
    Z: "--..",
    0: "-----",
    1: ".----",
    2: "..---",
    3: "...--",
    4: "....-",
    5: ".....",
    6: "-....",
    7: "--...",
    8: "---..",
    9: "----.",
    ".": ".-.-.-",
    ",": "--..--",
    "?": "..--..",
    "'": ".----.",
    "!": "-.-.--",
    "/": "-..-.",
    "(": "-.--.",
    ")": "-.--.-",
    "&": ".-...",
    ":": "---...",
    ";": "-.-.-.",
    "=": "-...-",
    "+": ".-.-.",
    "-": "-....-",
    "_": "..--.-",
    '"': ".-..-.",
    "$": "...-..-",
    "@": ".--.-."
  };

  const sleep = (ms) => new Promise(r => setTimeout(r, ms));

  function getMorseCodeElement() {
    return document.querySelector("code.language-morse, code[data-lang='morse']");
  }

  function getMorseInputElement() {
    return document.querySelector("[data-morse-input]");
  }

  function getMorseSourceText() {
    const inputEl = getMorseInputElement();
    if (inputEl) {
      const inputText = (inputEl.value || "").trim();
      return inputText || null;
    }

    const codeEl = getMorseCodeElement();
    if (!codeEl) return null;

    if (!codeEl.dataset.morseSource) {
      codeEl.dataset.morseSource = (codeEl.textContent || "").trim();
    }

    return codeEl.dataset.morseSource || null;
  }

  function renderMorseExample() {
    const codeEl = getMorseCodeElement();
    const sourceText = getMorseSourceText();
    if (!codeEl || !sourceText) return null;

    codeEl.textContent = encodeMorse(sourceText);
    return sourceText;
  }

  function encodeMorse(text) {
    const up = text.toUpperCase();
    // Words separated by " / ", letters by spaces
    const words = up.split(/\s+/).filter(Boolean);
    return words.map(word => {
      return word.split("").map(ch => MORSE[ch] ?? "").filter(Boolean).join(" ");
    }).join(" / ");
  }

  function makeBeepEngine() {
    const ctx = new (window.AudioContext || window.webkitAudioContext)();
    const gain = ctx.createGain();
    gain.gain.value = 0; // start silent
    gain.connect(ctx.destination);

    const osc = ctx.createOscillator();
    osc.type = "sine";
    osc.frequency.value = 650; // Hz
    osc.connect(gain);
    osc.start();

    return {
      ctx,
      async ensureRunning() {
        if (ctx.state !== "running") await ctx.resume();
      },
      on() { gain.gain.setTargetAtTime(0.12, ctx.currentTime, 0.005); },
      off() { gain.gain.setTargetAtTime(0.0, ctx.currentTime, 0.005); },
      stop() {
        try { osc.stop(); } catch {}
        ctx.close();
      }
    };
  }

  /**
   * Tap Morse live.
   * Timing is based on "unit" length (dot = 1u, dash = 3u).
   * - intra-symbol gap: 1u
   * - letter gap: 3u
   * - word gap: 7u (we approximate with "/" token)
   */
  async function tapMorse(morse, {
    unitMs = 90,
    useAudio = true,
    useVibrate = false,
    log = true,
    signal = undefined
  } = {}) {
    const engine = useAudio ? makeBeepEngine() : null;
    try {
      if (engine) await engine.ensureRunning();

      const tokens = morse.split(" "); // includes ".", "-", "/" as tokens
      if (log) console.log("[morse] tapping:", morse);

      for (let i = 0; i < tokens.length; i++) {
        if (signal?.aborted) throw new DOMException("Aborted", "AbortError");

        const t = tokens[i];

        if (t === ".") {
          engine?.on();
          if (useVibrate && navigator.vibrate) navigator.vibrate(unitMs);
          await sleep(unitMs);
          engine?.off();
          await sleep(unitMs); // intra-symbol gap
          continue;
        }

        if (t === "-") {
          engine?.on();
          if (useVibrate && navigator.vibrate) navigator.vibrate(unitMs * 3);
          await sleep(unitMs * 3);
          engine?.off();
          await sleep(unitMs); // intra-symbol gap
          continue;
        }

        if (t === "/") {
          // word gap: 7 units (we already likely had 1 unit after prior symbol)
          await sleep(unitMs * 6);
          continue;
        }

        // Letter boundary: when we see a multi-symbol chunk like ".-.." we won't,
        // because we tokenized by spaces. But we *do* need letter gaps.
        // Our encoding already put spaces between letters, so the token stream
        // is just symbols and "/" — the letter gap is handled by a longer pause
        // when the next token is a symbol but we are at a letter boundary.
        //
        // Easiest: detect original morse string boundaries by using the fact
        // that encodeMorse uses single spaces between letters, and "/" is separate.
        //
        // We already include the 1u intra-symbol gap after each dot/dash, so for
        // letter gap (3u) we add 2u extra when the *next* token is a symbol and
        // we are between letters. We can approximate by looking at the raw string.
      }
    } finally {
      engine?.stop();
    }
  }

  // Slightly better timing: tap by parsing the original morse string
  async function tapMorsePrecise(morse, opts = {}) {
    const {
      unitMs = 90, useAudio = true, useVibrate = false, log = true, signal
    } = opts;

    const engine = useAudio ? makeBeepEngine() : null;
    try {
      if (engine) await engine.ensureRunning();
      if (log) console.log("[morse] tapping:", morse);

      // Parse words split by " / "
      const words = morse.split(" / ").map(w => w.trim()).filter(Boolean);

      for (let w = 0; w < words.length; w++) {
        const letters = words[w].split(" ").map(l => l.trim()).filter(Boolean); // each letter is like ".-"
        for (let l = 0; l < letters.length; l++) {
          if (signal?.aborted) throw new DOMException("Aborted", "AbortError");

          const symbols = letters[l].split("");
          for (let s = 0; s < symbols.length; s++) {
            const sym = symbols[s];
            if (sym === ".") {
              engine?.on();
              if (useVibrate && navigator.vibrate) navigator.vibrate(unitMs);
              await sleep(unitMs);
              engine?.off();
            } else if (sym === "-") {
              engine?.on();
              if (useVibrate && navigator.vibrate) navigator.vibrate(unitMs * 3);
              await sleep(unitMs * 3);
              engine?.off();
            }
            // intra-symbol gap (1u) between parts of same letter
            if (s < symbols.length - 1) await sleep(unitMs);
          }
          // letter gap: 3u (but we've already waited 1u after last symbol? no)
          if (l < letters.length - 1) await sleep(unitMs * 3);
        }
        // word gap: 7u
        if (w < words.length - 1) await sleep(unitMs * 7);
      }
    } finally {
      engine?.stop();
    }
  }

  const START_BUTTON_SELECTOR = "[data-morse-start]";
  const STOP_BUTTON_SELECTOR = "[data-morse-stop]";
  const startButton = document.querySelector(START_BUTTON_SELECTOR);
  const stopButton = document.querySelector(STOP_BUTTON_SELECTOR);

  function setPlayingState(isPlaying) {
    if (startButton) startButton.disabled = isPlaying;
    if (stopButton) stopButton.disabled = !isPlaying;
  }

  // Controller so user can stop it
  let current = null;

  async function playMorse({
    unitMs = 90,
    useAudio = true,
    useVibrate = false,
    log = true
  } = {}) {
    const text = getMorseSourceText();

    if (!text) {
      console.warn("[morse] No morse source code block found.");
      return;
    }

    const morse = encodeMorse(text);

    // Abort previous run if any
    if (current?.controller) current.controller.abort();
    const controller = new AbortController();
    current = { controller };
    setPlayingState(true);

    try {
      await tapMorsePrecise(morse, { unitMs, useAudio, useVibrate, log, signal: controller.signal });
    } catch (e) {
      if (e?.name === "AbortError") {
        if (log) console.log("[morse] stopped.");
        return;
      }
      throw e;
    } finally {
      setPlayingState(false);
    }
  }

  function morseStop() {
    if (current?.controller) current.controller.abort();
    setPlayingState(false);
  }

  const sourceText = renderMorseExample();
  if (!sourceText) {
    console.warn("[morse] Could not render morse example from code block.");
  }

  const inputEl = getMorseInputElement();
  if (inputEl) {
    inputEl.addEventListener("input", () => {
      renderMorseExample();
    });
  }

  setPlayingState(false);

  if (startButton) {
    startButton.addEventListener("click", () => {
      playMorse();
    });
  } else {
    console.warn(`[morse] Start button not found (${START_BUTTON_SELECTOR}).`);
  }

  if (stopButton) {
    stopButton.addEventListener("click", morseStop);
  } else {
    console.warn(`[morse] Stop button not found (${STOP_BUTTON_SELECTOR}).`);
  }
})();
</script>
<p>To make this I was going to need to look up the Morse code alphabet, and then use it as part of a script.</p>
<p>I was feeling lazy so I used an LLM to do that for me, and it ended up writing the whole thing, including turning it into sound, which I hadn&rsquo;t even asked it to do.</p>
<p>It didn&rsquo;t just make it all, in seconds, it ruined any fun I might have had experimenting on a new thing, maybe even learning a thing or two.</p>
<p>So I present it here begrudgingly.</p>
<hr>
<p>I am using this note to test I can send an <a href="https://indieweb.org/rsvp">RSVP</a>
.</p>
<p>If you see my name <a href="https://events.indieweb.org/2026/03/homebrew-website-club-europe-london-0EMPkdPMzZKX#rsvps">on this list</a>
 then it worked.</p>]]></content:encoded><source:markdown>
&lt;p class=&#34;p-summary&#34;&gt;
	&lt;span class=&#34;hidden&#34;&gt;&lt;span class=&#34;p-rsvp&#34;&gt;Yes&lt;/span&gt;, &lt;/span&gt;I&#39;m going to  
	&lt;a href=&#34;https://events.indieweb.org/2026/03/homebrew-website-club-europe-london-0EMPkdPMzZKX&#34; class=&#34;u-in-reply-to&#34;&gt;Homebrew Website Club London tonight&lt;/a&gt;.
&lt;/p&gt;

&lt;!--more--&gt;

I get inspired by many things mentioned at [HWC](https://indieweb.org/Homebrew_Website_Club), but somehow the first to make it to my blog, based on a comment from the last meetup, is this:

&lt;style&gt;
  .h-entry.notes [data-morse-start],
  .h-entry.notes [data-morse-stop] {
    cursor: pointer;
  }

  .h-entry.notes [data-morse-start]:disabled,
  .h-entry.notes [data-morse-stop]:disabled {
    cursor: default;
    opacity: 0.5;
  }

  .h-entry.notes pre:has(code.language-morse),
  .h-entry.notes pre:has(code[data-lang=&#34;morse&#34;]) {
    overflow-x: hidden;
  }

  .h-entry.notes pre code.language-morse,
  .h-entry.notes pre code[data-lang=&#34;morse&#34;] {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    word-break: break-word;
  }
&lt;/style&gt;

&lt;div class=&#34;mt-2&#34;&gt;
    &lt;div class=&#34;flex items-center rounded-md bg-white/5 pl-3 outline-1 -outline-offset-1 outline-gray-600 has-[input:focus-within]:outline-2 has-[input:focus-within]:-outline-offset-2 has-[input:focus-within]:outline-indigo-500&#34;&gt;
		&lt;textarea
		    id=&#34;morse-input&#34;
		    data-morse-input
		    rows=&#34;2&#34;
		    style=&#34;width: 100%; margin-top: 0.25rem;&#34;
		    class=&#34;block min-w-0 grow py-1.5 pr-3 pl-1 text-base text-white placeholder:text-gray-500 focus:outline-none sm:text-sm/6&#34;
	&gt;Hello, World!&lt;/textarea&gt;
	&lt;/div&gt;
&lt;/div&gt;

```morse

```

&lt;p&gt;
  &lt;button type=&#34;button&#34; data-morse-start&gt;📡 Broadcast&lt;/button&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;button type=&#34;button&#34; data-morse-stop&gt;⏹️ Stop&lt;/button&gt;
&lt;/p&gt;

&lt;script type=&#34;text/javascript&#34;&gt;
// It would be cool to say I wrote all of this myself
// but I asked an LLM to do it
// and then I realised it could have been a fun little project.
//
// I will learn from this, and choose my LLM usage
// more carefully in the future.
//
// This is the LLM code.
// I take no credit for writing it, other than
// writing the prompts that resulted in this.
//
// It seems to have come from one or two examples
// that one or two people have made in the past.
// There is one line where the LLM seems to argue with
// itself in the comments (#315).
(() =&gt; {
  // International Morse (letters+digits; extend as needed)
  const MORSE = {
    A: &#34;.-&#34;,
    B: &#34;-...&#34;,
    C: &#34;-.-.&#34;,
    D: &#34;-..&#34;,
    E: &#34;.&#34;,
    F: &#34;..-.&#34;,
    G: &#34;--.&#34;,
    H: &#34;....&#34;,
    I: &#34;..&#34;,
    J: &#34;.---&#34;,
    K: &#34;-.-&#34;,
    L: &#34;.-..&#34;,
    M: &#34;--&#34;,
    N: &#34;-.&#34;,
    O: &#34;---&#34;,
    P: &#34;.--.&#34;,
    Q: &#34;--.-&#34;,
    R: &#34;.-.&#34;,
    S: &#34;...&#34;,
    T: &#34;-&#34;,
    U: &#34;..-&#34;,
    V: &#34;...-&#34;,
    W: &#34;.--&#34;,
    X: &#34;-..-&#34;,
    Y: &#34;-.--&#34;,
    Z: &#34;--..&#34;,
    0: &#34;-----&#34;,
    1: &#34;.----&#34;,
    2: &#34;..---&#34;,
    3: &#34;...--&#34;,
    4: &#34;....-&#34;,
    5: &#34;.....&#34;,
    6: &#34;-....&#34;,
    7: &#34;--...&#34;,
    8: &#34;---..&#34;,
    9: &#34;----.&#34;,
    &#34;.&#34;: &#34;.-.-.-&#34;,
    &#34;,&#34;: &#34;--..--&#34;,
    &#34;?&#34;: &#34;..--..&#34;,
    &#34;&#39;&#34;: &#34;.----.&#34;,
    &#34;!&#34;: &#34;-.-.--&#34;,
    &#34;/&#34;: &#34;-..-.&#34;,
    &#34;(&#34;: &#34;-.--.&#34;,
    &#34;)&#34;: &#34;-.--.-&#34;,
    &#34;&amp;&#34;: &#34;.-...&#34;,
    &#34;:&#34;: &#34;---...&#34;,
    &#34;;&#34;: &#34;-.-.-.&#34;,
    &#34;=&#34;: &#34;-...-&#34;,
    &#34;+&#34;: &#34;.-.-.&#34;,
    &#34;-&#34;: &#34;-....-&#34;,
    &#34;_&#34;: &#34;..--.-&#34;,
    &#39;&#34;&#39;: &#34;.-..-.&#34;,
    &#34;$&#34;: &#34;...-..-&#34;,
    &#34;@&#34;: &#34;.--.-.&#34;
  };

  const sleep = (ms) =&gt; new Promise(r =&gt; setTimeout(r, ms));

  function getMorseCodeElement() {
    return document.querySelector(&#34;code.language-morse, code[data-lang=&#39;morse&#39;]&#34;);
  }

  function getMorseInputElement() {
    return document.querySelector(&#34;[data-morse-input]&#34;);
  }

  function getMorseSourceText() {
    const inputEl = getMorseInputElement();
    if (inputEl) {
      const inputText = (inputEl.value || &#34;&#34;).trim();
      return inputText || null;
    }

    const codeEl = getMorseCodeElement();
    if (!codeEl) return null;

    if (!codeEl.dataset.morseSource) {
      codeEl.dataset.morseSource = (codeEl.textContent || &#34;&#34;).trim();
    }

    return codeEl.dataset.morseSource || null;
  }

  function renderMorseExample() {
    const codeEl = getMorseCodeElement();
    const sourceText = getMorseSourceText();
    if (!codeEl || !sourceText) return null;

    codeEl.textContent = encodeMorse(sourceText);
    return sourceText;
  }

  function encodeMorse(text) {
    const up = text.toUpperCase();
    // Words separated by &#34; / &#34;, letters by spaces
    const words = up.split(/\s+/).filter(Boolean);
    return words.map(word =&gt; {
      return word.split(&#34;&#34;).map(ch =&gt; MORSE[ch] ?? &#34;&#34;).filter(Boolean).join(&#34; &#34;);
    }).join(&#34; / &#34;);
  }

  function makeBeepEngine() {
    const ctx = new (window.AudioContext || window.webkitAudioContext)();
    const gain = ctx.createGain();
    gain.gain.value = 0; // start silent
    gain.connect(ctx.destination);

    const osc = ctx.createOscillator();
    osc.type = &#34;sine&#34;;
    osc.frequency.value = 650; // Hz
    osc.connect(gain);
    osc.start();

    return {
      ctx,
      async ensureRunning() {
        if (ctx.state !== &#34;running&#34;) await ctx.resume();
      },
      on() { gain.gain.setTargetAtTime(0.12, ctx.currentTime, 0.005); },
      off() { gain.gain.setTargetAtTime(0.0, ctx.currentTime, 0.005); },
      stop() {
        try { osc.stop(); } catch {}
        ctx.close();
      }
    };
  }

  /**
   * Tap Morse live.
   * Timing is based on &#34;unit&#34; length (dot = 1u, dash = 3u).
   * - intra-symbol gap: 1u
   * - letter gap: 3u
   * - word gap: 7u (we approximate with &#34;/&#34; token)
   */
  async function tapMorse(morse, {
    unitMs = 90,
    useAudio = true,
    useVibrate = false,
    log = true,
    signal = undefined
  } = {}) {
    const engine = useAudio ? makeBeepEngine() : null;
    try {
      if (engine) await engine.ensureRunning();

      const tokens = morse.split(&#34; &#34;); // includes &#34;.&#34;, &#34;-&#34;, &#34;/&#34; as tokens
      if (log) console.log(&#34;[morse] tapping:&#34;, morse);

      for (let i = 0; i &lt; tokens.length; i++) {
        if (signal?.aborted) throw new DOMException(&#34;Aborted&#34;, &#34;AbortError&#34;);

        const t = tokens[i];

        if (t === &#34;.&#34;) {
          engine?.on();
          if (useVibrate &amp;&amp; navigator.vibrate) navigator.vibrate(unitMs);
          await sleep(unitMs);
          engine?.off();
          await sleep(unitMs); // intra-symbol gap
          continue;
        }

        if (t === &#34;-&#34;) {
          engine?.on();
          if (useVibrate &amp;&amp; navigator.vibrate) navigator.vibrate(unitMs * 3);
          await sleep(unitMs * 3);
          engine?.off();
          await sleep(unitMs); // intra-symbol gap
          continue;
        }

        if (t === &#34;/&#34;) {
          // word gap: 7 units (we already likely had 1 unit after prior symbol)
          await sleep(unitMs * 6);
          continue;
        }

        // Letter boundary: when we see a multi-symbol chunk like &#34;.-..&#34; we won&#39;t,
        // because we tokenized by spaces. But we *do* need letter gaps.
        // Our encoding already put spaces between letters, so the token stream
        // is just symbols and &#34;/&#34; — the letter gap is handled by a longer pause
        // when the next token is a symbol but we are at a letter boundary.
        //
        // Easiest: detect original morse string boundaries by using the fact
        // that encodeMorse uses single spaces between letters, and &#34;/&#34; is separate.
        //
        // We already include the 1u intra-symbol gap after each dot/dash, so for
        // letter gap (3u) we add 2u extra when the *next* token is a symbol and
        // we are between letters. We can approximate by looking at the raw string.
      }
    } finally {
      engine?.stop();
    }
  }

  // Slightly better timing: tap by parsing the original morse string
  async function tapMorsePrecise(morse, opts = {}) {
    const {
      unitMs = 90, useAudio = true, useVibrate = false, log = true, signal
    } = opts;

    const engine = useAudio ? makeBeepEngine() : null;
    try {
      if (engine) await engine.ensureRunning();
      if (log) console.log(&#34;[morse] tapping:&#34;, morse);

      // Parse words split by &#34; / &#34;
      const words = morse.split(&#34; / &#34;).map(w =&gt; w.trim()).filter(Boolean);

      for (let w = 0; w &lt; words.length; w++) {
        const letters = words[w].split(&#34; &#34;).map(l =&gt; l.trim()).filter(Boolean); // each letter is like &#34;.-&#34;
        for (let l = 0; l &lt; letters.length; l++) {
          if (signal?.aborted) throw new DOMException(&#34;Aborted&#34;, &#34;AbortError&#34;);

          const symbols = letters[l].split(&#34;&#34;);
          for (let s = 0; s &lt; symbols.length; s++) {
            const sym = symbols[s];
            if (sym === &#34;.&#34;) {
              engine?.on();
              if (useVibrate &amp;&amp; navigator.vibrate) navigator.vibrate(unitMs);
              await sleep(unitMs);
              engine?.off();
            } else if (sym === &#34;-&#34;) {
              engine?.on();
              if (useVibrate &amp;&amp; navigator.vibrate) navigator.vibrate(unitMs * 3);
              await sleep(unitMs * 3);
              engine?.off();
            }
            // intra-symbol gap (1u) between parts of same letter
            if (s &lt; symbols.length - 1) await sleep(unitMs);
          }
          // letter gap: 3u (but we&#39;ve already waited 1u after last symbol? no)
          if (l &lt; letters.length - 1) await sleep(unitMs * 3);
        }
        // word gap: 7u
        if (w &lt; words.length - 1) await sleep(unitMs * 7);
      }
    } finally {
      engine?.stop();
    }
  }

  const START_BUTTON_SELECTOR = &#34;[data-morse-start]&#34;;
  const STOP_BUTTON_SELECTOR = &#34;[data-morse-stop]&#34;;
  const startButton = document.querySelector(START_BUTTON_SELECTOR);
  const stopButton = document.querySelector(STOP_BUTTON_SELECTOR);

  function setPlayingState(isPlaying) {
    if (startButton) startButton.disabled = isPlaying;
    if (stopButton) stopButton.disabled = !isPlaying;
  }

  // Controller so user can stop it
  let current = null;

  async function playMorse({
    unitMs = 90,
    useAudio = true,
    useVibrate = false,
    log = true
  } = {}) {
    const text = getMorseSourceText();

    if (!text) {
      console.warn(&#34;[morse] No morse source code block found.&#34;);
      return;
    }

    const morse = encodeMorse(text);

    // Abort previous run if any
    if (current?.controller) current.controller.abort();
    const controller = new AbortController();
    current = { controller };
    setPlayingState(true);

    try {
      await tapMorsePrecise(morse, { unitMs, useAudio, useVibrate, log, signal: controller.signal });
    } catch (e) {
      if (e?.name === &#34;AbortError&#34;) {
        if (log) console.log(&#34;[morse] stopped.&#34;);
        return;
      }
      throw e;
    } finally {
      setPlayingState(false);
    }
  }

  function morseStop() {
    if (current?.controller) current.controller.abort();
    setPlayingState(false);
  }

  const sourceText = renderMorseExample();
  if (!sourceText) {
    console.warn(&#34;[morse] Could not render morse example from code block.&#34;);
  }

  const inputEl = getMorseInputElement();
  if (inputEl) {
    inputEl.addEventListener(&#34;input&#34;, () =&gt; {
      renderMorseExample();
    });
  }

  setPlayingState(false);

  if (startButton) {
    startButton.addEventListener(&#34;click&#34;, () =&gt; {
      playMorse();
    });
  } else {
    console.warn(`[morse] Start button not found (${START_BUTTON_SELECTOR}).`);
  }

  if (stopButton) {
    stopButton.addEventListener(&#34;click&#34;, morseStop);
  } else {
    console.warn(`[morse] Stop button not found (${STOP_BUTTON_SELECTOR}).`);
  }
})();
&lt;/script&gt;

To make this I was going to need to look up the Morse code alphabet, and then use it as part of a script.

I was feeling lazy so I used an LLM to do that for me, and it ended up writing the whole thing, including turning it into sound, which I hadn&#39;t even asked it to do.

It didn&#39;t just make it all, in seconds, it ruined any fun I might have had experimenting on a new thing, maybe even learning a thing or two.

So I present it here begrudgingly.

---

I am using this note to test I can send an [RSVP](https://indieweb.org/rsvp).

If you see my name [on this list](https://events.indieweb.org/2026/03/homebrew-website-club-europe-london-0EMPkdPMzZKX#rsvps) then it worked.
</source:markdown></item><item>
      <title>Added support for photos and videos</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/17/added-support-for-photos-and-videos/</link>
      <pubDate>Tue, 17 Mar 2026 23:30:00 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/17/added-support-for-photos-and-videos/</guid>
      <category>meta</category>
      <description>Three days after posting Changes to the Site and it’s already out of date.
I wrote that I was going to optimise my photos so they would be smaller and I could include them in my posts, since that’s all I was planning to do.
Then I realised I needed a video in my post. If you’ve read Museum memories you’ll know why.</description>
      <content:encoded><![CDATA[<p>Three days after posting <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/14/changes-to-the-site/">Changes to the Site</a>
 and it&rsquo;s already out of date.</p>
<p>I wrote that I was going to optimise my photos so they would be smaller and I could include them in my posts, since that&rsquo;s all I was planning to do.</p>
<p>Then I realised I <em>needed</em> a video in my post. If you&rsquo;ve read <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/17/museum-memories">Museum memories</a>
 you&rsquo;ll know why.</p>
<h2 id="the-problem">The problem</h2>
<p>My website is powered by Hugo, a static site generator that takes an input, like my markdown files where I write my posts, and outputs a website.</p>
<p>I store both my site code and content in Git, which also drives my deployment process: every change triggers a new build and deploy.</p>
<p>This is where it gets tricky with media files.</p>
<h3 id="photos">Photos</h3>
<p>To include my own photos in a blog post I need to host them. I can do that in Hugo, but I would need to save the photos with Git so it&rsquo;s part of the site and the deployment process. The problem with this is photos can be quite large, so they bloat the Git repository.</p>
<p>This isn&rsquo;t really a problem with one blog post with a few photos, but it does become a problem when you do it over and over for many years.</p>
<p>I don&rsquo;t know if I&rsquo;ll be posting lots of photos, or doing it for many years, but I don&rsquo;t like the idea of one day having a scaling problem, I&rsquo;d rather fix it now.</p>
<h3 id="videos">Videos</h3>
<p>Videos are even more of a problem. They&rsquo;re much bigger than photos, and they don&rsquo;t compress down as effectively.</p>
<p>A 7 MB photo can compress down to 200 KB, which might be tolerable in a Git repo.</p>
<p>A 15 second video might be 30 MB and only compress down to 15 MB.</p>
<h2 id="a-solution">A solution</h2>
<p>Instead of keeping my photos and videos inside the Git repo I am moving towards separate media hosting for them. I wanted to publish <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/17/museum-memories/">my photo post</a>
 before fully solving media hosting, so I&rsquo;m using a temporary solution for now.</p>
<p>I have made a directory on my server and uploaded the videos there. I then changed my web server to serve the files in this directory at <code>https://paultibbetts.uk/media/</code> and updated my post to use this URL for videos.</p>
<p>You can see the change in infrastructure code <a href="https://github.com/paultibbetts/infra.paultibbetts.uk/commit/72240f724e786a189c2138d73a643c3b2ae4b4d9">on GitHub</a>
.</p>
<h2 id="its-temporary">It&rsquo;s Temporary™</h2>
<p>My web server has 50 GB of storage so there is a limit to how many photos and videos I can upload. This type of storage is relatively expensive and it&rsquo;s awkward changing the setup.</p>
<p>A better solution for media hosting would be a storage bucket and a Content Delivery Network to serve the files, as mentioned in <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/14/changes-to-the-site/">Changes to the Site</a>
.</p>
<p>I don&rsquo;t know when that&rsquo;s happening.</p>]]></content:encoded><source:markdown>
Three days after posting [Changes to the Site](/2026/03/14/changes-to-the-site/) and it&#39;s already out of date.

I wrote that I was going to optimise my photos so they would be smaller and I could include them in my posts, since that&#39;s all I was planning to do.

Then I realised I _needed_ a video in my post. If you&#39;ve read [Museum memories](/2026/03/17/museum-memories) you&#39;ll know why.

&lt;!--more--&gt;

## The problem

My website is powered by Hugo, a static site generator that takes an input, like my markdown files where I write my posts, and outputs a website.

I store both my site code and content in Git, which also drives my deployment process: every change triggers a new build and deploy.

This is where it gets tricky with media files.

### Photos

To include my own photos in a blog post I need to host them. I can do that in Hugo, but I would need to save the photos with Git so it&#39;s part of the site and the deployment process. The problem with this is photos can be quite large, so they bloat the Git repository.

This isn&#39;t really a problem with one blog post with a few photos, but it does become a problem when you do it over and over for many years.

I don&#39;t know if I&#39;ll be posting lots of photos, or doing it for many years, but I don&#39;t like the idea of one day having a scaling problem, I&#39;d rather fix it now.

### Videos

Videos are even more of a problem. They&#39;re much bigger than photos, and they don&#39;t compress down as effectively.

A 7 MB photo can compress down to 200 KB, which might be tolerable in a Git repo.

A 15 second video might be 30 MB and only compress down to 15 MB.

## A solution

Instead of keeping my photos and videos inside the Git repo I am moving towards separate media hosting for them. I wanted to publish [my photo post](/2026/03/17/museum-memories/) before fully solving media hosting, so I&#39;m using a temporary solution for now.

I have made a directory on my server and uploaded the videos there. I then changed my web server to serve the files in this directory at `https://paultibbetts.uk/media/` and updated my post to use this URL for videos.

You can see the change in infrastructure code [on GitHub](https://github.com/paultibbetts/infra.paultibbetts.uk/commit/72240f724e786a189c2138d73a643c3b2ae4b4d9).

## It&#39;s Temporary™

My web server has 50 GB of storage so there is a limit to how many photos and videos I can upload. This type of storage is relatively expensive and it&#39;s awkward changing the setup.

A better solution for media hosting would be a storage bucket and a Content Delivery Network to serve the files, as mentioned in [Changes to the Site](/2026/03/14/changes-to-the-site/).

I don&#39;t know when that&#39;s happening.
</source:markdown></item><item>
      <title>Museum memories</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/17/museum-memories/</link>
      <pubDate>Tue, 17 Mar 2026 14:00:22 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/17/museum-memories/</guid>
      <category>IndieWeb Carnival</category>
      <category>Barcelona</category>
      <description>This is my entry for this month’s IndieWeb Carnival: Museum memories.
I have not been to many museums or galleries. I am not a cultured person.
My favourite museum is the city of Barcelona.</description>
      <content:encoded><![CDATA[<p>This is my entry for this month&rsquo;s IndieWeb Carnival: <a class="u-in-reply-to" href="https://jamesg.blog/2026/03/01/indieweb-carnival-museum-memories/">Museum memories</a>.</p>
<p>I have not been to many museums or galleries. I am not a cultured person.</p>
<p>My favourite museum is the city of Barcelona.</p>
<h2 id="museums">Museums</h2>
<p>I grew up in Birmingham, UK, and I have been to a few museums there. We went on a school trip to the <a href="https://www.birminghammuseums.org.uk/thinktank">Science Museum</a>
 and I&rsquo;m pretty sure I&rsquo;ve been down to London to go to the Natural History Museum.</p>
<p>They&rsquo;re all great.</p>
<p>My favourite museum trips were all in Barcelona, and there must be something to that city because out of the three entries to this month&rsquo;s carnival I&rsquo;ve read there&rsquo;s already <a href="https://jeffbridgforth.com/two-museum-memories/">one about a museum there</a>
.</p>
<h2 id="barcelona">Barcelona</h2>
<figure>
      <img
        src="https://paultibbetts.uk/media/2026/03/17/museum-memories/barcelona.jpg"
        width="1200"
        height="800"
        alt="Barcelona&#39;s Gothic Quarter with ornate balconies and Sagrada Família visible in the distance"
        loading="lazy"
        decoding="async"
        class="w-full h-auto rounded-lg"
      />
      <figcaption>not my photo</figcaption>
    </figure>
<p>I first went in 2017, staying for a week in a hotel in Eixample, a giant extension to the city that was started in the 1860s.</p>
<p>The Old Quarter, the historic core, dates back over 2,000 years. It was founded by the Romans as Barcino and some of their walls are still standing.</p>
<p>The development of other parts of the city, like Barri Gòtic and El Born, happened during the medieval period and were built right on top of what came before.</p>
<img
      src="https://paultibbetts.uk/media/2026/03/17/museum-memories/old_wall.jpeg"
      width="1200"
      height="1600"
      alt="Ancient stone foundations and walls preserved beneath buildings in Barcelona"
      loading="lazy"
      decoding="async"
      class="rounded-lg"
    />
<p>It&rsquo;s chaotic and organic.</p>
<h3 id="eixample">Eixample</h3>
<p>Eixample, known in English as &ldquo;extension&rdquo;, is the complete opposite. It was designed as a uniform grid layout, and turned out to be very forward-thinking design.</p>
<figure>
      <img
        src="https://paultibbetts.uk/media/2026/03/17/museum-memories/eixample.jpg"
        width="1200"
        height="827"
        alt="Aerial view of Barcelona&#39;s Eixample district showing its distinctive grid layout with octagonal blocks"
        loading="lazy"
        decoding="async"
        class="w-full h-auto rounded-lg"
      />
      <figcaption>also not my photo</figcaption>
    </figure>
<p>The octagonal blocks give better visibility at intersections and make turning easier for vehicles, even ones we use now, which didn&rsquo;t exist when the design was first conceived.</p>
<h3 id="museums-1">Museums</h3>
<p>I mention all of this because the city itself is a giant museum. You can step back in time, in design, and urban planning, and wander through the narrow cobbled streets of the Old Quarter, then walk ten minutes to Eixample to see an extension, started two hundred years ago, that&rsquo;s still celebrated for how modern it is.</p>
<figure>
      <img
        src="https://paultibbetts.uk/media/2026/03/17/museum-memories/museum.jpeg"
        width="1200"
        height="900"
        alt="Museum"
        loading="lazy"
        decoding="async"
        class="rounded-lg"
      />
      <figcaption>Castle of the Three Dragons</figcaption>
    </figure>
<p>Barcelona has free museum visits on Sundays (in most city-run museums), with free entry from 3pm onwards and all day on the first Sunday of the month.</p>
<p>I made a second trip later in 2017 for a month, and ended up staying six weeks. I visited most of <a href="https://www.barcelonaturisme.com/wv3/en/enjoy/25/a-zero-cost-cultural-afternoon.html">the free museums</a>
 whilst I was there,</p>
<p>as well as Castell de Montjuïc, the fort on the hill.</p>
<div class="not-prose image-grid my-6">
<img
      src="https://paultibbetts.uk/media/2026/03/17/museum-memories/fort_garden.jpeg"
      width="1200"
      height="900"
      alt="Gardens and stone walls at Castell de Montjuïc overlooking Barcelona"
      loading="lazy"
      decoding="async"
      class="rounded-lg"
    />

<img
      src="https://paultibbetts.uk/media/2026/03/17/museum-memories/fort_entrance.jpeg"
      width="1200"
      height="900"
      alt="Entrance to Castell de Montjuïc with stone walls and walkway"
      loading="lazy"
      decoding="async"
      class="rounded-lg"
    />
</div>

<p>It was interesting to see how it was being used.</p>
<figure>
      <img
        src="https://paultibbetts.uk/media/2026/03/17/museum-memories/fort_inside.jpeg"
        width="1200"
        height="675"
        alt="Interior courtyard of Castell de Montjuïc with white flags showing messages of peace"
        loading="lazy"
        decoding="async"
        class="not-prose wide-photo rounded-lg"
      />
      <figcaption><blockquote>Do economic and military interests help educate for peace?</blockquote></figcaption>
    </figure>
<p>Barcelona does a really good job of preserving layers of its past, from Castell de Montjuïc, to Roman walls in the Gothic Quarter and entire neighbourhoods from the 1700s, like those uncovered in El Born.</p>
<p>These are curated spaces that preserve and display the history of the city itself.</p>
<figure>
      <img
        src="https://paultibbetts.uk/media/2026/03/17/museum-memories/old_walls.jpeg"
        width="1200"
        height="1600"
        alt="Preserved walls from the 18th century displayed under a covered walkway"
        loading="lazy"
        decoding="async"
        class="rounded-lg"
      />
      <figcaption>El Born Centre de Cultura i Memòria</figcaption>
    </figure>
<p>I don&rsquo;t have a favourite museum in Barcelona. Barcelona is my favourite museum.</p>
<h3 id="galleries">Galleries</h3>
<p>It&rsquo;s also a giant street art gallery.</p>
<p>The residents communicate with posters, stickers and slogans:</p>
<img
      src="https://paultibbetts.uk/media/2026/03/17/museum-memories/street_art.jpeg"
      width="1200"
      height="1600"
      alt="Street posters and stickers covering a wall"
      loading="lazy"
      decoding="async"
      class="rounded-lg"
    />
<p>To deter graffiti many property owners commission work for their shutters:</p>
<img
      src="https://paultibbetts.uk/media/2026/03/17/museum-memories/painted_door.jpeg"
      width="1200"
      height="900"
      alt="Colourful mural painted on a metal shop shutter"
      loading="lazy"
      decoding="async"
      class="col-span-2 w-full h-auto rounded-lg"
    />
<div class="not-prose image-grid my-6">
<img
      src="https://paultibbetts.uk/media/2026/03/17/museum-memories/painted_door_bird.jpeg"
      width="1200"
      height="1600"
      alt="Street art mural of a bird painted on a shop shutter"
      loading="lazy"
      decoding="async"
      class="w-full h-auto rounded-lg"
    />

<img
      src="https://paultibbetts.uk/media/2026/03/17/museum-memories/painted_door_woman.jpeg"
      width="1200"
      height="1600"
      alt="Street art mural of a woman painted on a shop shutter"
      loading="lazy"
      decoding="async"
      class="w-full h-auto rounded-lg"
    />
</div>

<p>which is respected and left untouched.</p>
<p>The streets become a gallery, with guides offering tours of their own curated favourites.</p>
<img
      src="https://paultibbetts.uk/media/2026/03/17/museum-memories/gato.jpeg"
      width="1200"
      height="900"
      alt="Street art of 2 cat heads formed into one"
      loading="lazy"
      decoding="async"
      class="w-full h-auto rounded-lg"
    />
<h2 id="architecture">Architecture</h2>
<p>Speaking of streets, some of the best views are of the architecture.</p>
<p>Whether it&rsquo;s an open public space…</p>
<img
      src="https://paultibbetts.uk/media/2026/03/17/museum-memories/parque.jpeg"
      width="1200"
      height="960"
      alt="Open public space around the Arc de Triomf"
      loading="lazy"
      decoding="async"
      class="rounded-lg"
    />
<p>or one of the many gold statues…</p>
<img
      src="https://paultibbetts.uk/media/2026/03/17/museum-memories/park.jpeg"
      width="1200"
      height="960"
      alt="Ornate golden statue in a Barcelona park surrounded by greenery"
      loading="lazy"
      decoding="async"
      class="rounded-lg"
    />
<p>or an ornate entrance to a church…</p>
<img
      src="https://paultibbetts.uk/media/2026/03/17/museum-memories/sagrada_familia_entrance.jpeg"
      width="1200"
      height="900"
      alt="Highly detailed sculpted entrance of the Sagrada Família basilica"
      loading="lazy"
      decoding="async"
      class="rounded-lg"
    />
<p>it&rsquo;s hard not to admire the intricate details and extravagance of Barcelona.</p>
<p>A lot of that is the result of Antoni Gaudí, a Catalan architect and designer, whose designs stretched from parks and gardens:</p>
<figure>
      <img
        src="https://paultibbetts.uk/media/2026/03/17/museum-memories/fantasy_house.jpeg"
        width="1200"
        height="1200"
        alt="Colourful Gaudí-designed house in Park Güell"
        loading="lazy"
        decoding="async"
        class="rounded-lg"
      />
      <figcaption>Park Güell</figcaption>
    </figure>
<p>to one of the most ambitious Catholic churches in the world, started 143 years ago and still under construction:</p>
<figure>
      <img
        src="https://paultibbetts.uk/media/2026/03/17/museum-memories/sagrada_familia.jpeg"
        width="1200"
        height="1600"
        alt="The towering spires of the Sagrada Família basilica in Barcelona"
        loading="lazy"
        decoding="async"
        class="rounded-lg"
      />
      <figcaption>Sagrada Família</figcaption>
    </figure>
<h2 id="culture">Culture</h2>
<p>Perhaps the biggest highlight of my first trip was its timing. I&rsquo;d accidentally arrived during La Mercè, Barcelona&rsquo;s annual festival that&rsquo;s been going on since 1871.</p>
<p>Every corner of the city was filled with art and music…</p>
<img
      src="https://paultibbetts.uk/media/2026/03/17/museum-memories/music_show.jpeg"
      width="1200"
      height="960"
      alt="Outdoor music performance during the La Mercè festival in Barcelona"
      loading="lazy"
      decoding="async"
      class="wide-photo rounded-lg"
    />
<p>as well as papier-mâché giants and castells (human pyramids, or &ldquo;castles&rdquo;)…</p>
<img
      src="https://paultibbetts.uk/media/2026/03/17/museum-memories/la_merce.jpeg"
      width="1200"
      height="675"
      alt="Human pyramid and giant papier-mâché figures during La Mercè festival"
      loading="lazy"
      decoding="async"
      class="wide-photo rounded-lg"
    />
<p>and the wildest fireworks show…</p>
<video controls playsinline preload="none" poster="https://paultibbetts.uk/media/2026/03/17/museum-memories/fireworks_poster.jpeg" width="1200" height="675">
  <source src="https://paultibbetts.uk/media/2026/03/17/museum-memories/fireworks.webm" type="video/webm">
  <a href="https://paultibbetts.uk/media/2026/03/17/museum-memories/fireworks.webm">Open the fireworks video directly</a>.
</video>
<p>I&rsquo;ve ever been to…</p>
<video controls playsinline preload="none" poster="https://paultibbetts.uk/media/2026/03/17/museum-memories/more_fireworks_poster.jpeg" width="1200" height="675">
  <source src="https://paultibbetts.uk/media/2026/03/17/museum-memories/more_fireworks.webm" type="video/webm">
  <a href="https://paultibbetts.uk/media/2026/03/17/museum-memories/more_fireworks.webm">Open the second fireworks video directly</a>.
</video>
<h2 id="2017">2017</h2>
<p>What made 2017 special was the result of hundreds of years of history - it marked the first independence referendum hosted by the Catalan government.</p>
<figure>
      <img
        src="https://paultibbetts.uk/media/2026/03/17/museum-memories/democracy_month.jpeg"
        width="1200"
        height="900"
        alt="Poster reading &#39;Democracy Month&#39; displayed during Catalonia&#39;s independence movement"
        loading="lazy"
        decoding="async"
        class="rounded-lg"
      />
      <figcaption>democracy month</figcaption>
    </figure>
<p>The city was covered in flags, and signs that said &ldquo;Si&rdquo; - which meant yes, if given the chance to vote.</p>
<div class="not-prose image-grid my-6">
<img
      src="https://paultibbetts.uk/media/2026/03/17/museum-memories/want_to_vote.jpeg"
      width="900"
      height="720"
      alt="People surround a poster that reads &#39;We want to vote&#39;"
      loading="lazy"
      decoding="async"
      class="rounded-lg"
    />
<img
      src="https://paultibbetts.uk/media/2026/03/17/museum-memories/wants_to_vote.jpeg"
      width="1200"
      height="960"
      alt="Poster on a balcony that reads &#39;Catalonia wants to vote&#39;"
      loading="lazy"
      decoding="async"
      class="rounded-lg"
    />
</div>

<p>There were plenty of Si supporters…</p>
<img
      src="https://paultibbetts.uk/media/2026/03/17/museum-memories/flags_parade.jpeg"
      width="1200"
      height="675"
      alt="Lots of Catalan flags"
      loading="lazy"
      decoding="async"
      class="rounded-lg"
    />
<p>They once interrupted a conversation I was having, and it was the highlight of my trip.</p>
<p>At, I think it was, 6pm, supporters of the vote leaned out their windows and started smashing pots and pans. It was a DIY siren to let everyone else know they existed, and that they wanted to vote.</p>
<p>It was deafeningly loud and an extremely effective way of communicating.</p>
<h2 id="history">History</h2>
<p>I&rsquo;m no expert, but a brief history is:</p>
<ul>
<li>in the Middle Ages, Catalonia was part of the Crown of Aragon</li>
<li>the War of Spanish Succession (1701-1714) saw Catalonia become part of Spain</li>
<li>the dictatorship of Francisco Franco restricted Catalan language and culture from 1939 to 1975</li>
<li>in 2006 Catalonia negotiated a new agreement with Spain</li>
<li>Spain&rsquo;s conservative party opposed it and it went to court for 4 years</li>
<li>in 2010 the court ruled that parts of the agreement should be annulled</li>
<li>Catalans saw the agreement as settled and not to be changed</li>
<li>this sparked a resurgence in the independence movement</li>
</ul>
<p>which led to the referendum in 2017.</p>
<h2 id="the-result">The result</h2>
<p>The Catalan government claimed that 90% of voters voted &ldquo;Yes&rdquo;, however they also recognised that it had only a 42% turnout.</p>
<p>Because of this, Spain ignored the result and then called it illegal anyway.</p>
<p>A month later Catalonia&rsquo;s parliament declared independence.</p>
<p>The next day Spain dissolved the Catalan government, arrested several of its leaders, and the regional president Carles Puigdemont fled to Belgium.</p>
<h2 id="since-then">Since then</h2>
<p>Unfortunately, since then, the only notable things to have happened are trials and prison sentences followed by a new amnesty law that pardons some of those involved in the push for independence.</p>
<p>I wish this had a better ending, and that the Catalan people were given a legal vote to decide what they want to do. I don&rsquo;t know if independence is a good or bad thing for Catalonia, I&rsquo;m just a tourist with an admiration for its capital.</p>
<h2 id="memories">Memories</h2>
<p>I met a Spanish man on a hiking trail once and tried to tell him how nice Barcelona is. It turned out he did not want Catalonia to become independent, so he didn&rsquo;t want to talk to me, and he definitely didn&rsquo;t want to see my photos.</p>
<p>Thanks for the IndieWeb Carnival prompt <a href="https://jamesg.blog">James</a>
. It gave me an excuse to put this on my blog and add support for photos and videos.</p>
<p>And it was good to reminisce.</p>]]></content:encoded><source:markdown>
This is my entry for this month&#39;s IndieWeb Carnival: &lt;a class=&#34;u-in-reply-to&#34; href=&#34;https://jamesg.blog/2026/03/01/indieweb-carnival-museum-memories/&#34;&gt;Museum memories&lt;/a&gt;.

I have not been to many museums or galleries. I am not a cultured person.

My favourite museum is the city of Barcelona.

&lt;!--more--&gt;

## Museums

I grew up in Birmingham, UK, and I have been to a few museums there. We went on a school trip to the [Science Museum](https://www.birminghammuseums.org.uk/thinktank) and I&#39;m pretty sure I&#39;ve been down to London to go to the Natural History Museum.

They&#39;re all great.

My favourite museum trips were all in Barcelona, and there must be something to that city because out of the three entries to this month&#39;s carnival I&#39;ve read there&#39;s already [one about a museum there](https://jeffbridgforth.com/two-museum-memories/).

## Barcelona

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/barcelona.jpg&#34; alt=&#34;Barcelona&#39;s Gothic Quarter with ornate balconies and Sagrada Família visible in the distance&#34; class=&#34;w-full h-auto rounded-lg&#34; width=&#34;1200&#34; height=&#34;800&#34; figcaption=&#34;not my photo&#34; &gt;}}

I first went in 2017, staying for a week in a hotel in Eixample, a giant extension to the city that was started in the 1860s.

The Old Quarter, the historic core, dates back over 2,000 years. It was founded by the Romans as Barcino and some of their walls are still standing.

The development of other parts of the city, like Barri Gòtic and El Born, happened during the medieval period and were built right on top of what came before.

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/old_wall.jpeg&#34; alt=&#34;Ancient stone foundations and walls preserved beneath buildings in Barcelona&#34; class=&#34;rounded-lg&#34; width=&#34;1200&#34; height=&#34;1600&#34; &gt;}}

It&#39;s chaotic and organic.

### Eixample

Eixample, known in English as &#34;extension&#34;, is the complete opposite. It was designed as a uniform grid layout, and turned out to be very forward-thinking design.

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/eixample.jpg&#34; alt=&#34;Aerial view of Barcelona&#39;s Eixample district showing its distinctive grid layout with octagonal blocks&#34; class=&#34;w-full h-auto rounded-lg&#34; width=&#34;1200&#34; height=&#34;827&#34; figcaption=&#34;also not my photo&#34; &gt;}}

The octagonal blocks give better visibility at intersections and make turning easier for vehicles, even ones we use now, which didn&#39;t exist when the design was first conceived.

### Museums

I mention all of this because the city itself is a giant museum. You can step back in time, in design, and urban planning, and wander through the narrow cobbled streets of the Old Quarter, then walk ten minutes to Eixample to see an extension, started two hundred years ago, that&#39;s still celebrated for how modern it is.

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/museum.jpeg&#34; alt=&#34;Museum&#34; class=&#34;rounded-lg&#34; width=&#34;1200&#34; height=&#34;900&#34; figcaption=&#34;Castle of the Three Dragons&#34; &gt;}}

Barcelona has free museum visits on Sundays (in most city-run museums), with free entry from 3pm onwards and all day on the first Sunday of the month.

I made a second trip later in 2017 for a month, and ended up staying six weeks. I visited most of [the free museums](https://www.barcelonaturisme.com/wv3/en/enjoy/25/a-zero-cost-cultural-afternoon.html) whilst I was there,

as well as Castell de Montjuïc, the fort on the hill.

{{&lt; image_grid &gt;}}
{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/fort_garden.jpeg&#34; alt=&#34;Gardens and stone walls at Castell de Montjuïc overlooking Barcelona&#34; class=&#34;rounded-lg&#34; width=&#34;1200&#34; height=&#34;900&#34; &gt;}}

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/fort_entrance.jpeg&#34; alt=&#34;Entrance to Castell de Montjuïc with stone walls and walkway&#34; class=&#34;rounded-lg&#34; width=&#34;1200&#34; height=&#34;900&#34; &gt;}}
{{&lt; /image_grid &gt;}}

It was interesting to see how it was being used.

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/fort_inside.jpeg&#34; alt=&#34;Interior courtyard of Castell de Montjuïc with white flags showing messages of peace&#34; class=&#34;not-prose wide-photo rounded-lg&#34; width=&#34;1200&#34; height=&#34;675&#34; figcaption=&#34;&lt;blockquote&gt;Do economic and military interests help educate for peace?&lt;/blockquote&gt;&#34; &gt;}}

Barcelona does a really good job of preserving layers of its past, from Castell de Montjuïc, to Roman walls in the Gothic Quarter and entire neighbourhoods from the 1700s, like those uncovered in El Born.

These are curated spaces that preserve and display the history of the city itself.

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/old_walls.jpeg&#34; alt=&#34;Preserved walls from the 18th century displayed under a covered walkway&#34; class=&#34;rounded-lg&#34; width=&#34;1200&#34; height=&#34;1600&#34; figcaption=&#34;El Born Centre de Cultura i Memòria&#34; &gt;}}

I don&#39;t have a favourite museum in Barcelona. Barcelona is my favourite museum.

### Galleries

It&#39;s also a giant street art gallery.

The residents communicate with posters, stickers and slogans:

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/street_art.jpeg&#34; alt=&#34;Street posters and stickers covering a wall&#34; class=&#34;rounded-lg&#34; width=&#34;1200&#34; height=&#34;1600&#34; &gt;}}

To deter graffiti many property owners commission work for their shutters:

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/painted_door.jpeg&#34; alt=&#34;Colourful mural painted on a metal shop shutter&#34; class=&#34;col-span-2 w-full h-auto rounded-lg&#34; width=&#34;1200&#34; height=&#34;900&#34; &gt;}}

{{&lt; image_grid &gt;}}
{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/painted_door_bird.jpeg&#34; alt=&#34;Street art mural of a bird painted on a shop shutter&#34; class=&#34;w-full h-auto rounded-lg&#34; width=&#34;1200&#34; height=&#34;1600&#34; &gt;}}

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/painted_door_woman.jpeg&#34; alt=&#34;Street art mural of a woman painted on a shop shutter&#34; class=&#34;w-full h-auto rounded-lg&#34; width=&#34;1200&#34; height=&#34;1600&#34; &gt;}}
{{&lt; /image_grid &gt;}}

which is respected and left untouched.

The streets become a gallery, with guides offering tours of their own curated favourites.

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/gato.jpeg&#34; alt=&#34;Street art of 2 cat heads formed into one&#34; class=&#34;w-full h-auto rounded-lg&#34; width=&#34;1200&#34; height=&#34;900&#34; &gt;}}

## Architecture

Speaking of streets, some of the best views are of the architecture.

Whether it&#39;s an open public space…

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/parque.jpeg&#34; alt=&#34;Open public space around the Arc de Triomf&#34; class=&#34;rounded-lg&#34; width=&#34;1200&#34; height=&#34;960&#34; &gt;}}

or one of the many gold statues…

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/park.jpeg&#34; alt=&#34;Ornate golden statue in a Barcelona park surrounded by greenery&#34; class=&#34;rounded-lg&#34; width=&#34;1200&#34; height=&#34;960&#34; &gt;}}

or an ornate entrance to a church…

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/sagrada_familia_entrance.jpeg&#34; alt=&#34;Highly detailed sculpted entrance of the Sagrada Família basilica&#34; class=&#34;rounded-lg&#34; width=&#34;1200&#34; height=&#34;900&#34; &gt;}}

it&#39;s hard not to admire the intricate details and extravagance of Barcelona.

A lot of that is the result of Antoni Gaudí, a Catalan architect and designer, whose designs stretched from parks and gardens:

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/fantasy_house.jpeg&#34; alt=&#34;Colourful Gaudí-designed house in Park Güell&#34; class=&#34;rounded-lg&#34; width=&#34;1200&#34; height=&#34;1200&#34; figcaption=&#34;Park Güell&#34; &gt;}}

to one of the most ambitious Catholic churches in the world, started 143 years ago and still under construction:

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/sagrada_familia.jpeg&#34; alt=&#34;The towering spires of the Sagrada Família basilica in Barcelona&#34; class=&#34;rounded-lg&#34; width=&#34;1200&#34; height=&#34;1600&#34; figcaption=&#34;Sagrada Família&#34; &gt;}}

## Culture

Perhaps the biggest highlight of my first trip was its timing. I&#39;d accidentally arrived during La Mercè, Barcelona&#39;s annual festival that&#39;s been going on since 1871.

Every corner of the city was filled with art and music…

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/music_show.jpeg&#34; alt=&#34;Outdoor music performance during the La Mercè festival in Barcelona&#34; class=&#34;wide-photo rounded-lg&#34; width=&#34;1200&#34; height=&#34;960&#34; &gt;}}

as well as papier-mâché giants and castells (human pyramids, or &#34;castles&#34;)…

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/la_merce.jpeg&#34; alt=&#34;Human pyramid and giant papier-mâché figures during La Mercè festival&#34; class=&#34;wide-photo rounded-lg&#34; width=&#34;1200&#34; height=&#34;675&#34; &gt;}}

and the wildest fireworks show…

&lt;video controls playsinline preload=&#34;none&#34; poster=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/fireworks_poster.jpeg&#34; width=&#34;1200&#34; height=&#34;675&#34;&gt;
  &lt;source src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/fireworks.webm&#34; type=&#34;video/webm&#34;&gt;
  &lt;a href=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/fireworks.webm&#34;&gt;Open the fireworks video directly&lt;/a&gt;.
&lt;/video&gt;

I&#39;ve ever been to…

&lt;video controls playsinline preload=&#34;none&#34; poster=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/more_fireworks_poster.jpeg&#34; width=&#34;1200&#34; height=&#34;675&#34;&gt;
  &lt;source src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/more_fireworks.webm&#34; type=&#34;video/webm&#34;&gt;
  &lt;a href=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/more_fireworks.webm&#34;&gt;Open the second fireworks video directly&lt;/a&gt;.
&lt;/video&gt;

## 2017

What made 2017 special was the result of hundreds of years of history - it marked the first independence referendum hosted by the Catalan government.

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/democracy_month.jpeg&#34; alt=&#34;Poster reading &#39;Democracy Month&#39; displayed during Catalonia&#39;s independence movement&#34; class=&#34;rounded-lg&#34; width=&#34;1200&#34; height=&#34;900&#34; figcaption=&#34;democracy month&#34; &gt;}}

The city was covered in flags, and signs that said &#34;Si&#34; - which meant yes, if given the chance to vote.

{{&lt; image_grid &gt;}}
{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/want_to_vote.jpeg&#34; alt=&#34;People surround a poster that reads &#39;We want to vote&#39;&#34; class=&#34;rounded-lg&#34; width=&#34;900&#34; height=&#34;720&#34; &gt;}}
{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/wants_to_vote.jpeg&#34; alt=&#34;Poster on a balcony that reads &#39;Catalonia wants to vote&#39;&#34; class=&#34;rounded-lg&#34; width=&#34;1200&#34; height=&#34;960&#34; &gt;}}
{{&lt;/image_grid &gt;}}

There were plenty of Si supporters…

{{&lt; img src=&#34;https://paultibbetts.uk/media/2026/03/17/museum-memories/flags_parade.jpeg&#34; alt=&#34;Lots of Catalan flags&#34; class=&#34;rounded-lg&#34; width=&#34;1200&#34; height=&#34;675&#34; &gt;}}

They once interrupted a conversation I was having, and it was the highlight of my trip.

At, I think it was, 6pm, supporters of the vote leaned out their windows and started smashing pots and pans. It was a DIY siren to let everyone else know they existed, and that they wanted to vote.

It was deafeningly loud and an extremely effective way of communicating.

## History

I&#39;m no expert, but a brief history is:

- in the Middle Ages, Catalonia was part of the Crown of Aragon
- the War of Spanish Succession (1701-1714) saw Catalonia become part of Spain
- the dictatorship of Francisco Franco restricted Catalan language and culture from 1939 to 1975
- in 2006 Catalonia negotiated a new agreement with Spain
- Spain&#39;s conservative party opposed it and it went to court for 4 years
- in 2010 the court ruled that parts of the agreement should be annulled
- Catalans saw the agreement as settled and not to be changed
- this sparked a resurgence in the independence movement

which led to the referendum in 2017.

## The result

The Catalan government claimed that 90% of voters voted &#34;Yes&#34;, however they also recognised that it had only a 42% turnout.

Because of this, Spain ignored the result and then called it illegal anyway.

A month later Catalonia&#39;s parliament declared independence.

The next day Spain dissolved the Catalan government, arrested several of its leaders, and the regional president Carles Puigdemont fled to Belgium.

## Since then

Unfortunately, since then, the only notable things to have happened are trials and prison sentences followed by a new amnesty law that pardons some of those involved in the push for independence.

I wish this had a better ending, and that the Catalan people were given a legal vote to decide what they want to do. I don&#39;t know if independence is a good or bad thing for Catalonia, I&#39;m just a tourist with an admiration for its capital.

## Memories

I met a Spanish man on a hiking trail once and tried to tell him how nice Barcelona is. It turned out he did not want Catalonia to become independent, so he didn&#39;t want to talk to me, and he definitely didn&#39;t want to see my photos.

Thanks for the IndieWeb Carnival prompt [James](https://jamesg.blog). It gave me an excuse to put this on my blog and add support for photos and videos.

And it was good to reminisce.
</source:markdown></item><item>
      <title>Changes to the Site</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/14/changes-to-the-site/</link>
      <pubDate>Sat, 14 Mar 2026 23:07:25 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/14/changes-to-the-site/</guid>
      <category>meta</category>
      <description>I’ve made some recent changes to the site and have a few ideas for what comes next.
Changes:
Notes: shorter posts that don’t need titles Bookmarks and likes: a way to share links without writing full posts Replies: posts that are part of a conversation with another site Webmentions: now appear below posts Ideas:
serving media through a CDN dynamic image resizing with imgproxy publishing from my phone using micropub</description>
      <content:encoded><![CDATA[<p>I&rsquo;ve made some recent changes to the site and have a few ideas for what comes next.</p>
<p>Changes:</p>
<ul>
<li>Notes: shorter posts that don&rsquo;t need titles</li>
<li>Bookmarks and likes: a way to share links without writing full posts</li>
<li>Replies: posts that are part of a conversation with another site</li>
<li>Webmentions: now appear below posts</li>
</ul>
<p>Ideas:</p>
<ul>
<li>serving media through a CDN</li>
<li>dynamic image resizing with imgproxy</li>
<li>publishing from my phone using micropub</li>
</ul>
<h2 id="notes">Notes</h2>
<p>My site now has <a href="https://indieweb.org/note">notes</a>
. They are still posts but I treat them a bit differently.</p>
<p>Notes don&rsquo;t need a title, and they&rsquo;re usually shorter, but they&rsquo;re effectively the same as the articles I&rsquo;ve already been posting. The real difference will be what I end up classing as an article instead of a note.</p>
<p>I added them because I wanted to post moments in time without needing to write several paragraphs like I would in an article. This post was nearly a note, until I realised it was more than just recent changes and that I could write about future ones as well.</p>
<p>I think I will probably end up writing less articles now I have notes but when I do write an article it should mean that it&rsquo;s taken more effort to write, and is therefore of higher quality, but I&rsquo;m not guaranteeing anything.</p>
<h2 id="bookmarks">Bookmarks</h2>
<p>I&rsquo;ve also added <a href="https://indieweb.org/bookmark">bookmarks</a>
, which I&rsquo;m going to use to share links to interesting things around the web.</p>
<p>These are notes with a <code>bookmark_of</code> field set to the URL I&rsquo;ve bookmarked. They are then displayed a little differently, with the link going to the bookmarked site instead of my own post.</p>
<pre><code class="language-html">&lt;a class=&#34;u-bookmark-of&#34; href=&#34;https://example.com/post&#34;&gt;Example&lt;/a&gt;</code></pre>
<p>Using a different post kind for this makes it easier for me to share links, since I don&rsquo;t need to write an article talking about it, or even a note saying what I like about it. I can just share the link.</p>
<h2 id="likes">Likes</h2>
<p>Another type of link I&rsquo;m going to share is a <a href="https://indieweb.org/like">like</a>
, which is both a reaction and me sharing something. I don&rsquo;t know if there&rsquo;s going to be a system for deciding whether something is a bookmark or a like so I will make it up as I go along.</p>
<pre><code class="language-html">&lt;a class=&#34;u-like-of&#34; href=&#34;https://example.com/post&#34;&gt;I liked this&lt;/a&gt;</code></pre>
<p>Because I don&rsquo;t know what the difference will end up being, and they&rsquo;re both just a link to something, I have also added a <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/links">/links</a>
 feed which includes both bookmarks and likes.</p>
<p>This will be my own curated version of the internet I want others to know about.</p>
<h2 id="webmentions">Webmentions</h2>
<p>Speaking of likes, I got my first webmention like! Thank you <a href="https://artlung.com/">Joe</a>
!</p>
<p>I was checking Google Search Console and found a page showing me what sites linked to mine when I saw Joe had <a href="https://artlung.com/likes/2f08886085fe293d93bce727c2f75051">liked one of my posts</a>
.</p>
<p>I run my own site because I enjoy doing so, but that interaction made it all seem worth it, and I want to post likes from my own site to share that with others going forward.</p>
<h3 id="webmentionio">webmention.io</h3>
<p>Joe was able to send me a like because I signed up to <a href="https://webmention.io">webmention.io</a>
, which now collects my <a href="https://indieweb.org/webmention/">webmentions</a>
 for me.</p>
<p>It also provides a feed showing all the webmentions my site has received, which I&rsquo;ve added to my reader so I&rsquo;m notified of new ones.</p>
<p>To display the webmentions on my site I&rsquo;m using <a href="https://github.com/PlaidWeb/webmention.js/">webmention.js</a>
 to show them below the post they are mentioning.</p>
<h2 id="replies">Replies</h2>
<p>The other post type I&rsquo;ve added recently is a <a href="https://indieweb.org/reply/">reply</a>
.</p>
<p>These are notes that are in response to someone else&rsquo;s post. I will use webmentions to tell them that I&rsquo;ve written it, after which they could display my reply as a comment.</p>
<p>This now means that to write about a link I could:</p>
<ul>
<li>write an article about it</li>
<li>share it as a bookmark</li>
<li>share it as a like</li>
<li>write a note about it</li>
<li>reply to it directly</li>
</ul>
<p>So to try and make a rule for them: replies are notes that are part of a conversation.</p>
<p>Where it gets confusing is that any type of post can also be a reply. I could publish a post as an article, and then include a line like</p>
<pre><code class="language-html">my entry for
&lt;a class=&#34;u-in-reply-to&#34; href=&#34;https://carnival.host/prompt.html&#34;&gt;this month&#39;s IndieWeb Carnival&lt;/a&gt;</code></pre>
<p>and the post would be both an article and a reply.</p>
<p>If the site I&rsquo;m replying to displayed my article in full, it might be as long as their original post. Because of that they might show only an excerpt, or just link to it instead.</p>
<p>So, for long articles that are technically also replies I will have to make sure I write a shorter summary that works well as a comment, which could be displayed in full.</p>
<p>The replies I write using the reply post type will be written as I want them to appear in the conversation.</p>
<h2 id="planned-changes">Planned changes</h2>
<p>My entry for this month&rsquo;s <a href="https://indieweb.org/IndieWeb_Carnival/">IndieWeb Carnival</a>
 is taking longer than I thought because it&rsquo;s turned into a holiday photo dump.</p>
<p>This has had me questioning my setup, which is annoying because I thought it would last a bit longer before it needed changing.</p>
<img src="whiteboard.png" title="whiteboard of ideas for the site">
<p>I have not even finished my post about <a href="https://infra.paultibbetts.uk/">infra.paultibbetts.uk</a>
, which documents my infrastructure, and I am already thinking of changing it.</p>
<p>I do enjoy working on my site, maybe even more than actually using it, but I have other things I should be working on.</p>
<p>I don&rsquo;t know when the following will get done.</p>
<h2 id="media">Media</h2>
<p>My draft Carnival post was starting to include photos from my phone, which were about 7 MB each. I hadn&rsquo;t thought about photos before and didn&rsquo;t have a process for them.</p>
<p>My site uses git for version control, which is great for text like code and content but it starts to get bloated when you fill it with 7 MB photos. I started thinking up a better system for this, which is coming up next, until I realised I could optimise my images before saving them in git.</p>
<p>The solution for now is to run a script that replaces the original image with a compressed version. The 7 MB photos shrink down to ~200 kB. I am still bloating my repo with images, but at a much more tolerable size.</p>
<h2 id="cdn">CDN</h2>
<p>A better solution is to store my photos separately, which lets me keep the repo slim by tracking only text files.</p>
<p>I could use <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/19/moved-my-website-from-github-pages-to-a-raspberry-pi/">my Raspberry Pi</a>
 to host the photos, but it uses Network File Storage which is a bit slow and has only 50 GB of storage.</p>
<p>Instead I&rsquo;m thinking of using a storage bucket, which could grow to any size, and then using a Content Delivery Network to serve the assets.</p>
<h3 id="edit">Edit</h3>
<p>I needed a temporary solution, so in <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/17/added-support-for-photos-and-videos/">Added support for photos and videos</a>
 I explain how I&rsquo;ve set up my Raspberry Pi to host my media files until I get a storage bucket and CDN sorted out.</p>
<h2 id="dynamic-image-resizing">Dynamic Image Resizing</h2>
<p>I might also use something like <a href="https://imgproxy.net/">imgproxy</a>
 to dynamically resize images.</p>
<p>When a user requests a smaller version of a photo, imgproxy would generate it on demand. That resized image would then be cached by the CDN, so future visitors can receive the optimised version immediately.</p>
<p>This <em>might</em> mean I can stop compressing photos manually and just use the originals.</p>
<h2 id="micropub">Micropub</h2>
<p>The final change to my site, which will really elevate it from a simple blog to a personal publishing system, is using <a href="https://indieweb.org/micropub">micropub</a>
 to let me post from my phone.</p>
<p>This would need hosting and hooking up to my git repo to manage my content. I would have the same problem as I do right now if I used it for photos, so I will need to wait until I have the storage bucket and CDN sorted before tackling this.</p>
<p>I&rsquo;m considering using <a href="https://getindiekit.com/">Indiekit</a>
 as my micropub server, as it works with Hugo (which powers my site), and can also <a href="https://indieweb.org/cross-posting">syndicate</a>
 my posts out to social networks. It doesn&rsquo;t look like it has a Micro.blog plugin yet, so I&rsquo;d need to write one before I can selectively choose which posts to syndicate there.</p>
<h2 id="microblog">Micro.blog</h2>
<p>Which leaves me with a site that can do what I currently use my <a href="https://micro.blog">Micro.blog</a>
 <a href="https://micro.paultibbetts.uk/">microblog</a>
 for. I want to stay as part of the Micro.blog community, and will continue using their apps, but I can see myself migrating the hosting of my content to my own site in the future.</p>
<p>I could merge my Micro.blog posts into the notes on my own site and then syndicate future posts to the Micro.blog timeline, and for Micro.blog users nothing will seem to have changed, only where my content is hosted.</p>
<p>My feed might even get better, as I could filter out posts that I know the Micro.blog community wouldn&rsquo;t be interested in.</p>
<p>This isn&rsquo;t a remark against Micro.blog by the way - I think it&rsquo;s an excellent service - I just believe that I was destined to outgrow it, since I always intended on working on my own site. I think of it more as graduating as a socially-capable indie website, and because Micro.blog is part of the indieweb I can stay part of its community.</p>
<p>I would be missing out on lots of features that Micro.blog has that I don&rsquo;t yet support, so it wouldn&rsquo;t be a seamless transition, but after implementing all the above ideas my site should be capable of hosting all of my post kinds in one place.</p>]]></content:encoded><source:markdown>
I&#39;ve made some recent changes to the site and have a few ideas for what comes next.

Changes:

- Notes: shorter posts that don&#39;t need titles
- Bookmarks and likes: a way to share links without writing full posts
- Replies: posts that are part of a conversation with another site
- Webmentions: now appear below posts

Ideas:

- serving media through a CDN
- dynamic image resizing with imgproxy
- publishing from my phone using micropub

&lt;!--more--&gt;

## Notes

My site now has [notes](https://indieweb.org/note). They are still posts but I treat them a bit differently.

Notes don&#39;t need a title, and they&#39;re usually shorter, but they&#39;re effectively the same as the articles I&#39;ve already been posting. The real difference will be what I end up classing as an article instead of a note.

I added them because I wanted to post moments in time without needing to write several paragraphs like I would in an article. This post was nearly a note, until I realised it was more than just recent changes and that I could write about future ones as well.

I think I will probably end up writing less articles now I have notes but when I do write an article it should mean that it&#39;s taken more effort to write, and is therefore of higher quality, but I&#39;m not guaranteeing anything.

## Bookmarks

I&#39;ve also added [bookmarks](https://indieweb.org/bookmark), which I&#39;m going to use to share links to interesting things around the web.

These are notes with a `bookmark_of` field set to the URL I&#39;ve bookmarked. They are then displayed a little differently, with the link going to the bookmarked site instead of my own post.

```html
&lt;a class=&#34;u-bookmark-of&#34; href=&#34;https://example.com/post&#34;&gt;Example&lt;/a&gt;
```

Using a different post kind for this makes it easier for me to share links, since I don&#39;t need to write an article talking about it, or even a note saying what I like about it. I can just share the link.

## Likes

Another type of link I&#39;m going to share is a [like](https://indieweb.org/like), which is both a reaction and me sharing something. I don&#39;t know if there&#39;s going to be a system for deciding whether something is a bookmark or a like so I will make it up as I go along.

```html
&lt;a class=&#34;u-like-of&#34; href=&#34;https://example.com/post&#34;&gt;I liked this&lt;/a&gt;
```

Because I don&#39;t know what the difference will end up being, and they&#39;re both just a link to something, I have also added a [/links](/links) feed which includes both bookmarks and likes.

This will be my own curated version of the internet I want others to know about.

## Webmentions

Speaking of likes, I got my first webmention like! Thank you [Joe](https://artlung.com/)!

I was checking Google Search Console and found a page showing me what sites linked to mine when I saw Joe had [liked one of my posts](https://artlung.com/likes/2f08886085fe293d93bce727c2f75051).

I run my own site because I enjoy doing so, but that interaction made it all seem worth it, and I want to post likes from my own site to share that with others going forward.

### webmention.io

Joe was able to send me a like because I signed up to [webmention.io](https://webmention.io), which now collects my [webmentions](https://indieweb.org/webmention/) for me.

It also provides a feed showing all the webmentions my site has received, which I&#39;ve added to my reader so I&#39;m notified of new ones.

To display the webmentions on my site I&#39;m using [webmention.js](https://github.com/PlaidWeb/webmention.js/) to show them below the post they are mentioning.

## Replies

The other post type I&#39;ve added recently is a [reply](https://indieweb.org/reply/).

These are notes that are in response to someone else&#39;s post. I will use webmentions to tell them that I&#39;ve written it, after which they could display my reply as a comment.

This now means that to write about a link I could:

- write an article about it
- share it as a bookmark
- share it as a like
- write a note about it
- reply to it directly

So to try and make a rule for them: replies are notes that are part of a conversation.

Where it gets confusing is that any type of post can also be a reply. I could publish a post as an article, and then include a line like

```html
my entry for
&lt;a class=&#34;u-in-reply-to&#34; href=&#34;https://carnival.host/prompt.html&#34;&gt;this month&#39;s IndieWeb Carnival&lt;/a&gt;
```

and the post would be both an article and a reply.

If the site I&#39;m replying to displayed my article in full, it might be as long as their original post. Because of that they might show only an excerpt, or just link to it instead.

So, for long articles that are technically also replies I will have to make sure I write a shorter summary that works well as a comment, which could be displayed in full.

The replies I write using the reply post type will be written as I want them to appear in the conversation.

## Planned changes

My entry for this month&#39;s [IndieWeb Carnival](https://indieweb.org/IndieWeb_Carnival/) is taking longer than I thought because it&#39;s turned into a holiday photo dump.

This has had me questioning my setup, which is annoying because I thought it would last a bit longer before it needed changing.

&lt;img src=&#34;whiteboard.png&#34; title=&#34;whiteboard of ideas for the site&#34;&gt;

I have not even finished my post about [infra.paultibbetts.uk](https://infra.paultibbetts.uk/), which documents my infrastructure, and I am already thinking of changing it.

I do enjoy working on my site, maybe even more than actually using it, but I have other things I should be working on.

I don&#39;t know when the following will get done.

## Media

My draft Carnival post was starting to include photos from my phone, which were about 7 MB each. I hadn&#39;t thought about photos before and didn&#39;t have a process for them.

My site uses git for version control, which is great for text like code and content but it starts to get bloated when you fill it with 7 MB photos. I started thinking up a better system for this, which is coming up next, until I realised I could optimise my images before saving them in git.

The solution for now is to run a script that replaces the original image with a compressed version. The 7 MB photos shrink down to ~200 kB. I am still bloating my repo with images, but at a much more tolerable size.

## CDN

A better solution is to store my photos separately, which lets me keep the repo slim by tracking only text files.

I could use [my Raspberry Pi](/2026/02/19/moved-my-website-from-github-pages-to-a-raspberry-pi/) to host the photos, but it uses Network File Storage which is a bit slow and has only 50 GB of storage.

Instead I&#39;m thinking of using a storage bucket, which could grow to any size, and then using a Content Delivery Network to serve the assets.

### Edit

I needed a temporary solution, so in [Added support for photos and videos](/2026/03/17/added-support-for-photos-and-videos/) I explain how I&#39;ve set up my Raspberry Pi to host my media files until I get a storage bucket and CDN sorted out.

## Dynamic Image Resizing

I might also use something like [imgproxy](https://imgproxy.net/) to dynamically resize images.

When a user requests a smaller version of a photo, imgproxy would generate it on demand. That resized image would then be cached by the CDN, so future visitors can receive the optimised version immediately.

This _might_ mean I can stop compressing photos manually and just use the originals.

## Micropub

The final change to my site, which will really elevate it from a simple blog to a personal publishing system, is using [micropub](https://indieweb.org/micropub) to let me post from my phone.

This would need hosting and hooking up to my git repo to manage my content. I would have the same problem as I do right now if I used it for photos, so I will need to wait until I have the storage bucket and CDN sorted before tackling this.

I&#39;m considering using [Indiekit](https://getindiekit.com/) as my micropub server, as it works with Hugo (which powers my site), and can also [syndicate](https://indieweb.org/cross-posting) my posts out to social networks. It doesn&#39;t look like it has a Micro.blog plugin yet, so I&#39;d need to write one before I can selectively choose which posts to syndicate there.

## Micro.blog

Which leaves me with a site that can do what I currently use my [Micro.blog](https://micro.blog) [microblog](https://micro.paultibbetts.uk/) for. I want to stay as part of the Micro.blog community, and will continue using their apps, but I can see myself migrating the hosting of my content to my own site in the future.

I could merge my Micro.blog posts into the notes on my own site and then syndicate future posts to the Micro.blog timeline, and for Micro.blog users nothing will seem to have changed, only where my content is hosted.

My feed might even get better, as I could filter out posts that I know the Micro.blog community wouldn&#39;t be interested in.

This isn&#39;t a remark against Micro.blog by the way - I think it&#39;s an excellent service - I just believe that I was destined to outgrow it, since I always intended on working on my own site. I think of it more as graduating as a socially-capable indie website, and because Micro.blog is part of the indieweb I can stay part of its community.

I would be missing out on lots of features that Micro.blog has that I don&#39;t yet support, so it wouldn&#39;t be a seamless transition, but after implementing all the above ideas my site should be capable of hosting all of my post kinds in one place.
</source:markdown></item><item>
      <title>Liked: IndieWeb.org Principles</title>
      <link>https://indieweb.org/principles</link>
      <pubDate>Wed, 04 Mar 2026 17:00:44 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/04/2026-03-04-principles-indieweb/</guid>
      <category>IndieWeb</category>
      <description></description>
      <content:encoded><![CDATA[]]></content:encoded></item><item>
      <title>Bookmarked: A Website to End All Websites</title>
      <link>https://henry.codes/writing/a-website-to-destroy-all-websites/</link>
      <pubDate>Wed, 04 Mar 2026 14:19:08 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/04/2026-03-04-a-website-to-end-all-websites-henry-from-online/</guid>
      <category>IndieWeb</category>
      <description>[…] the path back to feeling like you have some control is to un-spin yourself from the Five Apps of the Apocalypse and reclaim the Internet as a set of tools you use to build something you can own &amp;amp; be proud of</description>
      <content:encoded><![CDATA[<blockquote>
<p>[&hellip;] the path back to feeling like you have some control is to un-spin yourself from the Five Apps of the Apocalypse and reclaim the Internet as a set of tools you use to build something you can own &amp; be proud of</p>
</blockquote>]]></content:encoded><source:markdown>
&gt; [...] the path back to feeling like you have some control is to un-spin yourself from the Five Apps of the Apocalypse and reclaim the Internet as a set of tools you use to build something you can own &amp; be proud of
</source:markdown></item><item>
      <title>I Made a Terraform Provider for Mythic Beasts</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/03/i-made-a-terraform-provider-for-mythic-beasts/</link>
      <pubDate>Tue, 03 Mar 2026 12:00:17 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/03/i-made-a-terraform-provider-for-mythic-beasts/</guid>
      <category>Terraform</category>
      <category>Mythic Beasts</category>
      <category>go</category>
      <category>code</category>
      <description>paultibbetts/terraform-provider-mythicbeasts is a Terraform provider that lets you declare infrastructure from the company Mythic Beasts as code.
I use it to provision the infrastructure for my personal website.
This is why I built it, what it does, and how to use it.</description>
      <content:encoded><![CDATA[<p><a href="https://github.com/paultibbetts/terraform-provider-mythicbeasts">paultibbetts/terraform-provider-mythicbeasts</a>
 is a Terraform provider that lets you declare infrastructure from the company <a href="https://www.mythic-beasts.com/">Mythic Beasts</a>
 as code.</p>
<p>I use it to provision the infrastructure for my personal website.</p>
<p>This is why I built it, what it does, and how to use it.</p>
<h2 id="why-i-built-it">Why I built it</h2>
<p>I prefer writing down my infrastructure as code. It&rsquo;s something I&rsquo;ve been doing for years now, and I don&rsquo;t think I could go back to doing things manually, no matter how small or simple the setup.</p>
<p>I&rsquo;d decided to use a Pi from Mythic Beasts to host my site and saw that they had APIs available, but no Terraform provider.</p>
<p>Terraform is a tool I&rsquo;ve been using for infrastructure for a while now, and I wanted to use it with Mythic Beasts because:</p>
<ul>
<li>I wanted a fully reproducible setup</li>
<li>I didn&rsquo;t want to click around the control panel</li>
<li>their Pis are IPv6-only, so I&rsquo;d need to also set up the proxy</li>
<li>
<ul>
<li>I wanted to declare the Pi and the endpoints for the proxy in the same place</li>
</ul>
</li>
</ul>
<p>Plus, I&rsquo;d never written a Terraform provider before, and I wanted to see what it was like.</p>
<h2 id="what-it-does">What it does</h2>
<p>Terraform is a tool for writing your infrastructure as code, and a provider is a plugin that lets it work with different infrastructure providers.</p>
<p>For example you can write:</p>
<pre><code class="language-hcl">terraform {
  required_version = &#34;&gt;= 1.11.0&#34;

  required_providers {
    mythicbeasts = {
      source = &#34;paultibbetts/mythicbeasts&#34;
    }
  }
}

resource &#34;mythicbeasts_pi&#34; &#34;pi&#34; {
	identifier   = &#34;web&#34;
	disk_size    = 10
	model        = 4
	memory       = 4096
	os_image     = &#34;rpi-bookworm-arm64&#34;
    ssh_key      = &#34;ssh-ed25519 ...&#34;
    wait_for_dns = true
}</code></pre>
<p>then run <code>terraform apply</code>, and you would get a Raspberry Pi 4 from Mythic Beasts.</p>
<h3 id="resources">Resources</h3>
<p>The provider uses the Mythic Beasts APIs to configure the following resources:</p>
<ul>
<li>VPS</li>
<li>Raspberry Pi</li>
<li>Proxy endpoint</li>
</ul>
<p>The Raspberry Pis from Mythic Beasts are on an IPv6-only network, so the Proxy endpoints are to allow users trying to access from an IPv4-only network.</p>
<h3 id="data-sources">Data sources</h3>
<p>Some of the settings for the resources require specific values, like the <code>os_image</code> to use, and data sources make it easy to get this info from the API.</p>
<p>For example, the <code>mythicbeasts_pi_operating_systems</code> data source can be used to get all valid <code>os_image</code> values for a Raspberry Pi 4:</p>
<pre><code class="language-hcl">data &#34;mythicbeasts_pi_operating_systems&#34; &#34;four&#34; {
  model = 4
}</code></pre>
<p>where you would inspect the data to choose the right value to use.</p>
<p>Alternatively you could use Terraform to find the right value for you, for example to use &ldquo;Bookworm&rdquo; for &ldquo;Arm64&rdquo;:</p>
<pre><code class="language-hcl">data &#34;mythicbeasts_pi_operating_systems&#34; &#34;four&#34; {
  model = 4
}

locals {
  bookworm_arm64 = one([
    for image in data.mythicbeasts_pi_operating_systems.four.images :
    image.id
    if strcontains(image.id, &#34;bookworm&#34;) &amp;&amp;
    strcontains(image.id, &#34;arm64&#34;)
  ])
}

resource &#34;mythicbeasts_pi&#34; &#34;bookworm&#34; {
  model    = 4
  os_image = local.bookworm_arm64
  ...
}</code></pre>
<p>where Terraform will find the image with both &ldquo;bookworm&rdquo; and &ldquo;arm64&rdquo; in the ID.</p>
<h3 id="attributes">Attributes</h3>
<p>Using Terraform for your infrastructure not only lets you write it down as code, with all the <a href="https://infra.paultibbetts.uk/decisions/why-iac/">benefits that brings</a>
, but you can use the attributes of one thing as inputs for another.</p>
<p>For example: the Pis from Mythic Beasts are on an IPv6-only network and need endpoints set up on the proxy to let users access them from IPv4-only networks.</p>
<p>To do this you can get the attribute from a Pi, like the IP address, and then use that as the input for the proxy endpoint resource:</p>
<pre><code class="language-hcl">locals {
  domain = &#34;example.com&#34;
  subdomains = {
    apex  = &#34;@&#34;
    www   = &#34;www&#34;
  }
}

resource &#34;mythicbeasts_pi&#34; &#34;web&#34; {
	identifier   = &#34;web&#34;
	disk_size    = 10
	model        = 4
	memory       = 4096
	os_image     = &#34;rpi-bookworm-arm64&#34;
    ssh_key      = &#34;ssh-ed25519 ...&#34;
    wait_for_dns = true
}

resource &#34;mythicbeasts_proxy_endpoint&#34; &#34;endpoint&#34; {
  for_each = local.subdomains

  domain         = local.domain
  hostname       = each.value
  address        = mythicbeasts_pi.web.ip
  site           = &#34;all&#34;
  proxy_protocol = true
}</code></pre>
<h2 id="how-to-use-it">How to use it</h2>
<p>The provider is available on <a href="https://registry.terraform.io/providers/paultibbetts/mythicbeasts/">the Terraform registry</a>
.</p>
<h3 id="authentication">Authentication</h3>
<p>The Mythic Beasts APIs require an <a href="https://www.mythic-beasts.com/customer/api-users">API key</a>
 for authentication.</p>
<p>When creating the key you must add permissions to work with the APIs you wish to use, such as:</p>
<ul>
<li>&ldquo;Virtual Server Provisioning&rdquo; for VPS</li>
<li>&ldquo;Raspberry Pi provisioning&rdquo; for Pis</li>
<li>&ldquo;IPv4 to IPv6 Proxy API&rdquo; for Proxy endpoints</li>
</ul>
<h3 id="proxy-endpoint-domain-management">Proxy endpoint domain management</h3>
<p>The domain used for proxy endpoints must be registered with the Mythic Beasts control panel.</p>
<p>This can be done by registering the domain using their <a href="https://www.mythic-beasts.com/customer/domains">domain management</a>
 or by <a href="https://www.mythic-beasts.com/customer/3rdpartydomain">adding it as a 3rd party domain</a>
.</p>
<h3 id="installation">Installation</h3>
<pre><code class="language-hcl">terraform {
  required_version = &#34;&gt;= 1.11.0&#34;

  required_providers {
    mythicbeasts = {
      source = &#34;paultibbetts/mythicbeasts&#34;
    }
  }
}

provider &#34;mythicbeasts&#34; {
  keyid  = &#34;your-keyid&#34;
  secret = &#34;your-secret&#34;
}</code></pre>
<p>Alternatively, credentials can be supplied via environment variables:</p>
<ul>
<li><code>MYTHICBEASTS_KEYID</code></li>
<li><code>MYTHICBEASTS_SECRET</code></li>
</ul>
<h2 id="examples">Examples</h2>
<p>Examples of each resource and data source can be found at <a href="https://github.com/paultibbetts/terraform-provider-mythicbeasts/blob/main/examples">/examples</a>
.</p>
<p>These are also used to generate the <a href="https://registry.terraform.io/providers/paultibbetts/mythicbeasts/latest/docs">documentation hosted by the Terraform registry</a>
.</p>
<h2 id="how-it-works">How it works</h2>
<p>The provider uses the newer <a href="https://github.com/hashicorp/terraform-plugin-framework">Terraform Plugin Framework</a>
.</p>
<p>For it to work with the Mythic Beasts APIs I had to create a Go client, which can be found <a href="https://github.com/paultibbetts/mythicbeasts-client-go/">on GitHub</a>
. The client handles the APIs and means that the provider only needs to focus on Terraform itself.</p>
<p>The Mythic Beasts VPS API needs values that are only ever used when creating a VPS and so the provider uses the new <a href="https://developer.hashicorp.com/terraform/language/manage-sensitive-data/write-only">write-only</a>
 arguments feature, which is only available in Terraform v1.11.0 and later. Write-only is normally used for sensitive values like passwords but the VPS API doesn&rsquo;t return every attribute so write-only is used for these values when creating the VPS and then they are forgotten.</p>
<h2 id="support">Support</h2>
<p>To make it clear, this is an unofficial provider that is not endorsed by Mythic Beasts.</p>
<p>It&rsquo;s currently at version <code>v0.1</code> and I wouldn&rsquo;t consider it stable until it hits <code>v1.0</code>.</p>
<p>Right now it supports the APIs I need it to. Further features will be added as needed.</p>
<h2 id="in-use">In use</h2>
<p>I use the provider to provision a Raspberry Pi 4 from Mythic Beasts and set up the proxy endpoints for my personal website.</p>
<p>The whole setup, with Terraform to provision the Pi and then Ansible to configure it, is documented at <a href="https://infra.paultibbetts.uk">infra.paultibbetts.uk</a>
.</p>
<h2 id="whats-next">What&rsquo;s next?</h2>
<p>The provider works with the VPS, Pi, and Proxy APIs, as these are what I currently use or have used in the past.</p>
<p>Mythic Beasts also have a <a href="https://www.mythic-beasts.com/support/api/dnsv2">DNS API</a>
 which lets you manage the DNS records for domains.</p>
<p>If I were to add this to the provider I could change the management of my domain from Cloudflare over to Mythic Beasts, which would improve my website&rsquo;s <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/19/moved-my-website-from-github-pages-to-a-raspberry-pi/">sovereignty</a>
.</p>]]></content:encoded><source:markdown>
[paultibbetts/terraform-provider-mythicbeasts](https://github.com/paultibbetts/terraform-provider-mythicbeasts) is a Terraform provider that lets you declare infrastructure from the company [Mythic Beasts](https://www.mythic-beasts.com/) as code.

I use it to provision the infrastructure for my personal website.

This is why I built it, what it does, and how to use it.

&lt;!--more--&gt;

## Why I built it

I prefer writing down my infrastructure as code. It&#39;s something I&#39;ve been doing for years now, and I don&#39;t think I could go back to doing things manually, no matter how small or simple the setup.

I&#39;d decided to use a Pi from Mythic Beasts to host my site and saw that they had APIs available, but no Terraform provider.

Terraform is a tool I&#39;ve been using for infrastructure for a while now, and I wanted to use it with Mythic Beasts because:

- I wanted a fully reproducible setup
- I didn&#39;t want to click around the control panel
- their Pis are IPv6-only, so I&#39;d need to also set up the proxy
- - I wanted to declare the Pi and the endpoints for the proxy in the same place

Plus, I&#39;d never written a Terraform provider before, and I wanted to see what it was like.

## What it does

Terraform is a tool for writing your infrastructure as code, and a provider is a plugin that lets it work with different infrastructure providers.

For example you can write:

```hcl
terraform {
  required_version = &#34;&gt;= 1.11.0&#34;

  required_providers {
    mythicbeasts = {
      source = &#34;paultibbetts/mythicbeasts&#34;
    }
  }
}

resource &#34;mythicbeasts_pi&#34; &#34;pi&#34; {
	identifier   = &#34;web&#34;
	disk_size    = 10
	model        = 4
	memory       = 4096
	os_image     = &#34;rpi-bookworm-arm64&#34;
    ssh_key      = &#34;ssh-ed25519 ...&#34;
    wait_for_dns = true
}
```

then run `terraform apply`, and you would get a Raspberry Pi 4 from Mythic Beasts.

### Resources

The provider uses the Mythic Beasts APIs to configure the following resources:

- VPS
- Raspberry Pi
- Proxy endpoint

The Raspberry Pis from Mythic Beasts are on an IPv6-only network, so the Proxy endpoints are to allow users trying to access from an IPv4-only network.

### Data sources

Some of the settings for the resources require specific values, like the `os_image` to use, and data sources make it easy to get this info from the API.

For example, the `mythicbeasts_pi_operating_systems` data source can be used to get all valid `os_image` values for a Raspberry Pi 4:

```hcl
data &#34;mythicbeasts_pi_operating_systems&#34; &#34;four&#34; {
  model = 4
}
```

where you would inspect the data to choose the right value to use.

Alternatively you could use Terraform to find the right value for you, for example to use &#34;Bookworm&#34; for &#34;Arm64&#34;:

```hcl
data &#34;mythicbeasts_pi_operating_systems&#34; &#34;four&#34; {
  model = 4
}

locals {
  bookworm_arm64 = one([
    for image in data.mythicbeasts_pi_operating_systems.four.images :
    image.id
    if strcontains(image.id, &#34;bookworm&#34;) &amp;&amp;
    strcontains(image.id, &#34;arm64&#34;)
  ])
}

resource &#34;mythicbeasts_pi&#34; &#34;bookworm&#34; {
  model    = 4
  os_image = local.bookworm_arm64
  ...
}
```

where Terraform will find the image with both &#34;bookworm&#34; and &#34;arm64&#34; in the ID.

### Attributes

Using Terraform for your infrastructure not only lets you write it down as code, with all the [benefits that brings](https://infra.paultibbetts.uk/decisions/why-iac/), but you can use the attributes of one thing as inputs for another.

For example: the Pis from Mythic Beasts are on an IPv6-only network and need endpoints set up on the proxy to let users access them from IPv4-only networks.

To do this you can get the attribute from a Pi, like the IP address, and then use that as the input for the proxy endpoint resource:

```hcl
locals {
  domain = &#34;example.com&#34;
  subdomains = {
    apex  = &#34;@&#34;
    www   = &#34;www&#34;
  }
}

resource &#34;mythicbeasts_pi&#34; &#34;web&#34; {
	identifier   = &#34;web&#34;
	disk_size    = 10
	model        = 4
	memory       = 4096
	os_image     = &#34;rpi-bookworm-arm64&#34;
    ssh_key      = &#34;ssh-ed25519 ...&#34;
    wait_for_dns = true
}

resource &#34;mythicbeasts_proxy_endpoint&#34; &#34;endpoint&#34; {
  for_each = local.subdomains

  domain         = local.domain
  hostname       = each.value
  address        = mythicbeasts_pi.web.ip
  site           = &#34;all&#34;
  proxy_protocol = true
}
```

## How to use it

The provider is available on [the Terraform registry](https://registry.terraform.io/providers/paultibbetts/mythicbeasts/).

### Authentication

The Mythic Beasts APIs require an [API key](https://www.mythic-beasts.com/customer/api-users) for authentication.

When creating the key you must add permissions to work with the APIs you wish to use, such as:

- &#34;Virtual Server Provisioning&#34; for VPS
- &#34;Raspberry Pi provisioning&#34; for Pis
- &#34;IPv4 to IPv6 Proxy API&#34; for Proxy endpoints

### Proxy endpoint domain management

The domain used for proxy endpoints must be registered with the Mythic Beasts control panel.

This can be done by registering the domain using their [domain management](https://www.mythic-beasts.com/customer/domains) or by [adding it as a 3rd party domain](https://www.mythic-beasts.com/customer/3rdpartydomain).

### Installation

```hcl
terraform {
  required_version = &#34;&gt;= 1.11.0&#34;

  required_providers {
    mythicbeasts = {
      source = &#34;paultibbetts/mythicbeasts&#34;
    }
  }
}

provider &#34;mythicbeasts&#34; {
  keyid  = &#34;your-keyid&#34;
  secret = &#34;your-secret&#34;
}
```

Alternatively, credentials can be supplied via environment variables:

- `MYTHICBEASTS_KEYID`
- `MYTHICBEASTS_SECRET`

## Examples

Examples of each resource and data source can be found at [/examples](https://github.com/paultibbetts/terraform-provider-mythicbeasts/blob/main/examples).

These are also used to generate the [documentation hosted by the Terraform registry](https://registry.terraform.io/providers/paultibbetts/mythicbeasts/latest/docs).

## How it works

The provider uses the newer [Terraform Plugin Framework](https://github.com/hashicorp/terraform-plugin-framework).

For it to work with the Mythic Beasts APIs I had to create a Go client, which can be found [on GitHub](https://github.com/paultibbetts/mythicbeasts-client-go/). The client handles the APIs and means that the provider only needs to focus on Terraform itself.

The Mythic Beasts VPS API needs values that are only ever used when creating a VPS and so the provider uses the new [write-only](https://developer.hashicorp.com/terraform/language/manage-sensitive-data/write-only) arguments feature, which is only available in Terraform v1.11.0 and later. Write-only is normally used for sensitive values like passwords but the VPS API doesn&#39;t return every attribute so write-only is used for these values when creating the VPS and then they are forgotten.

## Support

To make it clear, this is an unofficial provider that is not endorsed by Mythic Beasts.

It&#39;s currently at version `v0.1` and I wouldn&#39;t consider it stable until it hits `v1.0`.

Right now it supports the APIs I need it to. Further features will be added as needed.

## In use

I use the provider to provision a Raspberry Pi 4 from Mythic Beasts and set up the proxy endpoints for my personal website.

The whole setup, with Terraform to provision the Pi and then Ansible to configure it, is documented at [infra.paultibbetts.uk](https://infra.paultibbetts.uk).

## What&#39;s next?

The provider works with the VPS, Pi, and Proxy APIs, as these are what I currently use or have used in the past.

Mythic Beasts also have a [DNS API](https://www.mythic-beasts.com/support/api/dnsv2) which lets you manage the DNS records for domains.

If I were to add this to the provider I could change the management of my domain from Cloudflare over to Mythic Beasts, which would improve my website&#39;s [sovereignty](/2026/02/19/moved-my-website-from-github-pages-to-a-raspberry-pi/).
</source:markdown></item><item>
      <title>My RSS feeds now contain content:encoded and source:markdown. These are used by …</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/02/2026-03-02-133227-rss-feed-changes/</link>
      <pubDate>Mon, 02 Mar 2026 13:32:27 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/02/2026-03-02-133227-rss-feed-changes/</guid>
      <category>RSS</category>
      <description>My RSS feeds now contain content:encoded and source:markdown.
These are used by some feed readers to show the full content, which means my posts are now fully readable through RSS.</description>
      <content:encoded><![CDATA[<p>My RSS feeds now contain <code>content:encoded</code> and <code>source:markdown</code>.</p>
<p>These are used by some feed readers to show the full content, which means my posts are now fully readable through RSS.</p>]]></content:encoded><source:markdown>
My RSS feeds now contain `content:encoded` and `source:markdown`.

These are used by some feed readers to show the full content, which means my posts are now fully readable through RSS.
</source:markdown></item><item>
      <title>How to create an RSS Feed</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/01/how-to-create-an-rss-feed/</link>
      <pubDate>Sun, 01 Mar 2026 16:33:46 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/01/how-to-create-an-rss-feed/</guid>
      <category>IndieWeb</category>
      <category>RSS</category>
      <description>In my last post I recommended starting with RSS to make your site followable.
This is how to do that.</description>
      <content:encoded><![CDATA[<p>In my last post I recommended starting with RSS to make your site followable.</p>
<p>This is how to do that.</p>
<h2 id="you-might-already-have-one">You might already have one</h2>
<p>If you&rsquo;re using a static site generator like Hugo, or a CMS like WordPress, then you might already have an RSS feed.</p>
<p>If not, and you&rsquo;re writing your site manually, or just want to understand what&rsquo;s going on, here&rsquo;s what an RSS feed looks like.</p>
<h2 id="a-minimal-rss-feed">A minimal RSS feed</h2>
<p>At a minimum an RSS feed contains:</p>
<pre><code class="language-xml">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;
&lt;rss version=&#34;2.0&#34;&gt;
  &lt;channel&gt;
    &lt;title&gt;Example Blog&lt;/title&gt;
    &lt;link&gt;https://example.com/&lt;/link&gt;
    &lt;description&gt;Updates from my blog.&lt;/description&gt;

    &lt;item&gt;
      &lt;title&gt;Hello World&lt;/title&gt;
      &lt;link&gt;https://example.com/hello-world&lt;/link&gt;
      &lt;description&gt;My first post.&lt;/description&gt;
    &lt;/item&gt;

  &lt;/channel&gt;
&lt;/rss&gt;</code></pre>
<h3 id="required">Required</h3>
<pre><code class="language-xml">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;</code></pre>
<p>This line declares the XML version of the file and the encoding that it uses. <code>UTF-8</code> supports text and emoji.</p>
<p>It&rsquo;s strongly recommended to include this line so the file is read correctly.</p>
<pre><code class="language-xml">&lt;rss version=&#34;2.0&#34;&gt;</code></pre>
<p>This is the start of the <code>&lt;rss&gt;</code> element. The <code>version=&quot;2.0&quot;</code> tells consumers that the feed conforms to the RSS 2.0 specification, the most widely supported version.</p>
<pre><code class="language-xml">&lt;channel&gt;
  &lt;title&gt;Example Blog&lt;/title&gt;
  &lt;link&gt;https://example.com/&lt;/link&gt;
  &lt;description&gt;Updates from my blog.&lt;/description&gt;</code></pre>
<p>The <code>&lt;channel&gt;</code> declares the feed itself, so <code>&lt;title&gt;</code>, <code>&lt;link&gt;</code> and <code>&lt;description&gt;</code> are used to describe the feed and its contents.</p>
<p>These details should match what&rsquo;s displayed on your site and will show up for users when adding or managing your feed in their reader.</p>
<p>The <code>&lt;link&gt;</code> should point to the URL of the <code>&lt;channel&gt;</code>, which is the site itself.</p>
<pre><code class="language-xml">&lt;item&gt;
  &lt;title&gt;Hello World&lt;/title&gt;
  &lt;link&gt;https://example.com/hello-world&lt;/link&gt;
  &lt;description&gt;My first post.&lt;/description&gt;
&lt;/item&gt;</code></pre>
<p>The <code>&lt;item&gt;</code> tag is used for each post in the feed.</p>
<p>An <code>&lt;item&gt;</code> <strong>must</strong> contain at least one of:</p>
<ul>
<li><code>&lt;title&gt;</code></li>
<li><code>&lt;description&gt;</code></li>
</ul>
<p>which means you could do:</p>
<pre><code class="language-xml">&lt;item&gt;
  &lt;title&gt;My first post&lt;/title&gt;
&lt;/item&gt;</code></pre>
<p>which is valid, but not very useful, or:</p>
<pre><code class="language-xml">&lt;item&gt;
  &lt;description&gt;Hello, World!&lt;/description&gt;
&lt;/item&gt;</code></pre>
<p>which is also valid, and slightly more useful, if only for microblogs and notes and other posts without a title.</p>
<pre><code class="language-xml">&lt;item&gt;
  &lt;title&gt;An example&lt;/title&gt;
  &lt;link&gt;https://example.com/posts/1&lt;/link&gt;
&lt;/item&gt;</code></pre>
<p>Title + link is more practical, and may be all you need, but:</p>
<pre><code class="language-xml">&lt;item&gt;
  &lt;title&gt;An example&lt;/title&gt;
  &lt;link&gt;https://example.com/posts/1&lt;/link&gt;
  &lt;description&gt;Hello, World!&lt;/description&gt;
&lt;/item&gt;</code></pre>
<p>using all three is most common.</p>
<p>You could use the <code>&lt;description&gt;</code> for the summary of your post or for the whole thing.</p>
<p>There&rsquo;s no upper limit on the amount of <code>&lt;item&gt;</code>s you can include in a feed, but there are some practical things to consider:</p>
<ul>
<li>RSS does not do pagination</li>
<li>
<ul>
<li>everything is listed in one file</li>
</ul>
</li>
<li>if the file gets really big then it takes longer to download</li>
<li>
<ul>
<li>aim to keep it under 500KB</li>
</ul>
</li>
<li>maybe only show your latest posts if you have lots of them</li>
</ul>
<h3 id="optional">Optional</h3>
<p>The fields mentioned above are all that is required for a minimal feed, but there are some optional extras that are recommended.</p>
<pre><code class="language-xml">&lt;item&gt;
  ...
  &lt;guid isPermaLink=&#34;true&#34;&gt;
    https://example.com/posts/1
  &lt;/guid&gt;
&lt;/item&gt;</code></pre>
<p>A <code>guid</code>, for &ldquo;Globally Unique IDentifier&rdquo;, gives each item in the feed a stable identity. This means feed readers can detect and remove duplicates correctly.</p>
<p>This is usually the permalink of the post. If so, <code>isPermaLink</code> defaults to true and can be omitted, however it it&rsquo;s not the permalink, like a database ID, or the exact datetime the post was published, you would change <code>isPermaLink</code> to <code>false</code>.</p>
<p>Note that the <code>L</code> in <code>PermaLink</code> is also capitalised.</p>
<pre><code class="language-xml">&lt;item&gt;
  ...
  &lt;pubDate&gt;Sun, 1 Mar 2026 14:00:00 GMT&lt;/pubDate&gt;
&lt;/item&gt;</code></pre>
<p>The <code>&lt;pubDate&gt;</code> is used to show when an item in the feed was published. It can help feed readers understand the order of your posts and should be added.</p>
<pre><code class="language-xml">&lt;rss version=&#34;2.0&#34;
xmlns:atom=&#34;http://www.w3.org/2005/Atom&#34;&gt;</code></pre>
<p>Changing the <code>&lt;rss&gt;</code> tag to include <code>xmlns:atom=&quot;http://www.w3.org/2005/Atom&quot;</code> is also optional but recommended.</p>
<p>This tells whatever is reading the feed that anything defined with <code>atom:</code> comes from the Atom specification, and lets you use extra tags defined by the Atom spec.</p>
<p>This isn&rsquo;t useful by itself, but it is used when you also add:</p>
<pre><code class="language-xml">&lt;atom:link
href=&#34;https://example.com/index.xml&#34;
rel=&#34;self&#34;
type=&#34;application/rss+xml&#34; /&gt;</code></pre>
<p>which defines the URL of the feed and helps feed readers know the canonical URL of the feed itself, which helps prevent duplicate subscriptions.</p>
<pre><code class="language-xml">&lt;lastBuildDate&gt;Sun, 1 Mar 2026 14:00:00 GMT&lt;/lastBuildDate&gt;</code></pre>
<p>The <code>&lt;lastBuildDate&gt;</code> can be used by feed readers to know if the feed has changed.</p>
<p>Set it to the date of the most recent post.</p>
<pre><code class="language-xml">&lt;language&gt;en-gb&lt;/language&gt;</code></pre>
<p>You should declare the language of the feed. It&rsquo;s small, but it&rsquo;s technically correct.</p>
<h2 id="things-to-remember">Things to remember</h2>
<p>RSS is pretty simple but dealing with XML means you have to remember a few things.</p>
<h3 id="xml-must-be-well-formed">XML must be well-formed</h3>
<p>Writing an RSS feed is still writing XML, and so it must obey the rules of an XML file.</p>
<p>This means the file must not have any missing closing tags, or mix ups with the order in which you close them.</p>
<p>It also means you must handle special characters, otherwise the file is invalid and won&rsquo;t be understood.</p>
<p>For example:</p>
<pre><code class="language-xml">&lt;title&gt;Special &amp; Character&lt;/title&gt;</code></pre>
<p>won&rsquo;t work, because of the <code>&amp;</code>, which must be escaped:</p>
<pre><code class="language-xml">&lt;title&gt;Special &amp;amp; Character&lt;/title&gt;</code></pre>
<h4 id="no-unescaped-html-in-the-description">No unescaped HTML in the description</h4>
<p>The same happens with the <code>&lt;description&gt;</code> tag, which usually holds the summary or the contents of the post, and must not include unescaped HTML characters.</p>
<p>The first option is to escape all of the HTML in the description:</p>
<pre><code class="language-xml">&lt;description&gt;&amp;lt;p&amp;gt;Hello, World!&amp;lt;/p&amp;gt;&lt;/description&gt;</code></pre>
<p>or the more common option is to wrap the HTML content in the description in a <code>CDATA</code> tag:</p>
<pre><code class="language-xml">&lt;description&gt;&lt;![CDATA[
&lt;p&gt;Hello, World!&lt;/p&gt;
]]&gt;&lt;/description&gt;</code></pre>
<p>where CDATA tells whatever is reading the XML file that everything inside this is raw text.</p>
<h4 id="dates-must-be-written-as-rfc-822">Dates must be written as RFC 822</h4>
<p>If you do use <code>&lt;pubDate&gt;</code> or <code>&lt;lastBuildDate&gt;</code> to show dates you must ensure they are written using the <a href="https://www.w3.org/Protocols/rfc822/#z28">RFC 822 format</a>
:</p>
<pre><code class="language-xml">&lt;pubDate&gt;Sun, 1 Mar 2026 14:00:00 GMT&lt;/pubDate&gt;</code></pre>
<h3 id="serve-the-file-with-the-correct-content-type">Serve the file with the correct Content-Type</h3>
<p>When your server responds to a request to show the feed it should send:</p>
<pre><code class="language-text">Content-Type: application/rss+xml</code></pre>
<p>so that feed readers recognise it as an RSS feed.</p>
<p>Most static web hosting handles this for you, I only mention it in case you are setting up a server and are messing with the Content-Type of the files you serve.</p>
<h2 id="how-to-check-your-feed">How to check your feed</h2>
<p>There are a few ways to check you&rsquo;ve written your feed correctly.</p>
<h3 id="open-it-in-a-browser">Open it in a browser</h3>
<p>The file should open and look similar to what you wrote it as. If it doesn&rsquo;t open, there&rsquo;s a problem, and if it shows an error then you need to fix it.</p>
<h3 id="use-a-validator">Use a validator</h3>
<p>You can use <a href="https://validator.w3.org/feed/">validator.w3.org/feed</a>
 to validate your feed. It can work with the URL of your feed online or you can paste in its contents if you&rsquo;re still working on it.</p>
<h3 id="add-it-to-your-reader">Add it to your reader</h3>
<p>The best test is to add your own feed to your reader and check it looks the way you want.</p>
<p>Make sure it&rsquo;s showing in the order you expect and you can edit posts without making duplicates in your reader.</p>
<h2 id="full-example">Full example</h2>
<p>Here is a condensed version of my own RSS feed, containing two articles and a note.</p>
<p>The note doesn&rsquo;t have a <code>&lt;title&gt;</code>, which influenced the URL used for its <code>&lt;guid&gt;</code>.</p>
<pre><code class="language-xml">&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34; standalone=&#34;yes&#34;?&gt;
&lt;rss version=&#34;2.0&#34;
	xmlns:atom=&#34;http://www.w3.org/2005/Atom&#34;&gt;
	&lt;channel&gt;
		&lt;title&gt;Paul Tibbetts&lt;/title&gt;
		&lt;link&gt;https://paultibbetts.uk/&lt;/link&gt;
		&lt;description&gt;Recent posts by Paul Tibbetts&lt;/description&gt;
		&lt;generator&gt;Hugo&lt;/generator&gt;
		&lt;language&gt;en-gb&lt;/language&gt;
		&lt;lastBuildDate&gt;Sun, 01 Mar 2026 12:41:46 +0000&lt;/lastBuildDate&gt;
		&lt;atom:link href=&#34;https://paultibbetts.uk/feed.xml&#34; rel=&#34;self&#34; type=&#34;application/rss+xml&#34; /&gt;
		&lt;item&gt;
			&lt;title&gt;How to create an RSS Feed&lt;/title&gt;
			&lt;link&gt;https://paultibbetts.uk/2026/03/01/how-to-create-an-rss-feed/&lt;/link&gt;
			&lt;pubDate&gt;Sun, 01 Mar 2026 16:33:46 +0000&lt;/pubDate&gt;
			&lt;guid&gt;https://paultibbetts.uk/2026/03/01/how-to-create-an-rss-feed/&lt;/guid&gt;
			&lt;description&gt;In my last post I recommended starting with RSS to make your site followable. This is how to do that.&lt;/description&gt;
		&lt;/item&gt;
		&lt;item&gt;
			&lt;title&gt;Ways to make your feed followable&lt;/title&gt;
			&lt;link&gt;https://paultibbetts.uk/2026/03/01/ways-to-make-your-feed-followable/&lt;/link&gt;
			&lt;pubDate&gt;Sun, 01 Mar 2026 11:36:03 +0000&lt;/pubDate&gt;
			&lt;guid&gt;https://paultibbetts.uk/2026/03/01/ways-to-make-your-feed-followable/&lt;/guid&gt;
			&lt;description&gt;You’ve got 3 posts on your site and you want people to be notified about new ones. Here’s all the ways I can think of doing that.&lt;/description&gt;
			&lt;/item&gt;
		&lt;item&gt;
			&lt;title&gt;2026-02-15 14:54:58&lt;/title&gt;
			&lt;link&gt;https://paultibbetts.uk/2026/02/15/2026-02-15-145458/&lt;/link&gt;
			&lt;pubDate&gt;Sun, 15 Feb 2026 14:54:58 +0000&lt;/pubDate&gt;
			&lt;guid&gt;https://paultibbetts.uk/2026/02/15/2026-02-15-145458/&lt;/guid&gt;
			&lt;description&gt;Launched: infra.paultibbetts.uk A documentation site for the infrastructure that runs paultibbetts.uk.&lt;/description&gt;
		&lt;/item&gt;
	&lt;/channel&gt;
&lt;/rss&gt;</code></pre>]]></content:encoded><source:markdown>
In my last post I recommended starting with RSS to make your site followable.

This is how to do that.

&lt;!--more--&gt;

## You might already have one

If you&#39;re using a static site generator like Hugo, or a CMS like WordPress, then you might already have an RSS feed.

If not, and you&#39;re writing your site manually, or just want to understand what&#39;s going on, here&#39;s what an RSS feed looks like.

## A minimal RSS feed

At a minimum an RSS feed contains:

```xml
&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;
&lt;rss version=&#34;2.0&#34;&gt;
  &lt;channel&gt;
    &lt;title&gt;Example Blog&lt;/title&gt;
    &lt;link&gt;https://example.com/&lt;/link&gt;
    &lt;description&gt;Updates from my blog.&lt;/description&gt;

    &lt;item&gt;
      &lt;title&gt;Hello World&lt;/title&gt;
      &lt;link&gt;https://example.com/hello-world&lt;/link&gt;
      &lt;description&gt;My first post.&lt;/description&gt;
    &lt;/item&gt;

  &lt;/channel&gt;
&lt;/rss&gt;
```

### Required

```xml
&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;
```

This line declares the XML version of the file and the encoding that it uses. `UTF-8` supports text and emoji.

It&#39;s strongly recommended to include this line so the file is read correctly.

```xml
&lt;rss version=&#34;2.0&#34;&gt;
```

This is the start of the `&lt;rss&gt;` element. The `version=&#34;2.0&#34;` tells consumers that the feed conforms to the RSS 2.0 specification, the most widely supported version.

```xml
&lt;channel&gt;
  &lt;title&gt;Example Blog&lt;/title&gt;
  &lt;link&gt;https://example.com/&lt;/link&gt;
  &lt;description&gt;Updates from my blog.&lt;/description&gt;
```

The `&lt;channel&gt;` declares the feed itself, so `&lt;title&gt;`, `&lt;link&gt;` and `&lt;description&gt;` are used to describe the feed and its contents.

These details should match what&#39;s displayed on your site and will show up for users when adding or managing your feed in their reader.

The `&lt;link&gt;` should point to the URL of the `&lt;channel&gt;`, which is the site itself.

```xml
&lt;item&gt;
  &lt;title&gt;Hello World&lt;/title&gt;
  &lt;link&gt;https://example.com/hello-world&lt;/link&gt;
  &lt;description&gt;My first post.&lt;/description&gt;
&lt;/item&gt;
```

The `&lt;item&gt;` tag is used for each post in the feed.

An `&lt;item&gt;` **must** contain at least one of:

- `&lt;title&gt;`
- `&lt;description&gt;`

which means you could do:

```xml
&lt;item&gt;
  &lt;title&gt;My first post&lt;/title&gt;
&lt;/item&gt;
```

which is valid, but not very useful, or:

```xml
&lt;item&gt;
  &lt;description&gt;Hello, World!&lt;/description&gt;
&lt;/item&gt;
```

which is also valid, and slightly more useful, if only for microblogs and notes and other posts without a title.

```xml
&lt;item&gt;
  &lt;title&gt;An example&lt;/title&gt;
  &lt;link&gt;https://example.com/posts/1&lt;/link&gt;
&lt;/item&gt;
```

Title + link is more practical, and may be all you need, but:

```xml
&lt;item&gt;
  &lt;title&gt;An example&lt;/title&gt;
  &lt;link&gt;https://example.com/posts/1&lt;/link&gt;
  &lt;description&gt;Hello, World!&lt;/description&gt;
&lt;/item&gt;
```

using all three is most common.

You could use the `&lt;description&gt;` for the summary of your post or for the whole thing.

There&#39;s no upper limit on the amount of `&lt;item&gt;`s you can include in a feed, but there are some practical things to consider:

- RSS does not do pagination
- - everything is listed in one file
- if the file gets really big then it takes longer to download
- - aim to keep it under 500KB
- maybe only show your latest posts if you have lots of them

### Optional

The fields mentioned above are all that is required for a minimal feed, but there are some optional extras that are recommended.

```xml
&lt;item&gt;
  ...
  &lt;guid isPermaLink=&#34;true&#34;&gt;
    https://example.com/posts/1
  &lt;/guid&gt;
&lt;/item&gt;
```

A `guid`, for &#34;Globally Unique IDentifier&#34;, gives each item in the feed a stable identity. This means feed readers can detect and remove duplicates correctly.

This is usually the permalink of the post. If so, `isPermaLink` defaults to true and can be omitted, however it it&#39;s not the permalink, like a database ID, or the exact datetime the post was published, you would change `isPermaLink` to `false`.

Note that the `L` in `PermaLink` is also capitalised.

```xml
&lt;item&gt;
  ...
  &lt;pubDate&gt;Sun, 1 Mar 2026 14:00:00 GMT&lt;/pubDate&gt;
&lt;/item&gt;
```

The `&lt;pubDate&gt;` is used to show when an item in the feed was published. It can help feed readers understand the order of your posts and should be added.

```xml
&lt;rss version=&#34;2.0&#34;
xmlns:atom=&#34;http://www.w3.org/2005/Atom&#34;&gt;
```

Changing the `&lt;rss&gt;` tag to include `xmlns:atom=&#34;http://www.w3.org/2005/Atom&#34;` is also optional but recommended.

This tells whatever is reading the feed that anything defined with `atom:` comes from the Atom specification, and lets you use extra tags defined by the Atom spec.

This isn&#39;t useful by itself, but it is used when you also add:

```xml
&lt;atom:link
href=&#34;https://example.com/index.xml&#34;
rel=&#34;self&#34;
type=&#34;application/rss+xml&#34; /&gt;
```

which defines the URL of the feed and helps feed readers know the canonical URL of the feed itself, which helps prevent duplicate subscriptions.

```xml
&lt;lastBuildDate&gt;Sun, 1 Mar 2026 14:00:00 GMT&lt;/lastBuildDate&gt;
```

The `&lt;lastBuildDate&gt;` can be used by feed readers to know if the feed has changed.

Set it to the date of the most recent post.

```xml
&lt;language&gt;en-gb&lt;/language&gt;
```

You should declare the language of the feed. It&#39;s small, but it&#39;s technically correct.

## Things to remember

RSS is pretty simple but dealing with XML means you have to remember a few things.

### XML must be well-formed

Writing an RSS feed is still writing XML, and so it must obey the rules of an XML file.

This means the file must not have any missing closing tags, or mix ups with the order in which you close them.

It also means you must handle special characters, otherwise the file is invalid and won&#39;t be understood.

For example:

```xml
&lt;title&gt;Special &amp; Character&lt;/title&gt;
```

won&#39;t work, because of the `&amp;`, which must be escaped:

```xml
&lt;title&gt;Special &amp;amp; Character&lt;/title&gt;
```

#### No unescaped HTML in the description

The same happens with the `&lt;description&gt;` tag, which usually holds the summary or the contents of the post, and must not include unescaped HTML characters.

The first option is to escape all of the HTML in the description:

```xml
&lt;description&gt;&amp;lt;p&amp;gt;Hello, World!&amp;lt;/p&amp;gt;&lt;/description&gt;
```

or the more common option is to wrap the HTML content in the description in a `CDATA` tag:

```xml
&lt;description&gt;&lt;![CDATA[
&lt;p&gt;Hello, World!&lt;/p&gt;
]]&gt;&lt;/description&gt;
```

where CDATA tells whatever is reading the XML file that everything inside this is raw text.

#### Dates must be written as RFC 822

If you do use `&lt;pubDate&gt;` or `&lt;lastBuildDate&gt;` to show dates you must ensure they are written using the [RFC 822 format](https://www.w3.org/Protocols/rfc822/#z28):

```xml
&lt;pubDate&gt;Sun, 1 Mar 2026 14:00:00 GMT&lt;/pubDate&gt;
```

### Serve the file with the correct Content-Type

When your server responds to a request to show the feed it should send:

```
Content-Type: application/rss+xml
```

so that feed readers recognise it as an RSS feed.

Most static web hosting handles this for you, I only mention it in case you are setting up a server and are messing with the Content-Type of the files you serve.

## How to check your feed

There are a few ways to check you&#39;ve written your feed correctly.

### Open it in a browser

The file should open and look similar to what you wrote it as. If it doesn&#39;t open, there&#39;s a problem, and if it shows an error then you need to fix it.

### Use a validator

You can use [validator.w3.org/feed](https://validator.w3.org/feed/) to validate your feed. It can work with the URL of your feed online or you can paste in its contents if you&#39;re still working on it.

### Add it to your reader

The best test is to add your own feed to your reader and check it looks the way you want.

Make sure it&#39;s showing in the order you expect and you can edit posts without making duplicates in your reader.

## Full example

Here is a condensed version of my own RSS feed, containing two articles and a note.

The note doesn&#39;t have a `&lt;title&gt;`, which influenced the URL used for its `&lt;guid&gt;`.

```xml
&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34; standalone=&#34;yes&#34;?&gt;
&lt;rss version=&#34;2.0&#34;
	xmlns:atom=&#34;http://www.w3.org/2005/Atom&#34;&gt;
	&lt;channel&gt;
		&lt;title&gt;Paul Tibbetts&lt;/title&gt;
		&lt;link&gt;https://paultibbetts.uk/&lt;/link&gt;
		&lt;description&gt;Recent posts by Paul Tibbetts&lt;/description&gt;
		&lt;generator&gt;Hugo&lt;/generator&gt;
		&lt;language&gt;en-gb&lt;/language&gt;
		&lt;lastBuildDate&gt;Sun, 01 Mar 2026 12:41:46 +0000&lt;/lastBuildDate&gt;
		&lt;atom:link href=&#34;https://paultibbetts.uk/feed.xml&#34; rel=&#34;self&#34; type=&#34;application/rss+xml&#34; /&gt;
		&lt;item&gt;
			&lt;title&gt;How to create an RSS Feed&lt;/title&gt;
			&lt;link&gt;https://paultibbetts.uk/2026/03/01/how-to-create-an-rss-feed/&lt;/link&gt;
			&lt;pubDate&gt;Sun, 01 Mar 2026 16:33:46 +0000&lt;/pubDate&gt;
			&lt;guid&gt;https://paultibbetts.uk/2026/03/01/how-to-create-an-rss-feed/&lt;/guid&gt;
			&lt;description&gt;In my last post I recommended starting with RSS to make your site followable. This is how to do that.&lt;/description&gt;
		&lt;/item&gt;
		&lt;item&gt;
			&lt;title&gt;Ways to make your feed followable&lt;/title&gt;
			&lt;link&gt;https://paultibbetts.uk/2026/03/01/ways-to-make-your-feed-followable/&lt;/link&gt;
			&lt;pubDate&gt;Sun, 01 Mar 2026 11:36:03 +0000&lt;/pubDate&gt;
			&lt;guid&gt;https://paultibbetts.uk/2026/03/01/ways-to-make-your-feed-followable/&lt;/guid&gt;
			&lt;description&gt;You’ve got 3 posts on your site and you want people to be notified about new ones. Here’s all the ways I can think of doing that.&lt;/description&gt;
			&lt;/item&gt;
		&lt;item&gt;
			&lt;title&gt;2026-02-15 14:54:58&lt;/title&gt;
			&lt;link&gt;https://paultibbetts.uk/2026/02/15/2026-02-15-145458/&lt;/link&gt;
			&lt;pubDate&gt;Sun, 15 Feb 2026 14:54:58 +0000&lt;/pubDate&gt;
			&lt;guid&gt;https://paultibbetts.uk/2026/02/15/2026-02-15-145458/&lt;/guid&gt;
			&lt;description&gt;Launched: infra.paultibbetts.uk A documentation site for the infrastructure that runs paultibbetts.uk.&lt;/description&gt;
		&lt;/item&gt;
	&lt;/channel&gt;
&lt;/rss&gt;
```
</source:markdown></item><item>
      <title>Ways to make your feed followable</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/01/ways-to-make-your-feed-followable/</link>
      <pubDate>Sun, 01 Mar 2026 11:36:03 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/03/01/ways-to-make-your-feed-followable/</guid>
      <category>IndieWeb</category>
      <category>RSS</category>
      <description>You’ve got 3 posts on your site and you want people to be notified about new ones.
Here’s all the ways I can think of doing that.</description>
      <content:encoded><![CDATA[<p>You&rsquo;ve got 3 posts on your site and you want people to be notified about new ones.</p>
<p>Here&rsquo;s all the ways I can think of doing that.</p>
<h2 id="email">Email</h2>
<p>Some sites let you send out your posts as emails.</p>
<p>Don&rsquo;t do it. It can be tempting, because it&rsquo;s easy, but emails are for emails.</p>
<p>There are better options for your feed.</p>
<h2 id="rss">RSS</h2>
<p>RSS stands for &ldquo;Really Simple Syndication&rdquo;.</p>
<p>It&rsquo;s a standard format for publishing a feed so apps can subscribe to your updates.</p>
<p>Feed readers and podcast apps use it.</p>
<p>It looks a bit like this:</p>
<pre><code class="language-xml">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;
&lt;rss version=&#34;2.0&#34;&gt;
  &lt;channel&gt;
    &lt;title&gt;RSS Example Site&lt;/title&gt;
    &lt;link&gt;https://example.com/&lt;/link&gt;
    &lt;description&gt;Example site description.&lt;/description&gt;

    &lt;item&gt;
      &lt;title&gt;Hello, World!&lt;/title&gt;
      &lt;link&gt;https://example.com/hello-world.html&lt;/link&gt;
      &lt;description&gt;My first post.&lt;/description&gt;
    &lt;/item&gt;

  &lt;/channel&gt;
&lt;/rss&gt;</code></pre>
<p>You would host a file like this, link to it from the <code>&lt;head&gt;</code> part of your pages, and readers would check it occasionally for updates.</p>
<p>Its simplicity means it&rsquo;s supported by almost all feed reading apps.</p>
<p>Because of this I&rsquo;d say RSS is the best starting point for letting users subscribe to your posts and is worth adding to your site first, before the other alternatives.</p>
<h2 id="atom">Atom</h2>
<p>RSS isn&rsquo;t the only way to create an XML feed. Atom is another standard which is more formally specified.</p>
<p>In the early 2000&rsquo;s, after Atom&rsquo;s launch, there was a time called the &ldquo;RSS vs Atom wars&rdquo;. There was no real winner. Atom was decided to be technically better, but by then RSS had already become dominant.</p>
<p>These days you can add a few Atom elements to your RSS feed, like a <code>rel=self</code> link, to get the best of both worlds.</p>
<h2 id="json">JSON</h2>
<p><a href="https://www.jsonfeed.org/">JSON feed</a>
 is the one of the newer standards. It works in the same way as RSS and Atom but the data is presented as JSON instead of XML.</p>
<p>Web development has moved on from XML and these days JSON-based things are much more common. The idea is that by using JSON we allow developers to build new things like feed readers using modern techniques and tooling, instead of ones used only for legacy publishing steps like an XML-based feed.</p>
<p>It looks a bit like this:</p>
<pre><code class="language-json">{
  &#34;version&#34;: &#34;https://jsonfeed.org/version/1.1&#34;,
  &#34;title&#34;: &#34;Example JSON Feed Site&#34;,
  &#34;home_page_url&#34;: &#34;https://example.com/&#34;,
  &#34;feed_url&#34;: &#34;https://example.com/feed.json&#34;,
  &#34;items&#34;: [
    {
      &#34;id&#34;: &#34;2026-02-28-153000&#34;,
      &#34;url&#34;: &#34;https://paultibbetts.uk/notes/2026-02-28-153000/&#34;,
      &#34;content_html&#34;: &#34;&lt;p&gt;Hello world&lt;/p&gt;&#34;,
      &#34;date_published&#34;: &#34;2026-02-28T15:30:00Z&#34;
    }
  ]
}</code></pre>
<p>JSON feed is already supported by many feed readers and, at least in theory, makes it easier than XML-based feeds to make new ones.</p>
<p>I&rsquo;d say it&rsquo;s worth adding to your site after you&rsquo;ve got an RSS feed, since those are more universally supported right now.</p>
<h2 id="microformats">Microformats</h2>
<p><a href="https://indieweb.org/microformats">Microformats</a>
 is a standard for marking up the contents of your posts, and in doing so makes your feed followable without any other files.</p>
<p>Classes like <code>h-entry</code> and <code>h-card</code> help others to understand the content you&rsquo;re publishing, and the <code>h-feed</code> class is used to identify feeds. This means readers using apps that understand microformats can subscribe to your content directly, without needing an XML or JSON file.</p>
<p>Right now most feed readers expect RSS or Atom feeds, so whilst I would suggest adding microformats, I would do it after adding an RSS feed.</p>
<h2 id="social-media">Social media</h2>
<p>There&rsquo;s also social media, where you would post a link to your content on another site, like Facebook or Instagram.</p>
<p>It&rsquo;s a great way to get your posts in front of users across the web on the apps they&rsquo;re already using, but it&rsquo;s no longer on your own site and you&rsquo;re now at the mercy of whatever the algorithms think of your content.</p>
<p>I&rsquo;d consider doing this as an extra, and only after making your own feed available.</p>
<p>For more information on posting out to social media the IndieWeb.org wiki has a great page called <a href="https://indieweb.org/POSSE">Publish Own Site, Syndicate Elsewhere (POSSE)</a>
.</p>
<h2 id="activitypub">ActivityPub</h2>
<p>Another option is to use the ActivityPub protocol for your content.</p>
<p>ActivityPub is what powers Fediverse apps like Mastodon and making your content available through it makes your site part of the Fediverse itself. Readers can then subscribe using whatever Fediverse app they want.</p>
<p>This involves a lot of work, and is difficult to implement for static sites, so hosted solutions like <a href="https://fed.brid.gy/">Bridgy Fed</a>
 can be used to do this for you.</p>
<h2 id="all-of-the-above">All of the above</h2>
<p>The best option could even be to do all of the above, to give your readers freedom to choose the method that works for them.</p>
<p>If you want the most compatible way to let others follow your posts: start with RSS.</p>
<p>Then add alternatives after.</p>]]></content:encoded><source:markdown>
You&#39;ve got 3 posts on your site and you want people to be notified about new ones.

Here&#39;s all the ways I can think of doing that.

&lt;!--more--&gt;

## Email

Some sites let you send out your posts as emails.

Don&#39;t do it. It can be tempting, because it&#39;s easy, but emails are for emails.

There are better options for your feed.

## RSS

RSS stands for &#34;Really Simple Syndication&#34;.

It&#39;s a standard format for publishing a feed so apps can subscribe to your updates.

Feed readers and podcast apps use it.

It looks a bit like this:

```xml
&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;
&lt;rss version=&#34;2.0&#34;&gt;
  &lt;channel&gt;
    &lt;title&gt;RSS Example Site&lt;/title&gt;
    &lt;link&gt;https://example.com/&lt;/link&gt;
    &lt;description&gt;Example site description.&lt;/description&gt;

    &lt;item&gt;
      &lt;title&gt;Hello, World!&lt;/title&gt;
      &lt;link&gt;https://example.com/hello-world.html&lt;/link&gt;
      &lt;description&gt;My first post.&lt;/description&gt;
    &lt;/item&gt;

  &lt;/channel&gt;
&lt;/rss&gt;
```

You would host a file like this, link to it from the `&lt;head&gt;` part of your pages, and readers would check it occasionally for updates.

Its simplicity means it&#39;s supported by almost all feed reading apps.

Because of this I&#39;d say RSS is the best starting point for letting users subscribe to your posts and is worth adding to your site first, before the other alternatives.

## Atom

RSS isn&#39;t the only way to create an XML feed. Atom is another standard which is more formally specified.

In the early 2000&#39;s, after Atom&#39;s launch, there was a time called the &#34;RSS vs Atom wars&#34;. There was no real winner. Atom was decided to be technically better, but by then RSS had already become dominant.

These days you can add a few Atom elements to your RSS feed, like a `rel=self` link, to get the best of both worlds.

## JSON

[JSON feed](https://www.jsonfeed.org/) is the one of the newer standards. It works in the same way as RSS and Atom but the data is presented as JSON instead of XML.

Web development has moved on from XML and these days JSON-based things are much more common. The idea is that by using JSON we allow developers to build new things like feed readers using modern techniques and tooling, instead of ones used only for legacy publishing steps like an XML-based feed.

It looks a bit like this:

```json
{
  &#34;version&#34;: &#34;https://jsonfeed.org/version/1.1&#34;,
  &#34;title&#34;: &#34;Example JSON Feed Site&#34;,
  &#34;home_page_url&#34;: &#34;https://example.com/&#34;,
  &#34;feed_url&#34;: &#34;https://example.com/feed.json&#34;,
  &#34;items&#34;: [
    {
      &#34;id&#34;: &#34;2026-02-28-153000&#34;,
      &#34;url&#34;: &#34;https://paultibbetts.uk/notes/2026-02-28-153000/&#34;,
      &#34;content_html&#34;: &#34;&lt;p&gt;Hello world&lt;/p&gt;&#34;,
      &#34;date_published&#34;: &#34;2026-02-28T15:30:00Z&#34;
    }
  ]
}
```

JSON feed is already supported by many feed readers and, at least in theory, makes it easier than XML-based feeds to make new ones.

I&#39;d say it&#39;s worth adding to your site after you&#39;ve got an RSS feed, since those are more universally supported right now.

## Microformats

[Microformats](https://indieweb.org/microformats) is a standard for marking up the contents of your posts, and in doing so makes your feed followable without any other files.

Classes like `h-entry` and `h-card` help others to understand the content you&#39;re publishing, and the `h-feed` class is used to identify feeds. This means readers using apps that understand microformats can subscribe to your content directly, without needing an XML or JSON file.

Right now most feed readers expect RSS or Atom feeds, so whilst I would suggest adding microformats, I would do it after adding an RSS feed.

## Social media

There&#39;s also social media, where you would post a link to your content on another site, like Facebook or Instagram.

It&#39;s a great way to get your posts in front of users across the web on the apps they&#39;re already using, but it&#39;s no longer on your own site and you&#39;re now at the mercy of whatever the algorithms think of your content.

I&#39;d consider doing this as an extra, and only after making your own feed available.

For more information on posting out to social media the IndieWeb.org wiki has a great page called [Publish Own Site, Syndicate Elsewhere (POSSE)](https://indieweb.org/POSSE).

## ActivityPub

Another option is to use the ActivityPub protocol for your content.

ActivityPub is what powers Fediverse apps like Mastodon and making your content available through it makes your site part of the Fediverse itself. Readers can then subscribe using whatever Fediverse app they want.

This involves a lot of work, and is difficult to implement for static sites, so hosted solutions like [Bridgy Fed](https://fed.brid.gy/) can be used to do this for you.

## All of the above

The best option could even be to do all of the above, to give your readers freedom to choose the method that works for them.

If you want the most compatible way to let others follow your posts: start with RSS.

Then add alternatives after.
</source:markdown></item><item>
      <title>I Made an Ansible Role for Caddy</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/24/i-made-an-ansible-role-for-caddy/</link>
      <pubDate>Tue, 24 Feb 2026 12:10:45 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/24/i-made-an-ansible-role-for-caddy/</guid>
      <category>Ansible</category>
      <category>Caddy</category>
      <category>code</category>
      <description>paultibbetts/ansible-role-caddy is an Ansible role for Caddy web server that lets you install Caddy with plugins.
I use it for my personal site and in my homelab.
This is what it does and how to use it.</description>
      <content:encoded><![CDATA[<p><a href="https://github.com/paultibbetts/ansible-role-caddy">paultibbetts/ansible-role-caddy</a>
 is an Ansible role for Caddy web server that lets you install Caddy with plugins.</p>
<p>I use it for my personal site and in my homelab.</p>
<p>This is what it does and how to use it.</p>
<h2 id="what-it-does">What it does</h2>
<p>An Ansible role is like a plugin that extends Ansible with the ability to do something, and in this case it&rsquo;s to install and manage <a href="https://caddyserver.com/">Caddy</a>
.</p>
<p>Specifically it:</p>
<ul>
<li>installs Caddy</li>
<li>
<ul>
<li>defaults to using the <code>apt</code> package manager</li>
</ul>
</li>
<li>
<ul>
<li>or it can be given the URL to a specific binary</li>
</ul>
</li>
<li>
<ul>
<li>or it can be given a list of plugins</li>
</ul>
</li>
<li>can be used to manage Caddy</li>
<li>
<ul>
<li>or completely uninstall it</li>
</ul>
</li>
<li>can write the Caddyfile from a template</li>
</ul>
<h2 id="why-caddy">Why Caddy?</h2>
<p>I use Caddy because it manages TLS certificates automatically and, with a plugin, supports <a href="https://letsencrypt.org/docs/challenge-types/#dns-01-challenge">DNS-01 challenges</a>
.</p>
<p>DNS-01 is useful when services aren&rsquo;t reachable by Lets Encrypt, like in a homelab, so private networks can also have TLS.</p>
<h2 id="design-goals">Design goals</h2>
<p>The role was designed with the following in mind:</p>
<ul>
<li>idempotent and safe to re-run</li>
<li>Debian and Ubuntu</li>
<li>works on x86 and ARM</li>
<li>supports installing with plugins</li>
<li>can optionally write the Caddyfile</li>
</ul>
<p>There are already Ansible roles for Caddy, like the one listed in <a href="https://caddyserver.com/docs/install#ansible">the docs</a>
, but I needed a role that could install Caddy with plugins.</p>
<h2 id="how-to-use-it">How to use it</h2>
<p>The role is available on <a href="https://galaxy.ansible.com/ui/standalone/roles/paultibbetts/caddy/">Ansible Galaxy</a>
.</p>
<h3 id="install">Install</h3>
<p>You can install it by adding:</p>
<pre><code class="language-yaml">- name: paultibbetts.caddy
  version: 1.0.0</code></pre>
<p>to your <code>requirements.yml</code> file and then running:</p>
<pre><code class="language-sh">ansible-galaxy role install -r requirements.yml</code></pre>
<h3 id="variables">Variables</h3>
<p>The variables for the role, as well as the default settings, can all be overridden.</p>
<p>The most important ones are the install method, the plugins to include, and whether the role writes the Caddyfile for you.</p>
<pre><code class="language-yaml">caddy_install_method: apt</code></pre>
<p>The role defaults to installing Caddy via <code>apt</code>, the package manager for Debian/Ubuntu.</p>
<p>This means Caddy does not include any plugins but it does get updated when you run <code>apt upgrade</code>.</p>
<pre><code class="language-yaml">caddy_install_method: download</code></pre>
<p>Alternatively you can change the install method to <code>download</code>.</p>
<p>Now you can pass in the URL of a specific binary using:</p>
<pre><code class="language-yaml">caddy_download_url: &#34;&#34;</code></pre>
<p>Using this you could build a binary for Caddy (including plugins), host it, then pass that URL to the role and that specific binary will be installed.</p>
<p>This is currently the only way to install a specific version of Caddy that isn&rsquo;t &ldquo;latest&rdquo;.</p>
<p>If you want to install plugins for Caddy and don&rsquo;t want to build your own binary you can leave the download URL empty and pass in the plugins:</p>
<pre><code class="language-yaml">caddy_plugins:
  - github.com/caddy-dns/cloudflare</code></pre>
<p>and the role will generate a URL from the <a href="https://caddyserver.com/download">download page</a>
.</p>
<pre><code class="language-yaml">caddy_caddyfile_template: &#34;&#34; | &#34;{{ playbook_dir }}/templates/Caddyfile.j2&#34;</code></pre>
<p>The Caddyfile template variable lets you pass in a template of a Caddyfile for Caddy to use and it will be written to the appropriate place.</p>
<p>Letting the role write the Caddyfile means you can import the role, set its variables, and have all of Caddy configured by one thing - you don&rsquo;t need to write extra tasks after installation to get it up and running.</p>
<h3 id="examples">Examples</h3>
<p>The following examples show how the role can be used for a public website and for a private homelab reverse proxy.</p>
<p>In both instances Caddy acquires TLS certificates from Lets Encrypt, using Cloudflare to do DNS-01 challenges.</p>
<h4 id="example-personal-website">Example: Personal website</h4>
<p>The role can be used to setup Caddy to host a personal website.</p>
<pre><code class="language-yaml">- hosts: server
  become: true
  roles:
    - role: paultibbetts.caddy
      vars:
        caddy_caddyfile_template: &#34;{{ playbook_dir }}/templates/Caddyfile.j2&#34;
        caddy_install_method: download
        caddy_plugins:
          - github.com/caddy-dns/cloudflare
        caddy_manage_systemd_env_file: true
        caddy_systemd_env:
          CF_API_TOKEN: &#34;{{ vault_cf_api_token }}&#34;</code></pre>
<p>The <code>download</code> install method is used to install Caddy with the <a href="https://github.com/caddy-dns/cloudflare">Cloudflare plugin</a>
, the Cloudflare API token is passed to Caddy from Ansible vault, and a template is provided for the Caddyfile, which could look like:</p>
<pre><code class="language-Caddyfile">(cf_tls) {
    tls {
        dns cloudflare {env.CF_API_TOKEN}
    }
}

{% for site in web_sites %}
{{ site }} {
  import cf_tls
  root * {{ web_sites_root }}/{{ site }}/current
  file_server
  encode zstd gzip
  log
}
{% endfor %}</code></pre>
<p>This sets up Caddy to use Cloudflare to do DNS-01 challenges with Lets Encrypt to get a TLS certificate for each <code>site in web_sites</code>.</p>
<h4 id="example-homelab">Example: Homelab</h4>
<p>The role can also be used to setup Caddy in a private network, like a homelab.</p>
<pre><code class="language-yaml">- hosts: homelab
  become: true
  roles:
    - role: paultibbetts.caddy
      vars:
        caddy_caddyfile_template: &#34;{{ playbook_dir }}/templates/Caddyfile.j2&#34;
        caddy_install_method: download
        caddy_plugins:
          - github.com/caddy-dns/cloudflare
        caddy_manage_systemd_env_file: true
        caddy_systemd_env:
          CF_API_TOKEN: &#34;{{ vault_cf_api_token }}&#34;</code></pre>
<pre><code class="language-Caddyfile">{
        acme_dns cloudflare {env.CF_API_TOKEN}
}

service.homelab.example {
        reverse_proxy &lt;ip&gt;:&lt;port&gt;
}</code></pre>
<p>where the Cloudflare plugin is configured once and is then used to do DNS-01 challenges for any TLS certificates Caddy requests. Note this style has been deprecated and you should do it per host, like the earlier example for a personal website.</p>
<p>DNS-01 challenges are solved by writing a TXT record to prove domain ownership. This means Lets Encrypt is able to issue certificates for domains it can&rsquo;t reach, which makes DNS-01 ideal when Caddy is running in a homelab.</p>
<h2 id="molecule-scenarios">Molecule Scenarios</h2>
<p>Ansible Molecule is a way to run tests for an Ansible role.</p>
<p>The role includes molecule scenarios that test it works on x86 as well as Arm, because I use the role on a Raspberry Pi 4, as well as scenarios for the Cloudflare plugin and the Caddyfile templating.</p>
<h2 id="in-use">In use</h2>
<p>I use this role as part of the code that sets up the infrastructure for my website.</p>
<p>Terraform is used to provision the server, then Ansible is used to configure it, with this role setting up Caddy and the Caddyfile it uses.</p>
<p>The complete setup - Terraform, Ansible, Caddy, and the rest of the files - is documented at <a href="https://infra.paultibbetts.uk">infra.paultibbetts.uk</a>
, where I explain how it all fits together.</p>]]></content:encoded><source:markdown>
[paultibbetts/ansible-role-caddy](https://github.com/paultibbetts/ansible-role-caddy) is an Ansible role for Caddy web server that lets you install Caddy with plugins.

I use it for my personal site and in my homelab.

This is what it does and how to use it.

&lt;!--more--&gt;

## What it does

An Ansible role is like a plugin that extends Ansible with the ability to do something, and in this case it&#39;s to install and manage [Caddy](https://caddyserver.com/).

Specifically it:

- installs Caddy
- - defaults to using the `apt` package manager
- - or it can be given the URL to a specific binary
- - or it can be given a list of plugins
- can be used to manage Caddy
- - or completely uninstall it
- can write the Caddyfile from a template

## Why Caddy?

I use Caddy because it manages TLS certificates automatically and, with a plugin, supports [DNS-01 challenges](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge).

DNS-01 is useful when services aren&#39;t reachable by Lets Encrypt, like in a homelab, so private networks can also have TLS.

## Design goals

The role was designed with the following in mind:

- idempotent and safe to re-run
- Debian and Ubuntu
- works on x86 and ARM
- supports installing with plugins
- can optionally write the Caddyfile

There are already Ansible roles for Caddy, like the one listed in [the docs](https://caddyserver.com/docs/install#ansible), but I needed a role that could install Caddy with plugins.

## How to use it

The role is available on [Ansible Galaxy](https://galaxy.ansible.com/ui/standalone/roles/paultibbetts/caddy/).

### Install

You can install it by adding:

```yaml
- name: paultibbetts.caddy
  version: 1.0.0
```

to your `requirements.yml` file and then running:

```sh
ansible-galaxy role install -r requirements.yml
```

### Variables

The variables for the role, as well as the default settings, can all be overridden.

The most important ones are the install method, the plugins to include, and whether the role writes the Caddyfile for you.

```yaml
caddy_install_method: apt
```

The role defaults to installing Caddy via `apt`, the package manager for Debian/Ubuntu.

This means Caddy does not include any plugins but it does get updated when you run `apt upgrade`.

```yaml
caddy_install_method: download
```

Alternatively you can change the install method to `download`.

Now you can pass in the URL of a specific binary using:

```yaml
caddy_download_url: &#34;&#34;
```

Using this you could build a binary for Caddy (including plugins), host it, then pass that URL to the role and that specific binary will be installed.

This is currently the only way to install a specific version of Caddy that isn&#39;t &#34;latest&#34;.

If you want to install plugins for Caddy and don&#39;t want to build your own binary you can leave the download URL empty and pass in the plugins:

```yaml
caddy_plugins:
  - github.com/caddy-dns/cloudflare
```

and the role will generate a URL from the [download page](https://caddyserver.com/download).

```yaml
caddy_caddyfile_template: &#34;&#34; | &#34;{{ playbook_dir }}/templates/Caddyfile.j2&#34;
```

The Caddyfile template variable lets you pass in a template of a Caddyfile for Caddy to use and it will be written to the appropriate place.

Letting the role write the Caddyfile means you can import the role, set its variables, and have all of Caddy configured by one thing - you don&#39;t need to write extra tasks after installation to get it up and running.

### Examples

The following examples show how the role can be used for a public website and for a private homelab reverse proxy.

In both instances Caddy acquires TLS certificates from Lets Encrypt, using Cloudflare to do DNS-01 challenges.

#### Example: Personal website

The role can be used to setup Caddy to host a personal website.

```yaml
- hosts: server
  become: true
  roles:
    - role: paultibbetts.caddy
      vars:
        caddy_caddyfile_template: &#34;{{ playbook_dir }}/templates/Caddyfile.j2&#34;
        caddy_install_method: download
        caddy_plugins:
          - github.com/caddy-dns/cloudflare
        caddy_manage_systemd_env_file: true
        caddy_systemd_env:
          CF_API_TOKEN: &#34;{{ vault_cf_api_token }}&#34;
```

The `download` install method is used to install Caddy with the [Cloudflare plugin](https://github.com/caddy-dns/cloudflare), the Cloudflare API token is passed to Caddy from Ansible vault, and a template is provided for the Caddyfile, which could look like:

```Caddyfile
(cf_tls) {
    tls {
        dns cloudflare {env.CF_API_TOKEN}
    }
}

{% for site in web_sites %}
{{ site }} {
  import cf_tls
  root * {{ web_sites_root }}/{{ site }}/current
  file_server
  encode zstd gzip
  log
}
{% endfor %}
```

This sets up Caddy to use Cloudflare to do DNS-01 challenges with Lets Encrypt to get a TLS certificate for each `site in web_sites`.

#### Example: Homelab

The role can also be used to setup Caddy in a private network, like a homelab.

```yaml
- hosts: homelab
  become: true
  roles:
    - role: paultibbetts.caddy
      vars:
        caddy_caddyfile_template: &#34;{{ playbook_dir }}/templates/Caddyfile.j2&#34;
        caddy_install_method: download
        caddy_plugins:
          - github.com/caddy-dns/cloudflare
        caddy_manage_systemd_env_file: true
        caddy_systemd_env:
          CF_API_TOKEN: &#34;{{ vault_cf_api_token }}&#34;
```

```Caddyfile
{
        acme_dns cloudflare {env.CF_API_TOKEN}
}

service.homelab.example {
        reverse_proxy &lt;ip&gt;:&lt;port&gt;
}
```

where the Cloudflare plugin is configured once and is then used to do DNS-01 challenges for any TLS certificates Caddy requests. Note this style has been deprecated and you should do it per host, like the earlier example for a personal website.

DNS-01 challenges are solved by writing a TXT record to prove domain ownership. This means Lets Encrypt is able to issue certificates for domains it can&#39;t reach, which makes DNS-01 ideal when Caddy is running in a homelab.

## Molecule Scenarios

Ansible Molecule is a way to run tests for an Ansible role.

The role includes molecule scenarios that test it works on x86 as well as Arm, because I use the role on a Raspberry Pi 4, as well as scenarios for the Cloudflare plugin and the Caddyfile templating.

## In use

I use this role as part of the code that sets up the infrastructure for my website.

Terraform is used to provision the server, then Ansible is used to configure it, with this role setting up Caddy and the Caddyfile it uses.

The complete setup - Terraform, Ansible, Caddy, and the rest of the files - is documented at [infra.paultibbetts.uk](https://infra.paultibbetts.uk), where I explain how it all fits together.
</source:markdown></item><item>
      <title>Moved My Website From GitHub Pages to a Raspberry Pi</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/19/moved-my-website-from-github-pages-to-a-raspberry-pi/</link>
      <pubDate>Thu, 19 Feb 2026 17:40:37 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/19/moved-my-website-from-github-pages-to-a-raspberry-pi/</guid>
      <category>GitHub Pages</category>
      <category>Raspberry Pi</category>
      <description>My site was hosted on GitHub Pages.
It was simple and free, but it didn’t really feel like it was mine.
I’ve fixed that with a Raspberry Pi.</description>
      <content:encoded><![CDATA[<p>My site was hosted on GitHub Pages.</p>
<p>It was simple and free, but it didn&rsquo;t really feel like it was mine.</p>
<p>I&rsquo;ve fixed that with a Raspberry Pi.</p>
<h2 id="why-leave-github-pages">Why leave GitHub Pages?</h2>
<p><a href="https://docs.github.com/en/pages">GitHub Pages</a>
 is excellent at what it does.</p>
<p>You upload the code for your site to GitHub and they will build and host it for you.<br>
All for free.</p>
<p>So why leave?</p>
<h3 id="independence">Independence</h3>
<p>GitHub is owned by Microsoft, one of the biggest companies on the planet.</p>
<p>I have no problem with Microsoft, or using their economies of scale to do something for free that would otherwise cost me money, but ultimately I didn&rsquo;t feel very indie hosting my site on something I didn&rsquo;t control.</p>
<h3 id="sovereignty">Sovereignty</h3>
<p>GitHub and Microsoft are companies based in the USA, and those of us in Europe are re-considering our dependence on US tech.</p>
<p>Moving has brought my website closer to home - physically, legally, and ethically.</p>
<h3 id="portability">Portability</h3>
<p>The work to move from GitHub Pages to <em>anything else</em> has also made it easier to move it all again to another provider, should I ever choose to do so.</p>
<p>I&rsquo;ve taken the script to build the site out of GitHub Actions and made it part of the code itself. This decouples the site from any specific CI platform and makes both hosting and deployment replaceable.</p>
<h2 id="what-it-now-runs-on">What it now runs on</h2>
<p>There&rsquo;s no good reason for using a Raspberry Pi for this, I just thought it would be cool.</p>
<p>It is some of the cheapest hosting I could find from a UK-based provider, but there are cheaper options and hosting in Europe works just the same and is even cheaper.</p>
<p>The Pi is from <a href="https://www.mythic-beasts.com/">Mythic Beasts</a>
, a UK company based in Cambridge. It lives in London and runs on <a href="https://www.mythic-beasts.com/article/environment">green energy</a>
.</p>
<p>I provision it with Terraform, which lets me write the Pi down as code, and then I configure it using Ansible, another tool that sets up the software it runs.</p>
<p>Ansible installs and manages Caddy, which is a web server that has been configured to serve my site.</p>
<p>Deployment is still handled by GitHub Actions but now it goes to the Pi instead of Pages.</p>
<p>From the outside it all works exactly the same, but now it&rsquo;s on hardware I control, with a setup that&rsquo;s more portable than it was before.</p>
<h2 id="under-the-hood">Under the hood</h2>
<p>To make this all possible I ended up writing more code than I set out to.</p>
<p>There wasn&rsquo;t a Terraform provider for Mythic Beasts, so <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/09/2026-02-09-151500/">I wrote one</a>
.</p>
<p>For that to work it needed a client to talk to their APIs, so <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/06/2026-02-06-130000/">I wrote that too</a>
.</p>
<p>And to set up Caddy using Ansible <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/03/2026-02-03-175500/">I wrote my own role</a>
. There were already others that did this but none of them did exactly what I wanted, so I made my own.</p>
<p>None of this was really required, I could have moved to the Pi without writing any code, but the change from a manual setup to an automated one - driven by code - has forced me to treat the infrastructure of my website as a real system, that can change and evolve over time.</p>
<h2 id="what-this-means">What this means</h2>
<p>As a result my setup is now reproducible, portable and more independent.</p>
<p>I can see all the parts that make up my infrastructure and swap them out for alternatives. I&rsquo;m not tied down to a provider I can replace with a few lines of code.</p>
<p>And ultimately, there&rsquo;s no difference to visitors of the site, who still get the same experience as before.</p>
<h2 id="what-comes-next">What comes next</h2>
<p>Through doing this I can see that I might also be able to move my domain from Cloudflare to Mythic Beasts, another win for sovereignty.</p>
<p>If I did that, and moved the code hosting away from GitHub, I&rsquo;d have a website that&rsquo;s fully independent.</p>
<p>But that&rsquo;s for another time.</p>
<p>Over the next few posts I&rsquo;ll go over:</p>
<ul>
<li>how to use the Ansible role</li>
<li>making a provider for Terraform</li>
<li>the code that ties it all together</li>
<li>the site that explains it all in-depth</li>
</ul>
<h2 id="now">Now</h2>
<p>For now this post marks that this site&rsquo;s no longer hosted by GitHub Pages.</p>
<p>It&rsquo;s on a tiny computer, closer to home, set up by my own code.</p>
<p>That feels a bit more mine.</p>]]></content:encoded><source:markdown>
My site was hosted on GitHub Pages.

It was simple and free, but it didn&#39;t really feel like it was mine.

I&#39;ve fixed that with a Raspberry Pi.

&lt;!--more--&gt;

## Why leave GitHub Pages?

[GitHub Pages](https://docs.github.com/en/pages) is excellent at what it does.

You upload the code for your site to GitHub and they will build and host it for you.  
All for free.

So why leave?

### Independence

GitHub is owned by Microsoft, one of the biggest companies on the planet.

I have no problem with Microsoft, or using their economies of scale to do something for free that would otherwise cost me money, but ultimately I didn&#39;t feel very indie hosting my site on something I didn&#39;t control.

### Sovereignty

GitHub and Microsoft are companies based in the USA, and those of us in Europe are re-considering our dependence on US tech.

Moving has brought my website closer to home - physically, legally, and ethically.

### Portability

The work to move from GitHub Pages to _anything else_ has also made it easier to move it all again to another provider, should I ever choose to do so.

I&#39;ve taken the script to build the site out of GitHub Actions and made it part of the code itself. This decouples the site from any specific CI platform and makes both hosting and deployment replaceable.

## What it now runs on

There&#39;s no good reason for using a Raspberry Pi for this, I just thought it would be cool.

It is some of the cheapest hosting I could find from a UK-based provider, but there are cheaper options and hosting in Europe works just the same and is even cheaper.

The Pi is from [Mythic Beasts](https://www.mythic-beasts.com/), a UK company based in Cambridge. It lives in London and runs on [green energy](https://www.mythic-beasts.com/article/environment).

I provision it with Terraform, which lets me write the Pi down as code, and then I configure it using Ansible, another tool that sets up the software it runs.

Ansible installs and manages Caddy, which is a web server that has been configured to serve my site.

Deployment is still handled by GitHub Actions but now it goes to the Pi instead of Pages.

From the outside it all works exactly the same, but now it&#39;s on hardware I control, with a setup that&#39;s more portable than it was before.

## Under the hood

To make this all possible I ended up writing more code than I set out to.

There wasn&#39;t a Terraform provider for Mythic Beasts, so [I wrote one](/2026/02/09/2026-02-09-151500/).

For that to work it needed a client to talk to their APIs, so [I wrote that too](/2026/02/06/2026-02-06-130000/).

And to set up Caddy using Ansible [I wrote my own role](/2026/02/03/2026-02-03-175500/). There were already others that did this but none of them did exactly what I wanted, so I made my own.

None of this was really required, I could have moved to the Pi without writing any code, but the change from a manual setup to an automated one - driven by code - has forced me to treat the infrastructure of my website as a real system, that can change and evolve over time.

## What this means

As a result my setup is now reproducible, portable and more independent.

I can see all the parts that make up my infrastructure and swap them out for alternatives. I&#39;m not tied down to a provider I can replace with a few lines of code.

And ultimately, there&#39;s no difference to visitors of the site, who still get the same experience as before.

## What comes next

Through doing this I can see that I might also be able to move my domain from Cloudflare to Mythic Beasts, another win for sovereignty.

If I did that, and moved the code hosting away from GitHub, I&#39;d have a website that&#39;s fully independent.

But that&#39;s for another time.

Over the next few posts I&#39;ll go over:

- how to use the Ansible role
- making a provider for Terraform
- the code that ties it all together
- the site that explains it all in-depth

## Now

For now this post marks that this site&#39;s no longer hosted by GitHub Pages.

It&#39;s on a tiny computer, closer to home, set up by my own code.

That feels a bit more mine.
</source:markdown></item><item>
      <title>Launched: infra.paultibbetts.uk A documentation site for the infrastructure that …</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/15/2026-02-15-145458/</link>
      <pubDate>Sun, 15 Feb 2026 14:54:58 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/15/2026-02-15-145458/</guid>
      <category>Ansible</category>
      <category>Terraform</category>
      <category>meta</category>
      <description>Launched: infra.paultibbetts.uk A documentation site for the infrastructure that runs paultibbetts.uk.</description>
      <content:encoded><![CDATA[<p>Launched: <a href="https://infra.paultibbetts.uk/">infra.paultibbetts.uk</a>
</p>
<p>A documentation site for the infrastructure that runs <code>paultibbetts.uk</code>.</p>]]></content:encoded><source:markdown>
Launched: [infra.paultibbetts.uk](https://infra.paultibbetts.uk/)

A documentation site for the infrastructure that runs `paultibbetts.uk`.
</source:markdown></item><item>
      <title>Published: paultibbetts/terraform-provider-mythicbeasts A Terraform provider for …</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/09/2026-02-09-151500/</link>
      <pubDate>Mon, 09 Feb 2026 15:15:00 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/09/2026-02-09-151500/</guid>
      <category>Terraform</category>
      <category>go</category>
      <category>code</category>
      <category>Mythic Beasts</category>
      <description>Published: paultibbetts/terraform-provider-mythicbeasts A Terraform provider for Mythic Beasts.</description>
      <content:encoded><![CDATA[<p>Published: <a href="https://github.com/paultibbetts/terraform-provider-mythicbeasts">paultibbetts/terraform-provider-mythicbeasts</a>
</p>
<p>A Terraform provider for Mythic Beasts.</p>]]></content:encoded><source:markdown>
Published: [paultibbetts/terraform-provider-mythicbeasts](https://github.com/paultibbetts/terraform-provider-mythicbeasts)

A Terraform provider for Mythic Beasts.
</source:markdown></item><item>
      <title>Published: paultibbetts/mythic-beasts-client-go A client for the Mythic Beasts …</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/06/2026-02-06-130000/</link>
      <pubDate>Fri, 06 Feb 2026 13:00:00 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/06/2026-02-06-130000/</guid>
      <category>go</category>
      <category>code</category>
      <category>Mythic Beasts</category>
      <description>Published: paultibbetts/mythic-beasts-client-go A client for the Mythic Beasts APIs, written in Go.</description>
      <content:encoded><![CDATA[<p>Published: <a href="https://github.com/paultibbetts/mythicbeasts-client-go/">paultibbetts/mythic-beasts-client-go</a>
</p>
<p>A client for the Mythic Beasts APIs, written in Go.</p>]]></content:encoded><source:markdown>
Published: [paultibbetts/mythic-beasts-client-go](https://github.com/paultibbetts/mythicbeasts-client-go/)

A client for the Mythic Beasts APIs, written in Go.
</source:markdown></item><item>
      <title>Published: paultibbetts/ansible-role-caddy An Ansible role to install and …</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/03/2026-02-03-175500/</link>
      <pubDate>Tue, 03 Feb 2026 17:55:00 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2026/02/03/2026-02-03-175500/</guid>
      <category>Ansible</category>
      <category>code</category>
      <category>Caddy</category>
      <description>Published: paultibbetts/ansible-role-caddy An Ansible role to install and operate Caddy web server.</description>
      <content:encoded><![CDATA[<p>Published: <a href="https://github.com/paultibbetts/ansible-role-caddy">paultibbetts/ansible-role-caddy</a>
</p>
<p>An Ansible role to install and operate Caddy web server.</p>]]></content:encoded><source:markdown>
Published: [paultibbetts/ansible-role-caddy](https://github.com/paultibbetts/ansible-role-caddy)

An Ansible role to install and operate Caddy web server.
</source:markdown></item><item>
      <title>Favourite Games of 2025</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/12/31/favourite-games-of-2025/</link>
      <pubDate>Wed, 31 Dec 2025 16:00:00 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/12/31/favourite-games-of-2025/</guid>
      <category>gaming</category>
      <description>Last year I wrote my own take on the Steam Awards , and in 2025 I played less games, so instead of pretending otherwise, this is a list of my favourites from this year.</description>
      <content:encoded><![CDATA[<p>Last year I wrote <a href="https://micro.paultibbetts.uk/2025/01/03/pc-games-i-liked-in.html">my own take on the Steam Awards</a>
, and in 2025 I played less games, so instead of pretending otherwise, this is a list of my favourites from this year.</p>
<h2 id="dispatch">Dispatch</h2>
<blockquote>
<p>Single-player story-driven narrative.</p>
</blockquote>
<p><a href="https://store.steampowered.com/app/2592160/Dispatch/"><img src="https://shared.fastly.steamstatic.com/store_item_assets/steam/apps/2592160/header.jpg" alt="Dispatch hero image" title="Dispatch hero image">
</a>
</p>
<p>Dispatch might be <em>the</em> funniest game I&rsquo;ve ever played. It&rsquo;s extremely well written, absurdly well voice acted and expertly made by the team that used to be Telltale Games.</p>
<p>It gives a real glimpse into the life of a superhero dispatcher and kept me entertained the whole way through.</p>
<p>I&rsquo;ll be playing whatever <a href="https://www.adhocla.com/">Adhoc Studios</a>
 makes next. I hope it&rsquo;s Wolf Among Us 2 or another season of Dispatch. Either way, I&rsquo;m in.</p>
<p>It&rsquo;s rated <a href="https://www.protondb.com/app/2592160">Platinum</a>
 on ProtonDB, so it plays great on Linux.</p>
<p><a href="https://store.steampowered.com/app/2592160/Dispatch/">Steam</a>
</p>
<h2 id="arc-raiders">Arc Raiders</h2>
<blockquote>
<p>Third-person PvPvE extraction-shooter.</p>
</blockquote>
<p><a href="https://store.steampowered.com/app/1808500/ARC_Raiders/"><img src="https://steamcdn-a.akamaihd.net/steam/apps/1808500/header.jpg" alt="Arc Raiders hero image" title="Arc Raiders hero image">
</a>
</p>
<p>I wasn&rsquo;t expecting to like Arc Raiders as much as I did. It makes the sweaty extraction-shooter genre more accessible, even if it is still pretty sweaty.</p>
<p>Escape from Tarkov was a bit too much for me; Arc Raiders has a much better balance. It&rsquo;s not for everyone, but it&rsquo;s that well done you might enjoy it even if you don&rsquo;t like similar games.</p>
<p>Whilst you can get shot by other players, it&rsquo;s the Arc that are the real threat, and this mix of danger, plus the proximity voice chat, make it unlike any other game out right now.</p>
<p>It won The Game Awards&rsquo; <a href="https://nitter.net/ARCRaidersGame/status/1999420050785042508?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1999420050785042508%7Ctwgr%5E%7Ctwcon%5Es1_&amp;ref_url=https%3A%2F%2Fwww.gamersheroes.com%2Fgaming-news%2Farc-raiders-best-multiplayer-game-of-2025-at-the-game-awards%2F">Multiplayer Game of the Year</a>
, so don&rsquo;t just take my word for it.</p>
<p><a href="https://www.embark-studios.com/">Embark</a>
&rsquo;s previous game, <a href="https://www.reachthefinals.com/">The Finals</a>
, made it onto last year&rsquo;s list. Maybe we&rsquo;ll see a third game on next year&rsquo;s list?</p>
<p>It&rsquo;s also rated <a href="https://www.protondb.com/app/1808500">Platinum</a>
 on ProtonDB.</p>
<p><a href="https://store.steampowered.com/app/1808500/ARC_Raiders/">Steam</a>
</p>
<h2 id="battlefield-6">Battlefield 6</h2>
<blockquote>
<p>PvP FPS explosion-simulator.</p>
</blockquote>
<p><a href="https://store.steampowered.com/app/2807960/Battlefield_6/"><img src="https://shared.fastly.steamstatic.com/store_item_assets/steam/apps/2807960/c12d12ce3c7d217398d3fcad77427bfc9d57c570/header.jpg" alt="Battlefield 6 hero image" title="Battlefield 6 hero image">
</a>
</p>
<p>Battlefield is back!</p>
<p>I&rsquo;ve played BF games ever since the first one, set all the way back in World War 2. I didn&rsquo;t buy BF5, and wasn&rsquo;t that impressed by 2042, so I was happy to see Battlefield make a comeback with 6.</p>
<p>And it was quite the comeback. Two of my friends bought new computers just to play it, and its launch weekend was the most active my little Discord server&rsquo;s ever been.</p>
<p>It&rsquo;s moved away from the slower, more tactical feel of its earlier entries though. If that older style is what you&rsquo;re after, games like <a href="https://www.joinsquad.com/">Squad</a>
 and <a href="https://www.hellletloose.com/">Hell Let Loose</a>
 now occupy that space.</p>
<p>With 6, Battlefield has settled into being a faster, more accessible squad-based shooter - and judged on those terms it&rsquo;s an excellent game. No wonder it was <a href="https://bsky.app/profile/matpiscatella.bsky.social/post/3ma6t4pndwc2w">the best selling FPS of the year</a>
.</p>
<p>Its only letdown is that it needs <a href="https://www.ea.com/games/battlefield/battlefield-6/news/secure-boot-information">Secure boot</a>
 to run the anti-cheat, which means you can&rsquo;t play it on Linux, so it gets a <a href="https://www.protondb.com/app/2807960">borked</a>
 rating on ProtonDB.</p>
<p><a href="https://store.steampowered.com/app/2807960/Battlefield_6/">Steam</a>
</p>
<h2 id="dying-light-the-beast">Dying Light: The Beast</h2>
<blockquote>
<p>First-person single-player/co-op story-driven parkour-action-zombie-killing.</p>
</blockquote>
<p><a href="https://store.steampowered.com/app/3008130/Dying_Light_The_Beast/"><img src="https://steamcdn-a.akamaihd.net/steam/apps/3008130/header.jpg" alt="Dying Light: The Beast hero image" title="Dying Light: The Beast hero image">
</a>
</p>
<p>The Beast is the best of the Dying Light series. Techland have improved on every part of the experience. And best of all, since I bought the deluxe edition of the second game, I got it for free!</p>
<p>It looks awesome, it runs well, and it&rsquo;s loads of fun. I&rsquo;m still playing through it in co-op, which lets up to 4 people be Kyle Crane, the protagonist from the first game.</p>
<p>Another <a href="https://www.protondb.com/app/3008130">Platinum</a>
 rating on ProtonDB.</p>
<p><a href="https://store.steampowered.com/app/3008130/Dying_Light_The_Beast/">Steam</a>
</p>
<h2 id="jump-space">Jump Space</h2>
<blockquote>
<p>Mission-based co-op PvE for up to 4 players.</p>
</blockquote>
<p><a href="https://store.steampowered.com/app/1757300/Jump_Space/"><img src="https://images.igdb.com/igdb/image/upload/t_1080p/ar3ysa.webp" alt="Jump Ship hero image" title="Jump Ship hero image">
</a>
</p>
<p>(Previously known as Jump Ship)</p>
<p>It&rsquo;s still in early access but Jump Space is more than playable right now. It&rsquo;s the chosen game for game night for us where once a week we crew a spaceship, fight some robots and <em>one of us</em> expertly puts out fires the others don&rsquo;t even notice because they&rsquo;re busy shooting space lasers.</p>
<p>Yet another <a href="https://www.protondb.com/app/1757300">Platinum</a>
 rating on ProtonDB.</p>
<p><a href="https://store.steampowered.com/app/1757300/Jump_Space/">Steam</a>
</p>
<h2 id="anno-117-pax-romana">Anno 117: Pax Romana</h2>
<blockquote>
<p>Single-player city-builder.</p>
</blockquote>
<p><a href="https://store.steampowered.com/app/3274580/Anno_117_Pax_Romana/"><img src="https://shared.fastly.steamstatic.com/store_item_assets/steam/apps/3274580/88dbcce1c7630e2d4334fa25e835968e57e0ebfe/header.jpg?t=1764100083" alt="Anno 117: Pax Romana hero image" title="Anno 117: Pax Romana hero image">
</a>
</p>
<p>Anno 117 is apparently very similar to Anno 1800 but in a different setting with different people, which is exactly what I wanted.</p>
<p>You build up an island, or a few of them, from humble beginnings all the way up to a mighty city, managing buildings, production, and trade.</p>
<p>It&rsquo;s a more relaxing game than the other ones on this list. There is a bit of combat, but its main appeal to me is the city building and the setting.</p>
<p>It gets only a <a href="https://www.protondb.com/app/3274580">Gold</a>
 rating on ProtonDB, which I can&rsquo;t confirm, I haven&rsquo;t yet tried it on Linux.</p>
<p><a href="https://store.steampowered.com/app/3274580/Anno_117_Pax_Romana/">Steam</a>
</p>
<h2 id="rv-there-yet">RV There Yet?</h2>
<blockquote>
<p>Co-op friendslop.</p>
</blockquote>
<p><a href="https://store.steampowered.com/app/3949040/RV_There_Yet/"><img src="https://shared.fastly.steamstatic.com/store_item_assets/steam/apps/3949040/cae24b4ed7f4531be51f0d63f785b7d253f92dc3/header.jpg?t=1766020280" alt="RV There Yet? hero image" title="RV There Yet? hero image">
</a>
</p>
<p>Friendslop is a new word for me. It&rsquo;s meant to define the low-stakes co-op games that are sold at low prices so that everyone in the group can afford to play them.</p>
<p>I don&rsquo;t know how true that definition is, I&rsquo;ve only ever played it with one person, who I can safely say is not qualified to drive an RV, whilst drinking a beer, smoking a cigarette, cooking burgers, being chased by a bear.</p>
<p>I almost put <a href="https://peak-game.com/">Peak</a>
 here, but so far I&rsquo;ve had more fun in RV There Yet?.</p>
<p>This game gets the last <a href="https://www.protondb.com/app/3949040">Platinum</a>
 rating on ProtonDB for games released in 2025, because this is the last of that list.</p>
<p><a href="https://store.steampowered.com/app/3949040/RV_There_Yet/">Steam</a>
</p>
<h2 id="not-released-in-2025">Not released in 2025</h2>
<p>These weren&rsquo;t released this year; they made the list anyway.</p>
<h3 id="the-beginners-guide">The Beginner&rsquo;s Guide</h3>
<blockquote>
<p>Single-player narrative-exploration-art.</p>
</blockquote>
<p><a href="https://store.steampowered.com/app/303210/The_Beginners_Guide/"><img src="https://shared.fastly.steamstatic.com/store_item_assets/steam/apps/303210/header.jpg?t=1765833998" alt="The Beginner&rsquo;s Guide hero image" title="The Beginner&#39;s Guide hero image">
</a>
</p>
<p>The Beginner&rsquo;s Guide took me a decade to play, and I&rsquo;m glad I finally did. It came out in 2015 and I got it this year as a Christmas present (thank you!). I played and finished it that same day.</p>
<p>It&rsquo;s made by the same developer as <a href="https://www.stanleyparable.com/">The Stanley Parable</a>
, which I really enjoyed, and it was a very different experience for this one. Whereas TSP was an interesting game, I&rsquo;d describe TBG as art. It doesn&rsquo;t involve much gameplay, and instead uses the medium of video games to do interactive storytelling.</p>
<p>Despite the rest of my list, I think you might need to view video games as an art-form, not just zombie-killing-space-laser-shooting-explosion-simulators, to really appreciate this one.</p>
<p>It gets a <a href="https://www.protondb.com/app/303210">Platinum</a>
 rating on ProtonDB and was natively released on Linux. I had to <a href="https://micro.paultibbetts.uk/2025/12/31/how-to-play-the-beginners.html">change a setting</a>
 to make it playable.</p>
<p><a href="https://store.steampowered.com/app/303210/The_Beginners_Guide/">Steam</a>
</p>
<h3 id="ministry-of-broadcast">Ministry of Broadcast</h3>
<blockquote>
<p>Single-player platformer</p>
</blockquote>
<p><a href="https://store.steampowered.com/app/874040/Ministry_of_Broadcast/"><img src="https://images.igdb.com/igdb/image/upload/t_1080p/ardda.webp" alt="Ministry of Broadcast hero image" title="Ministry of Broadcast hero image">
</a>
</p>
<p>Another Christmas present I&rsquo;m really enjoying. It&rsquo;s perfectly suited for the Steam Deck, so it also works well with a controller, and is the only game on this list where I get to lean back in my chair instead of forward.</p>
<p>It&rsquo;s inspired by George Orwell&rsquo;s 1984, which might be why I&rsquo;m liking it so much. Though I don&rsquo;t remember Winston falling off as many buildings as I have.</p>
<p><a href="https://www.protondb.com/app/874040">Platinum</a>
 rated on ProtonDB.</p>
<p><a href="https://store.steampowered.com/app/874040/Ministry_of_Broadcast/">Steam</a>
</p>
<h3 id="baldurs-gate-3">Baldur&rsquo;s Gate 3</h3>
<blockquote>
<p>Single-player / online co-op party-based RPG</p>
</blockquote>
<p><a href="https://store.steampowered.com/app/1086940/Baldurs_Gate_3/"><img src="https://shared.fastly.steamstatic.com/store_item_assets/steam/apps/1086940/48a2fcbda8565bb45025e98fd8ebde8a7203f6a0/header.jpg?t=1765505948" alt="Baldur&rsquo;s Gate 3 hero image" title="Baldur&#39;s Gate 3 hero image">
</a>
</p>
<p>I am still playing Baldur&rsquo;s Gate 3. I am still in Act 1. I know.</p>
<p>This is such a good game that I can&rsquo;t just turn it on mindlessly for 20 minutes. I need to settle in for an afternoon and play for a few hours at a time - to get into the right mind-frame and settle into being a powerful sorcerer.</p>
<p>Or whatever I am, I don&rsquo;t know, I&rsquo;ve never played D&amp;D before. My character keeps wanting to kill things, and I am happy to oblige.</p>
<p>I am sorry bard, I don&rsquo;t know what came over me.</p>
<p><a href="https://www.protondb.com/app/1086940">Gold</a>
 rated on ProtonDB, which is weird because it has a <a href="https://larian.com/support/faqs/steam-deck-native-version_121">native Linux build</a>
.</p>
<p><a href="https://store.steampowered.com/app/1086940/Baldurs_Gate_3/">Steam</a>
</p>]]></content:encoded><source:markdown>
Last year I wrote [my own take on the Steam Awards](https://micro.paultibbetts.uk/2025/01/03/pc-games-i-liked-in.html), and in 2025 I played less games, so instead of pretending otherwise, this is a list of my favourites from this year.

&lt;!--more--&gt;

## Dispatch

&gt; Single-player story-driven narrative.

[![Dispatch hero image](https://shared.fastly.steamstatic.com/store_item_assets/steam/apps/2592160/header.jpg &#34;Dispatch hero image&#34;)](https://store.steampowered.com/app/2592160/Dispatch/)

Dispatch might be _the_ funniest game I&#39;ve ever played. It&#39;s extremely well written, absurdly well voice acted and expertly made by the team that used to be Telltale Games.

It gives a real glimpse into the life of a superhero dispatcher and kept me entertained the whole way through.

I&#39;ll be playing whatever [Adhoc Studios](https://www.adhocla.com/) makes next. I hope it&#39;s Wolf Among Us 2 or another season of Dispatch. Either way, I&#39;m in.

It&#39;s rated [Platinum](https://www.protondb.com/app/2592160) on ProtonDB, so it plays great on Linux.

[Steam](https://store.steampowered.com/app/2592160/Dispatch/)

## Arc Raiders

&gt; Third-person PvPvE extraction-shooter.

[![Arc Raiders hero image](https://steamcdn-a.akamaihd.net/steam/apps/1808500/header.jpg &#34;Arc Raiders hero image&#34;)](https://store.steampowered.com/app/1808500/ARC_Raiders/)

I wasn&#39;t expecting to like Arc Raiders as much as I did. It makes the sweaty extraction-shooter genre more accessible, even if it is still pretty sweaty.

Escape from Tarkov was a bit too much for me; Arc Raiders has a much better balance. It&#39;s not for everyone, but it&#39;s that well done you might enjoy it even if you don&#39;t like similar games.

Whilst you can get shot by other players, it&#39;s the Arc that are the real threat, and this mix of danger, plus the proximity voice chat, make it unlike any other game out right now.

It won The Game Awards&#39; [Multiplayer Game of the Year](https://nitter.net/ARCRaidersGame/status/1999420050785042508?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1999420050785042508%7Ctwgr%5E%7Ctwcon%5Es1_&amp;ref_url=https%3A%2F%2Fwww.gamersheroes.com%2Fgaming-news%2Farc-raiders-best-multiplayer-game-of-2025-at-the-game-awards%2F), so don&#39;t just take my word for it.

[Embark](https://www.embark-studios.com/)&#39;s previous game, [The Finals](https://www.reachthefinals.com/), made it onto last year&#39;s list. Maybe we&#39;ll see a third game on next year&#39;s list?

It&#39;s also rated [Platinum](https://www.protondb.com/app/1808500) on ProtonDB.

[Steam](https://store.steampowered.com/app/1808500/ARC_Raiders/)

## Battlefield 6

&gt; PvP FPS explosion-simulator.

[![Battlefield 6 hero image](https://shared.fastly.steamstatic.com/store_item_assets/steam/apps/2807960/c12d12ce3c7d217398d3fcad77427bfc9d57c570/header.jpg &#34;Battlefield 6 hero image&#34;)](https://store.steampowered.com/app/2807960/Battlefield_6/)

Battlefield is back!

I&#39;ve played BF games ever since the first one, set all the way back in World War 2. I didn&#39;t buy BF5, and wasn&#39;t that impressed by 2042, so I was happy to see Battlefield make a comeback with 6.

And it was quite the comeback. Two of my friends bought new computers just to play it, and its launch weekend was the most active my little Discord server&#39;s ever been.

It&#39;s moved away from the slower, more tactical feel of its earlier entries though. If that older style is what you&#39;re after, games like [Squad](https://www.joinsquad.com/) and [Hell Let Loose](https://www.hellletloose.com/) now occupy that space.

With 6, Battlefield has settled into being a faster, more accessible squad-based shooter - and judged on those terms it&#39;s an excellent game. No wonder it was [the best selling FPS of the year](https://bsky.app/profile/matpiscatella.bsky.social/post/3ma6t4pndwc2w).

Its only letdown is that it needs [Secure boot](https://www.ea.com/games/battlefield/battlefield-6/news/secure-boot-information) to run the anti-cheat, which means you can&#39;t play it on Linux, so it gets a [borked](https://www.protondb.com/app/2807960) rating on ProtonDB.

[Steam](https://store.steampowered.com/app/2807960/Battlefield_6/)

## Dying Light: The Beast

&gt; First-person single-player/co-op story-driven parkour-action-zombie-killing.

[![Dying Light: The Beast hero image](https://steamcdn-a.akamaihd.net/steam/apps/3008130/header.jpg &#34;Dying Light: The Beast hero image&#34;)](https://store.steampowered.com/app/3008130/Dying_Light_The_Beast/)

The Beast is the best of the Dying Light series. Techland have improved on every part of the experience. And best of all, since I bought the deluxe edition of the second game, I got it for free!

It looks awesome, it runs well, and it&#39;s loads of fun. I&#39;m still playing through it in co-op, which lets up to 4 people be Kyle Crane, the protagonist from the first game.

Another [Platinum](https://www.protondb.com/app/3008130) rating on ProtonDB.

[Steam](https://store.steampowered.com/app/3008130/Dying_Light_The_Beast/)

## Jump Space

&gt; Mission-based co-op PvE for up to 4 players.

[![Jump Ship hero image](https://images.igdb.com/igdb/image/upload/t_1080p/ar3ysa.webp &#34;Jump Ship hero image&#34;)](https://store.steampowered.com/app/1757300/Jump_Space/)

(Previously known as Jump Ship)

It&#39;s still in early access but Jump Space is more than playable right now. It&#39;s the chosen game for game night for us where once a week we crew a spaceship, fight some robots and _one of us_ expertly puts out fires the others don&#39;t even notice because they&#39;re busy shooting space lasers.

Yet another [Platinum](https://www.protondb.com/app/1757300) rating on ProtonDB.

[Steam](https://store.steampowered.com/app/1757300/Jump_Space/)

## Anno 117: Pax Romana

&gt; Single-player city-builder.

[![Anno 117: Pax Romana hero image](https://shared.fastly.steamstatic.com/store_item_assets/steam/apps/3274580/88dbcce1c7630e2d4334fa25e835968e57e0ebfe/header.jpg?t=1764100083 &#34;Anno 117: Pax Romana hero image&#34;)](https://store.steampowered.com/app/3274580/Anno_117_Pax_Romana/)

Anno 117 is apparently very similar to Anno 1800 but in a different setting with different people, which is exactly what I wanted.

You build up an island, or a few of them, from humble beginnings all the way up to a mighty city, managing buildings, production, and trade.

It&#39;s a more relaxing game than the other ones on this list. There is a bit of combat, but its main appeal to me is the city building and the setting.

It gets only a [Gold](https://www.protondb.com/app/3274580) rating on ProtonDB, which I can&#39;t confirm, I haven&#39;t yet tried it on Linux.

[Steam](https://store.steampowered.com/app/3274580/Anno_117_Pax_Romana/)

## RV There Yet?

&gt; Co-op friendslop.

[![RV There Yet? hero image](https://shared.fastly.steamstatic.com/store_item_assets/steam/apps/3949040/cae24b4ed7f4531be51f0d63f785b7d253f92dc3/header.jpg?t=1766020280 &#34;RV There Yet? hero image&#34;)](https://store.steampowered.com/app/3949040/RV_There_Yet/)

Friendslop is a new word for me. It&#39;s meant to define the low-stakes co-op games that are sold at low prices so that everyone in the group can afford to play them.

I don&#39;t know how true that definition is, I&#39;ve only ever played it with one person, who I can safely say is not qualified to drive an RV, whilst drinking a beer, smoking a cigarette, cooking burgers, being chased by a bear.

I almost put [Peak](https://peak-game.com/) here, but so far I&#39;ve had more fun in RV There Yet?.

This game gets the last [Platinum](https://www.protondb.com/app/3949040) rating on ProtonDB for games released in 2025, because this is the last of that list.

[Steam](https://store.steampowered.com/app/3949040/RV_There_Yet/)

## Not released in 2025

These weren&#39;t released this year; they made the list anyway.

### The Beginner&#39;s Guide

&gt; Single-player narrative-exploration-art.

[![The Beginner&#39;s Guide hero image](https://shared.fastly.steamstatic.com/store_item_assets/steam/apps/303210/header.jpg?t=1765833998 &#34;The Beginner&#39;s Guide hero image&#34;)](https://store.steampowered.com/app/303210/The_Beginners_Guide/)

The Beginner&#39;s Guide took me a decade to play, and I&#39;m glad I finally did. It came out in 2015 and I got it this year as a Christmas present (thank you!). I played and finished it that same day.

It&#39;s made by the same developer as [The Stanley Parable](https://www.stanleyparable.com/), which I really enjoyed, and it was a very different experience for this one. Whereas TSP was an interesting game, I&#39;d describe TBG as art. It doesn&#39;t involve much gameplay, and instead uses the medium of video games to do interactive storytelling.

Despite the rest of my list, I think you might need to view video games as an art-form, not just zombie-killing-space-laser-shooting-explosion-simulators, to really appreciate this one.

It gets a [Platinum](https://www.protondb.com/app/303210) rating on ProtonDB and was natively released on Linux. I had to [change a setting](https://micro.paultibbetts.uk/2025/12/31/how-to-play-the-beginners.html) to make it playable.

[Steam](https://store.steampowered.com/app/303210/The_Beginners_Guide/)

### Ministry of Broadcast

&gt; Single-player platformer

[![Ministry of Broadcast hero image](https://images.igdb.com/igdb/image/upload/t_1080p/ardda.webp &#34;Ministry of Broadcast hero image&#34;)](https://store.steampowered.com/app/874040/Ministry_of_Broadcast/)

Another Christmas present I&#39;m really enjoying. It&#39;s perfectly suited for the Steam Deck, so it also works well with a controller, and is the only game on this list where I get to lean back in my chair instead of forward.

It&#39;s inspired by George Orwell&#39;s 1984, which might be why I&#39;m liking it so much. Though I don&#39;t remember Winston falling off as many buildings as I have.

[Platinum](https://www.protondb.com/app/874040) rated on ProtonDB.

[Steam](https://store.steampowered.com/app/874040/Ministry_of_Broadcast/)

### Baldur&#39;s Gate 3

&gt; Single-player / online co-op party-based RPG

[![Baldur&#39;s Gate 3 hero image](https://shared.fastly.steamstatic.com/store_item_assets/steam/apps/1086940/48a2fcbda8565bb45025e98fd8ebde8a7203f6a0/header.jpg?t=1765505948 &#34;Baldur&#39;s Gate 3 hero image&#34;)](https://store.steampowered.com/app/1086940/Baldurs_Gate_3/)

I am still playing Baldur&#39;s Gate 3. I am still in Act 1. I know.

This is such a good game that I can&#39;t just turn it on mindlessly for 20 minutes. I need to settle in for an afternoon and play for a few hours at a time - to get into the right mind-frame and settle into being a powerful sorcerer.

Or whatever I am, I don&#39;t know, I&#39;ve never played D&amp;D before. My character keeps wanting to kill things, and I am happy to oblige.

I am sorry bard, I don&#39;t know what came over me.

[Gold](https://www.protondb.com/app/1086940) rated on ProtonDB, which is weird because it has a [native Linux build](https://larian.com/support/faqs/steam-deck-native-version_121).

[Steam](https://store.steampowered.com/app/1086940/Baldurs_Gate_3/)
</source:markdown></item><item>
      <title>Pangolin on a Pi</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/12/30/pangolin-on-a-pi/</link>
      <pubDate>Tue, 30 Dec 2025 15:10:00 +0000</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/12/30/pangolin-on-a-pi/</guid>
      <category>homelab</category>
      <category>IPv6</category>
      <category>Pangolin</category>
      <category>Raspberry Pi</category>
      <description>This post is not a step-by-step guide. It’s a record of what I tried and what I learned getting Pangolin to run on a Raspberry Pi with NFS storage in an IPv6-only environment.
TLDR: if you want to run Pangolin hassle-free, use a VPS with local SSD storage on an IPv4 network.</description>
      <content:encoded><![CDATA[<p>This post is not a step-by-step guide. It&rsquo;s a record of what I tried and what I learned getting Pangolin to run on a Raspberry Pi with NFS storage in an IPv6-only environment.</p>
<p><strong>TLDR</strong>: if you want to run Pangolin hassle-free, use a VPS with local SSD storage on an IPv4 network.</p>
<h2 id="pangolin">Pangolin</h2>
<p><a href="https://digpangolin.com/">Pangolin</a>
 is a &ldquo;secure access platform&rdquo; for safely exposing internal services. I&rsquo;m using it to let friends and family reach a few apps in my homelab without giving them the keys to everything.</p>
<p>It&rsquo;s a free, open-source alternative to <a href="https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/">Cloudflare Tunnel</a>
 for those who like independence from Cloudflare, want to self-host the stack themselves, or prefer not to rely on a third-party network for media streaming, which is not its intended use-case.</p>
<p>If none of those things bother you then you&rsquo;ll be fine using Cloudflare Tunnel or one of the alternatives. Read the terms and conditions first, media streaming is a bit of a grey area, I am not a lawyer and that wasn&rsquo;t legal advice.</p>
<h2 id="alternatives">Alternatives</h2>
<p>I already use <a href="https://www.wireguard.com/">Wireguard</a>
 as a VPN, and I still use it myself to get into my home network. VPNs work, but they&rsquo;re not fun to manage for friends and family and I wanted the ability to lock them down to certain apps.</p>
<p>There&rsquo;s also <a href="https://tailscale.com/">Tailscale</a>
, <a href="https://netbird.io/">Netbird</a>
, and <a href="https://www.zerotier.com/">ZeroTier</a>
, which use Wireguard to make a mesh network, but that&rsquo;s more of the same VPN solution. I&rsquo;d need to set up profiles and install clients on devices, and I wanted something easier. Tailscale now has its own <a href="https://tailscale.com/kb/1223/funnel">Funnel</a>
 project, but by the time I&rsquo;d seen it I was already setting up Pangolin.</p>
<p>The last alternative to mention is that you can achieve a similar result by renting a server, adding it to your VPN, and setting up a reverse proxy on it. But then you&rsquo;d need something else to make it secure, and you end up back at Pangolin.</p>
<h2 id="hosting">Hosting</h2>
<p>Pangolin needs to be hosted outside your network, so I needed to rent a server. It doesn&rsquo;t need to be a big one, Pangolin can work with 1 vCPU and 1GB of RAM, but ideally it would be close to your internal network and the users that want to get into it.</p>
<p>I live in the UK, so my options were UK, Ireland or Europe. UK-based hosting turned out to be expensive, with Krystal charging <a href="https://krystal.io/cloud-vps">£12 per month</a>
, whereas Hetzner, based in Germany, offers the same specs for <a href="https://www.hetzner.com/cloud">£3.97</a>
.</p>
<p>I should have gone with Hetzner. The extra latency would likely have been ~20ms and for Pangolin&rsquo;s use case - web apps and media streaming - that wouldn&rsquo;t have been noticeable.</p>
<p>But at the time I wanted my server to be based in the UK, so I carried on looking, until I found <a href="https://www.mythic-beasts.com/">Mythic Beasts</a>
. Not only had they recently <a href="https://www.mythic-beasts.com/blog/2025/02/25/supporting-the-open-rights-group/">donated to the Open Rights Group</a>
, an organisation I also support, but they have a cluster of Raspberry Pis you can use.</p>
<p>My search was over. I wanted Pangolin on a Pi. I did some initial testing, and the early signs were good, so I signed up for a year.</p>
<h2 id="raspberry-pi">Raspberry Pi</h2>
<p>The Mythic Beasts Pis come with two obstacles to running Pangolin. The first is that they are IPv6 only, and the second is the storage they use. One of these involves a little workaround and the other makes it a bad choice for hosting Pangolin.</p>
<h3 id="nfs-storage">NFS Storage</h3>
<p>Pangolin is actually more of a stack, with Pangolin as the control plane, Traefik doing reverse proxying, Gerbil running the tunnel and a thing called Badger to do the middleware that checks for authentication. The easiest way to run it all is with Docker Compose, and this is the first hurdle to running Pangolin on a Mythic Beasts Pi.</p>
<p>The Pis only have network storage available, and Docker doesn&rsquo;t like that because NFS doesn&rsquo;t provide the kind of guarantees Docker wants from a filesystem. Docker <em>can</em> work with NFS, but only using the <code>vfs</code> storage driver. Unlike the default <code>overlay2</code> driver, which uses a layered filesystem, <code>vfs</code> doesn&rsquo;t share layers, so every image consumes more space than it should.</p>
<p>So not only does NFS end up being slow, copying over lots of small files, it means the Docker images can&rsquo;t do any of the space saving that would normally happen. I have 50GB of storage and it&rsquo;s at 70% usage, even though the final images only take up 2GB, all because of the way <code>vfs</code> does its layering.</p>
<p>Did I mention that it&rsquo;s slow? Starting up the containers takes about 15 minutes. Turning them off isn&rsquo;t fast either. And upgrades? You won&rsquo;t have room for the new images, so you&rsquo;ll have to turn everything off to remove the old ones before you can start downloading the new ones. This means upgrades can take about an hour, with Pangolin down the entire time.</p>
<h3 id="ipv6">IPv6</h3>
<p>The internet&rsquo;s ran out of IPv4 addresses, by the way, so the Pis are IPv6 only. This means you have to do a bit of extra work to make Pangolin available for everyone.</p>
<p>IPv6 is the future but support for it is still mixed, so Mythic Beasts have an IPv4 to IPv6 proxy to allow users stuck on IPv4-only networks to get to your server. To use it you configure the proxy to forward requests to your domain on to the IPv6 address of the Pi.</p>
<p>The other end of the tunnel, the one in your home network, is managed by a service called Newt. It needs to speak to Gerbil on the Pi, and I was not successful getting it to run with the default <code>pangolin.example.com</code> address I was using.</p>
<p>Newt is configured by pointing it at Pangolin, which advertises the Gerbil endpoint it should connect to. Maybe it was Pangolin, or the proxy, or even something else; I could not get it to work. To make some progress I changed the <code>base_endpoint</code> in <code>config/config.yml</code>. First I tried the IPv6 address of the Pi, but that still didn&rsquo;t work, so I created an AAAA record for Gerbil and pointed it at the Pi. This would do the same thing as the CNAME I&rsquo;d used for Pangolin, except that went through the proxy and maybe that was messing things up. This time, it worked.</p>
<p>Mythic Beasts <a href="https://www.mythic-beasts.com/support/topics/proxy">state their proxy</a>
:</p>
<blockquote>
<p>will relay traffic for common services, such as HTTP and HTTPS</p>
</blockquote>
<p>and I have a suspicion that Wireguard doesn&rsquo;t count as a &ldquo;common service&rdquo;.</p>
<h2 id="home-network">Home network</h2>
<p>The final hurdle to getting this setup to work was my own network. Since the proxy doesn&rsquo;t seem to forward Wireguard traffic, and I have to connect directly to the Pi with IPv6, my network needs to let me do that.</p>
<p>This means I need at least IPv6 egress. That doesn&rsquo;t mean full IPv6 support everywhere, just enough to have a usable outbound path. I tried a bunch of different things to get it to work, and some of my changes actually made things worse, so what follows is what worked for me, in case your setup is similar, most likely it isn&rsquo;t.</p>
<h3 id="isp-router">ISP router</h3>
<p>I&rsquo;m still using the router my ISP gave me, which has very basic IPv6 support. I&rsquo;m with Vodafone and it&rsquo;s the standard &ldquo;WiFi hub&rdquo; they gave out several years ago.</p>
<p>The right combination of buttons to press is under <code>Settings -&gt; Local Network</code> and you want:</p>
<p><code>IPv6</code> <strong>Enabled</strong></p>
<p><code>IPv6 ULA</code> <strong>Disabled</strong></p>
<p>The <code>IPv6</code> toggle enables Router Advertisements (RA) from the router and this gives hosts a global IPv6 address, and also tells them what the default route for IPv6 traffic is, which is through this router.</p>
<p>The <code>IPv6 ULA</code> toggle disables Unique Local Addresses, which is a private IPv6 space used for hosts to speak to each other over IPv6, and is not necessary for Newt to reach Gerbil, so it can be disabled.</p>
<h3 id="mesh-router">Mesh router</h3>
<p>I&rsquo;ve also got a pair of mesh devices I use as a wireless bridge between floors, since I&rsquo;m not able to run an ethernet cable.</p>
<p>They&rsquo;re definitely in bridge mode. However, in the admin panel under <code>Settings -&gt; Network -&gt; Advanced</code>, IPv6 was set to &ldquo;Auto Configuration&rdquo;, meaning they were also doing SLAAC and DHCPv6. This added to the confusion, not just for myself but for my devices, so I changed that to &ldquo;Local Connectivity Only&rdquo; to simplify things.</p>
<h3 id="dhcp">DHCP</h3>
<p>My ISP router sucks, it even crashed when I double checked the settings I mentioned above, so I&rsquo;ve moved DHCP off of it and instead do that with Pi-hole. It&rsquo;s way more stable and lets me view the admin panel without crashing.</p>
<p>An extra benefit of this is that when Pi-hole tells a device what its address is it will also tell them to use it as the DNS server, which makes it an automatic setup. The device now gets ad-blocking and can see the custom DNS records I&rsquo;ve set in Pi-hole without me doing anything.</p>
<h3 id="rdnss">RDNSS</h3>
<p>Except now that my ISP router is doing IPv6, it&rsquo;s also doing <abbr title="Recursive DNS Server">RDNSS</abbr>, which is DNS for IPv6, and I can&rsquo;t turn it off. This means it does the same thing that Pi-hole is doing but for IPv6 instead of 4. Since IPv6 is the future, every device on the network now prefers going to the router for DNS, instead of Pi-hole, so they lose the ad-blocking and custom DNS records I&rsquo;ve set.</p>
<p>If I had a better router that let me turn on IPv6 and either disable RDNSS, or advertise Pi-hole as the IPv6 DNS server, then this wouldn&rsquo;t be a problem. With my ISP router not letting me do that I could manually set every device to use Pi-hole for DNS, which is fine, unless you want to do it on Android.</p>
<h3 id="android">Android</h3>
<p>Most of the mobile devices in this house run iOS, which lets you manually set the DNS server, and allows you to enter an IPv4 address, which is fine for Pi-hole.</p>
<p>Android does it differently. It lets you set an IPv4 address, but if it sees RDNSS then it ignores your manual setting and uses that instead, which for me is the ISP router. So no ad-blocking or custom DNS records for Android devices.</p>
<p>This means if you&rsquo;re in the kitchen and want to check Mealie to see the recipe for tonight&rsquo;s meal you have to go out over the internet to come back and reach the server in the other room.</p>
<p>I don&rsquo;t personally use Android, but as the architect of the setup it still feels bad.</p>
<h3 id="router-dns">Router DNS</h3>
<p>There is a fix, although it&rsquo;s not ideal, and that&rsquo;s to set my router&rsquo;s DNS to use Pi-hole. I had initially forgotten about doing this, because I thought it was already setup, but it turns out I&rsquo;d turned it off for good reason.</p>
<p>Whilst it works, and the Android devices can now see apps in my homelab, all the traffic goes to Pi-hole through the router, so you can&rsquo;t distinguish where it came from. I don&rsquo;t really need this, but I remember now why I preferred Pi-hole handing out its own address as the DNS server.</p>
<h2 id="what-i-could-do-to-improve-it">What I could do to improve it</h2>
<p>This project has made me question, well a lot of things. The ability to update my RSS feeds without toggling my VPN on and off is kinda nice, and I can share limited access to my homelab with my friends and family, so for now it can stay.</p>
<p>Here&rsquo;s what would make it better:</p>
<h3 id="use-a-vps-with-ssd-storage-on-an-ipv4-network">Use a VPS with SSD storage on an IPv4 network</h3>
<p>This is the most obvious improvement to the setup.</p>
<p>The NFS storage on the Pi makes it a bad choice to run Docker Compose setups like the Pangolin stack. It works, but startup and even shutdown take too long and so upgrades have far more downtime than they should. NFS is fine for storage for the containers, but it&rsquo;s not suitable for running the containers themselves.</p>
<p>The IPv4 to IPv6 proxy that I&rsquo;m using doesn&rsquo;t forward Wireguard traffic, so I&rsquo;ve had to set up IPv6 in my network, which has caused some manual work I wasn&rsquo;t expecting.</p>
<p>To be clear, the Pi itself is not to blame for any of this, it&rsquo;s more than capable of handling the Pangolin stack. It&rsquo;s the fact it only has NFS storage that lets it down. The proxy not forwarding Wireguard can be worked around, and both are completely understandable tradeoffs that Mythic Beasts have made to make Raspberry PI hosting available in the first place.</p>
<p>The improvement would be to use a VPS, or even another Pi if I could find one, that uses fast local storage and is on an IPv4 network, or has a proxy available that also forwards Wireguard traffic.</p>
<p>If I hadn&rsquo;t already committed to a 12 month contract for the Pi I would have already done this. If I can find an alternative use for it maybe I&rsquo;ll move before the contract runs out.</p>
<p>Until then, I&rsquo;m not doing this.</p>
<h3 id="host-pangolin-without-docker">Host Pangolin without Docker</h3>
<p>This is entirely possible, but goes against the point of using Pangolin. I wanted an easy to use system, and as soon as I start deploying each part of it myself and connecting all the parts together it&rsquo;s no longer simple or easy to use.</p>
<p>The same goes for installing Wireguard manually on the Pi and reverse proxying through it into my homelab. I&rsquo;ve already got a Wireguard server up and running, so that&rsquo;s half the job already done, but then I&rsquo;d need to setup something to secure access into that tunnel, and that&rsquo;s what Pangolin&rsquo;s for.</p>
<h3 id="bluegreen-deployments">Blue/Green deployments</h3>
<p>NFS means the Pi takes a while to run upgrades, so there&rsquo;s about an hour of downtime.</p>
<p>I could get around this by spinning up a second Pi, installing the upgraded software, somehow sync the configs, and then switch the DNS to point to the upgraded Pi.</p>
<p>This wouldn&rsquo;t solve any of the problems except the downtime, and it would cost me more money to do so. I&rsquo;m not doing that. My friends and family will have to put up with the downtime.</p>
<p>They could chip in to cover the costs, but that turns the whole thing into a business arrangement and I don&rsquo;t want to go down that road.</p>
<h3 id="get-a-better-router">Get a better router</h3>
<p>This is more of a homelab quality-of-life improvement than a Pangolin-specific fix.</p>
<p>Getting rid of the ISP router and replacing it with something better would mean I can stop using Pi-hole for DHCP and only use it for its original intended purpose, DNS.</p>
<p>It only needs the ability to change the RDNSS setting to advertise Pi-hole as the DNS server to be better than my ISP router. Forwarding DNS through the router works, but advertising Pi-hole directly would preserve the per-device visibility in Pi-hole that I originally had for all devices.</p>
<p>This may need to happen anyway, but I wasn&rsquo;t looking to do this right now, so for now it isn&rsquo;t a priority. When I do upgrade, OpenWRT, OPNsense and pfSense would all allow me to use Pi-hole for RDNSS.</p>
<h3 id="host-my-own-ipv4-to-ipv6-proxy">Host my own IPv4 to IPv6 proxy</h3>
<p>I could technically go back to IPv4 only in my network if the proxy I use to connect to the Pi also forwarded Wireguard connections. Since the current one doesn&rsquo;t, the only options are to setup IPv6 in my home network or host my own proxy that can.</p>
<p>I picked the Pi because it was the cheapest UK-based hosting I could find, and this doubles the cost, so it isn&rsquo;t really an option.</p>
<p>Also I don&rsquo;t want to host a tunnel for my tunnel. That&rsquo;s too many tunnels.</p>
<h2 id="would-i-do-this-again">Would I do this again?</h2>
<p>Absolutely not.</p>
<h3 id="am-i-glad-i-tried-it">Am I glad I tried it?</h3>
<p>Yes. That&rsquo;s the point of a homelab.</p>]]></content:encoded><source:markdown>
This post is not a step-by-step guide. It&#39;s a record of what I tried and what I learned getting Pangolin to run on a Raspberry Pi with NFS storage in an IPv6-only environment.

**TLDR**: if you want to run Pangolin hassle-free, use a VPS with local SSD storage on an IPv4 network.

&lt;!--more--&gt;

## Pangolin

[Pangolin](https://digpangolin.com/) is a &#34;secure access platform&#34; for safely exposing internal services. I&#39;m using it to let friends and family reach a few apps in my homelab without giving them the keys to everything.

It&#39;s a free, open-source alternative to [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/) for those who like independence from Cloudflare, want to self-host the stack themselves, or prefer not to rely on a third-party network for media streaming, which is not its intended use-case.

If none of those things bother you then you&#39;ll be fine using Cloudflare Tunnel or one of the alternatives. Read the terms and conditions first, media streaming is a bit of a grey area, I am not a lawyer and that wasn&#39;t legal advice.

## Alternatives

I already use [Wireguard](https://www.wireguard.com/) as a VPN, and I still use it myself to get into my home network. VPNs work, but they&#39;re not fun to manage for friends and family and I wanted the ability to lock them down to certain apps.

There&#39;s also [Tailscale](https://tailscale.com/), [Netbird](https://netbird.io/), and [ZeroTier](https://www.zerotier.com/), which use Wireguard to make a mesh network, but that&#39;s more of the same VPN solution. I&#39;d need to set up profiles and install clients on devices, and I wanted something easier. Tailscale now has its own [Funnel](https://tailscale.com/kb/1223/funnel) project, but by the time I&#39;d seen it I was already setting up Pangolin.

The last alternative to mention is that you can achieve a similar result by renting a server, adding it to your VPN, and setting up a reverse proxy on it. But then you&#39;d need something else to make it secure, and you end up back at Pangolin.

## Hosting

Pangolin needs to be hosted outside your network, so I needed to rent a server. It doesn&#39;t need to be a big one, Pangolin can work with 1 vCPU and 1GB of RAM, but ideally it would be close to your internal network and the users that want to get into it.

I live in the UK, so my options were UK, Ireland or Europe. UK-based hosting turned out to be expensive, with Krystal charging [£12 per month](https://krystal.io/cloud-vps), whereas Hetzner, based in Germany, offers the same specs for [£3.97](https://www.hetzner.com/cloud).

I should have gone with Hetzner. The extra latency would likely have been ~20ms and for Pangolin&#39;s use case - web apps and media streaming - that wouldn&#39;t have been noticeable.

But at the time I wanted my server to be based in the UK, so I carried on looking, until I found [Mythic Beasts](https://www.mythic-beasts.com/). Not only had they recently [donated to the Open Rights Group](https://www.mythic-beasts.com/blog/2025/02/25/supporting-the-open-rights-group/), an organisation I also support, but they have a cluster of Raspberry Pis you can use.

My search was over. I wanted Pangolin on a Pi. I did some initial testing, and the early signs were good, so I signed up for a year.

## Raspberry Pi

The Mythic Beasts Pis come with two obstacles to running Pangolin. The first is that they are IPv6 only, and the second is the storage they use. One of these involves a little workaround and the other makes it a bad choice for hosting Pangolin.

### NFS Storage

Pangolin is actually more of a stack, with Pangolin as the control plane, Traefik doing reverse proxying, Gerbil running the tunnel and a thing called Badger to do the middleware that checks for authentication. The easiest way to run it all is with Docker Compose, and this is the first hurdle to running Pangolin on a Mythic Beasts Pi.

The Pis only have network storage available, and Docker doesn&#39;t like that because NFS doesn&#39;t provide the kind of guarantees Docker wants from a filesystem. Docker _can_ work with NFS, but only using the `vfs` storage driver. Unlike the default `overlay2` driver, which uses a layered filesystem, `vfs` doesn&#39;t share layers, so every image consumes more space than it should.

So not only does NFS end up being slow, copying over lots of small files, it means the Docker images can&#39;t do any of the space saving that would normally happen. I have 50GB of storage and it&#39;s at 70% usage, even though the final images only take up 2GB, all because of the way `vfs` does its layering.

Did I mention that it&#39;s slow? Starting up the containers takes about 15 minutes. Turning them off isn&#39;t fast either. And upgrades? You won&#39;t have room for the new images, so you&#39;ll have to turn everything off to remove the old ones before you can start downloading the new ones. This means upgrades can take about an hour, with Pangolin down the entire time.

### IPv6

The internet&#39;s ran out of IPv4 addresses, by the way, so the Pis are IPv6 only. This means you have to do a bit of extra work to make Pangolin available for everyone.

IPv6 is the future but support for it is still mixed, so Mythic Beasts have an IPv4 to IPv6 proxy to allow users stuck on IPv4-only networks to get to your server. To use it you configure the proxy to forward requests to your domain on to the IPv6 address of the Pi.

The other end of the tunnel, the one in your home network, is managed by a service called Newt. It needs to speak to Gerbil on the Pi, and I was not successful getting it to run with the default `pangolin.example.com` address I was using.

Newt is configured by pointing it at Pangolin, which advertises the Gerbil endpoint it should connect to. Maybe it was Pangolin, or the proxy, or even something else; I could not get it to work. To make some progress I changed the `base_endpoint` in `config/config.yml`. First I tried the IPv6 address of the Pi, but that still didn&#39;t work, so I created an AAAA record for Gerbil and pointed it at the Pi. This would do the same thing as the CNAME I&#39;d used for Pangolin, except that went through the proxy and maybe that was messing things up. This time, it worked.

Mythic Beasts [state their proxy](https://www.mythic-beasts.com/support/topics/proxy):

&gt; will relay traffic for common services, such as HTTP and HTTPS

and I have a suspicion that Wireguard doesn&#39;t count as a &#34;common service&#34;.

## Home network

The final hurdle to getting this setup to work was my own network. Since the proxy doesn&#39;t seem to forward Wireguard traffic, and I have to connect directly to the Pi with IPv6, my network needs to let me do that.

This means I need at least IPv6 egress. That doesn&#39;t mean full IPv6 support everywhere, just enough to have a usable outbound path. I tried a bunch of different things to get it to work, and some of my changes actually made things worse, so what follows is what worked for me, in case your setup is similar, most likely it isn&#39;t.

### ISP router

I&#39;m still using the router my ISP gave me, which has very basic IPv6 support. I&#39;m with Vodafone and it&#39;s the standard &#34;WiFi hub&#34; they gave out several years ago.

The right combination of buttons to press is under `Settings -&gt; Local Network` and you want:

`IPv6` **Enabled**

`IPv6 ULA` **Disabled**

The `IPv6` toggle enables Router Advertisements (RA) from the router and this gives hosts a global IPv6 address, and also tells them what the default route for IPv6 traffic is, which is through this router.

The `IPv6 ULA` toggle disables Unique Local Addresses, which is a private IPv6 space used for hosts to speak to each other over IPv6, and is not necessary for Newt to reach Gerbil, so it can be disabled.

### Mesh router

I&#39;ve also got a pair of mesh devices I use as a wireless bridge between floors, since I&#39;m not able to run an ethernet cable.

They&#39;re definitely in bridge mode. However, in the admin panel under `Settings -&gt; Network -&gt; Advanced`, IPv6 was set to &#34;Auto Configuration&#34;, meaning they were also doing SLAAC and DHCPv6. This added to the confusion, not just for myself but for my devices, so I changed that to &#34;Local Connectivity Only&#34; to simplify things.

### DHCP

My ISP router sucks, it even crashed when I double checked the settings I mentioned above, so I&#39;ve moved DHCP off of it and instead do that with Pi-hole. It&#39;s way more stable and lets me view the admin panel without crashing.

An extra benefit of this is that when Pi-hole tells a device what its address is it will also tell them to use it as the DNS server, which makes it an automatic setup. The device now gets ad-blocking and can see the custom DNS records I&#39;ve set in Pi-hole without me doing anything.

### RDNSS

Except now that my ISP router is doing IPv6, it&#39;s also doing &lt;abbr title=&#34;Recursive DNS Server&#34;&gt;RDNSS&lt;/abbr&gt;, which is DNS for IPv6, and I can&#39;t turn it off. This means it does the same thing that Pi-hole is doing but for IPv6 instead of 4. Since IPv6 is the future, every device on the network now prefers going to the router for DNS, instead of Pi-hole, so they lose the ad-blocking and custom DNS records I&#39;ve set.

If I had a better router that let me turn on IPv6 and either disable RDNSS, or advertise Pi-hole as the IPv6 DNS server, then this wouldn&#39;t be a problem. With my ISP router not letting me do that I could manually set every device to use Pi-hole for DNS, which is fine, unless you want to do it on Android.

### Android

Most of the mobile devices in this house run iOS, which lets you manually set the DNS server, and allows you to enter an IPv4 address, which is fine for Pi-hole.

Android does it differently. It lets you set an IPv4 address, but if it sees RDNSS then it ignores your manual setting and uses that instead, which for me is the ISP router. So no ad-blocking or custom DNS records for Android devices.

This means if you&#39;re in the kitchen and want to check Mealie to see the recipe for tonight&#39;s meal you have to go out over the internet to come back and reach the server in the other room.

I don&#39;t personally use Android, but as the architect of the setup it still feels bad.

### Router DNS

There is a fix, although it&#39;s not ideal, and that&#39;s to set my router&#39;s DNS to use Pi-hole. I had initially forgotten about doing this, because I thought it was already setup, but it turns out I&#39;d turned it off for good reason.

Whilst it works, and the Android devices can now see apps in my homelab, all the traffic goes to Pi-hole through the router, so you can&#39;t distinguish where it came from. I don&#39;t really need this, but I remember now why I preferred Pi-hole handing out its own address as the DNS server.

## What I could do to improve it

This project has made me question, well a lot of things. The ability to update my RSS feeds without toggling my VPN on and off is kinda nice, and I can share limited access to my homelab with my friends and family, so for now it can stay.

Here&#39;s what would make it better:

### Use a VPS with SSD storage on an IPv4 network

This is the most obvious improvement to the setup.

The NFS storage on the Pi makes it a bad choice to run Docker Compose setups like the Pangolin stack. It works, but startup and even shutdown take too long and so upgrades have far more downtime than they should. NFS is fine for storage for the containers, but it&#39;s not suitable for running the containers themselves.

The IPv4 to IPv6 proxy that I&#39;m using doesn&#39;t forward Wireguard traffic, so I&#39;ve had to set up IPv6 in my network, which has caused some manual work I wasn&#39;t expecting.

To be clear, the Pi itself is not to blame for any of this, it&#39;s more than capable of handling the Pangolin stack. It&#39;s the fact it only has NFS storage that lets it down. The proxy not forwarding Wireguard can be worked around, and both are completely understandable tradeoffs that Mythic Beasts have made to make Raspberry PI hosting available in the first place.

The improvement would be to use a VPS, or even another Pi if I could find one, that uses fast local storage and is on an IPv4 network, or has a proxy available that also forwards Wireguard traffic.

If I hadn&#39;t already committed to a 12 month contract for the Pi I would have already done this. If I can find an alternative use for it maybe I&#39;ll move before the contract runs out.

Until then, I&#39;m not doing this.

### Host Pangolin without Docker

This is entirely possible, but goes against the point of using Pangolin. I wanted an easy to use system, and as soon as I start deploying each part of it myself and connecting all the parts together it&#39;s no longer simple or easy to use.

The same goes for installing Wireguard manually on the Pi and reverse proxying through it into my homelab. I&#39;ve already got a Wireguard server up and running, so that&#39;s half the job already done, but then I&#39;d need to setup something to secure access into that tunnel, and that&#39;s what Pangolin&#39;s for.

### Blue/Green deployments

NFS means the Pi takes a while to run upgrades, so there&#39;s about an hour of downtime.

I could get around this by spinning up a second Pi, installing the upgraded software, somehow sync the configs, and then switch the DNS to point to the upgraded Pi.

This wouldn&#39;t solve any of the problems except the downtime, and it would cost me more money to do so. I&#39;m not doing that. My friends and family will have to put up with the downtime.

They could chip in to cover the costs, but that turns the whole thing into a business arrangement and I don&#39;t want to go down that road.

### Get a better router

This is more of a homelab quality-of-life improvement than a Pangolin-specific fix.

Getting rid of the ISP router and replacing it with something better would mean I can stop using Pi-hole for DHCP and only use it for its original intended purpose, DNS.

It only needs the ability to change the RDNSS setting to advertise Pi-hole as the DNS server to be better than my ISP router. Forwarding DNS through the router works, but advertising Pi-hole directly would preserve the per-device visibility in Pi-hole that I originally had for all devices.

This may need to happen anyway, but I wasn&#39;t looking to do this right now, so for now it isn&#39;t a priority. When I do upgrade, OpenWRT, OPNsense and pfSense would all allow me to use Pi-hole for RDNSS.

### Host my own IPv4 to IPv6 proxy

I could technically go back to IPv4 only in my network if the proxy I use to connect to the Pi also forwarded Wireguard connections. Since the current one doesn&#39;t, the only options are to setup IPv6 in my home network or host my own proxy that can.

I picked the Pi because it was the cheapest UK-based hosting I could find, and this doubles the cost, so it isn&#39;t really an option.

Also I don&#39;t want to host a tunnel for my tunnel. That&#39;s too many tunnels.

## Would I do this again?

Absolutely not.

### Am I glad I tried it?

Yes. That&#39;s the point of a homelab.
</source:markdown></item><item>
      <title>The Self-Inflicted Pain Ruining My Homelab</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/09/10/the-self-inflicted-pain-ruining-my-homelab/</link>
      <pubDate>Wed, 10 Sep 2025 15:30:00 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/09/10/the-self-inflicted-pain-ruining-my-homelab/</guid>
      <category>homelab</category>
      <category>networking</category>
      <category>DHCP</category>
      <category>Pi-hole</category>
      <description>The worst pains in tech are self-inflicted. Then it’s bad Wi-Fi.
I’ve just fixed a very niche problem that was both.</description>
      <content:encoded><![CDATA[<p>The worst pains in tech are self-inflicted. Then it&rsquo;s bad Wi-Fi.</p>
<p>I&rsquo;ve just fixed a very niche problem that was both.</p>
<h2 id="home-network">Home Network</h2>
<p>My home network&rsquo;s a bit unusual. The internet comes in downstairs, like the internet usually does, and I need it upstairs where my computers are. I&rsquo;m renting, and the property owner doesn&rsquo;t want wires going up and into walls, so I have to do it wirelessly.</p>
<p>WiFi is fine for most of my devices but a few of them are servers that don&rsquo;t have adapters. They want to be plugged in, so I share the internet between floors with a pair of <a href="https://www.dlink.com/en/products/m15-3-pack-ax1500-mesh-system">mesh routers</a>
 and plug in the servers to the upstairs mesh device.</p>
<p>It all works great and I can recommend the mesh routers, although they&rsquo;re a bit old and have probably been superseded by now.</p>
<h2 id="homelab">Homelab</h2>
<p>The problems started along with my homelab.</p>
<p>I was creating lots of virtual machines on one of the servers that weren&rsquo;t being recognised by the router, so they wouldn&rsquo;t join the network and had no internet access.</p>
<p>I couldn&rsquo;t get onto the admin panel of the router because it&rsquo;s the free one you get from the ISP and it wasn&rsquo;t powerful enough to keep up.</p>
<p>I was getting annoyed.</p>
<h3 id="dhcp">DHCP</h3>
<p>Then I found that Pi-hole, something I was already using for ad-blocking and DNS, could also <a href="https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026">act as a DHCP server</a>
. This would undoubtedly be better than doing it on the free ISP router that couldn&rsquo;t keep up, so I disabled it on the router and enabled it in Pi-hole.</p>
<p>The way DHCP works is that something on your network needs to answer requests from clients telling them what their address is. Usually this is the router, but it doesn&rsquo;t need to be.</p>
<p>So I set my instance of Pi-hole to handle DHCP.</p>
<p>And it all worked great.</p>
<p>Kind of.</p>
<h3 id="dhcp-over-mesh-wifi">DHCP over mesh WiFi?</h3>
<p>The first clue that things weren&rsquo;t quite right came when my devices wouldn&rsquo;t reconnect to WiFi. The next was when my PC wouldn&rsquo;t get a connection at all, until after I restarted the mesh router.</p>
<p>It took a while for me to connect the dots, but something about the mesh routers was making things weird.</p>
<p>If it didn&rsquo;t work then that was fine, I would move on. But it worked some of the time?</p>
<h3 id="have-you-tried-not-doing-that">Have you tried not doing that?</h3>
<p>I spent a lot of time this week trying to diagnose the issue.</p>
<p>I even started preparing to replace the router.</p>
<p>I needn&rsquo;t have bothered.</p>
<p>Moving the server with Pi-hole on it downstairs, where it doesn&rsquo;t need to use the mesh routers, has fixed the issue.</p>
<p>Pi-hole now responds to DHCP requests and all my devices can connect to the network.</p>
<h2 id="summary">Summary</h2>
<p>So if you ever have DHCP issues because your instance of Pi-hole is behind a mesh router, try not doing that and see if it fixes things.</p>]]></content:encoded><source:markdown>
The worst pains in tech are self-inflicted. Then it&#39;s bad Wi-Fi.

I&#39;ve just fixed a very niche problem that was both.

&lt;!--more--&gt;

## Home Network

My home network&#39;s a bit unusual. The internet comes in downstairs, like the internet usually does, and I need it upstairs where my computers are. I&#39;m renting, and the property owner doesn&#39;t want wires going up and into walls, so I have to do it wirelessly.

WiFi is fine for most of my devices but a few of them are servers that don&#39;t have adapters. They want to be plugged in, so I share the internet between floors with a pair of [mesh routers](https://www.dlink.com/en/products/m15-3-pack-ax1500-mesh-system) and plug in the servers to the upstairs mesh device.

It all works great and I can recommend the mesh routers, although they&#39;re a bit old and have probably been superseded by now.

## Homelab

The problems started along with my homelab.

I was creating lots of virtual machines on one of the servers that weren&#39;t being recognised by the router, so they wouldn&#39;t join the network and had no internet access.

I couldn&#39;t get onto the admin panel of the router because it&#39;s the free one you get from the ISP and it wasn&#39;t powerful enough to keep up.

I was getting annoyed.

### DHCP

Then I found that Pi-hole, something I was already using for ad-blocking and DNS, could also [act as a DHCP server](https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026). This would undoubtedly be better than doing it on the free ISP router that couldn&#39;t keep up, so I disabled it on the router and enabled it in Pi-hole.

The way DHCP works is that something on your network needs to answer requests from clients telling them what their address is. Usually this is the router, but it doesn&#39;t need to be.

So I set my instance of Pi-hole to handle DHCP.

And it all worked great.

Kind of.

### DHCP over mesh WiFi?

The first clue that things weren&#39;t quite right came when my devices wouldn&#39;t reconnect to WiFi. The next was when my PC wouldn&#39;t get a connection at all, until after I restarted the mesh router.

It took a while for me to connect the dots, but something about the mesh routers was making things weird.

If it didn&#39;t work then that was fine, I would move on. But it worked some of the time?

### Have you tried not doing that?

I spent a lot of time this week trying to diagnose the issue.

I even started preparing to replace the router.

I needn&#39;t have bothered.

Moving the server with Pi-hole on it downstairs, where it doesn&#39;t need to use the mesh routers, has fixed the issue.

Pi-hole now responds to DHCP requests and all my devices can connect to the network.

## Summary

So if you ever have DHCP issues because your instance of Pi-hole is behind a mesh router, try not doing that and see if it fixes things.
</source:markdown></item><item>
      <title>Into the Fediverse 🖖</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/09/10/into-the-fediverse/</link>
      <pubDate>Wed, 10 Sep 2025 12:00:00 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/09/10/into-the-fediverse/</guid>
      <category>IndieWeb</category>
      <category>Fediverse</category>
      <category>Bluesky</category>
      <category>Mastodon</category>
      <category>Micro.blog</category>
      <description>We live in strange times, with strange social networks doing strange things.
So why, on the same day I said I was team IndieWeb, did I enter the Fediverse?</description>
      <content:encoded><![CDATA[<p>We live in strange times, with strange social networks doing strange things.</p>
<p>So why, on the same day I said I was team IndieWeb, did I enter the Fediverse?</p>
<h2 id="indieweb">IndieWeb</h2>
<p>I&rsquo;ve been following the <a href="https://indieweb.org/">IndieWeb</a>
 for a decade now and have recently started blogging again, slowly adding IndieWeb features to my site.</p>
<p>Right now it&rsquo;s readable and followable, a bare minimum website experience, but with a little <a href="https://indieweb.org/webmention">webmention</a>
 magic it can grow into something more social. That&rsquo;s the plan anyway.</p>
<p>I&rsquo;d be able to write and respond to others on the IndieWeb that also do webmentions.</p>
<p>But not everyone wants to make their own website, or buy into a platform with strange features they don&rsquo;t understand , so there&rsquo;s a limit to how social it can really end up being.</p>
<h2 id="mastodon">Mastodon</h2>
<p>Enter Mastodon, which erupted in popularity when Musk started to ruin Twitter. It&rsquo;s a decentralised social network that works a bit like Twitter did but without as many nazis.</p>
<p>Mastodon is powered by <a href="https://activitypub.rocks/">ActivityPub</a>
, which means it&rsquo;s part of the Fediverse - a whole network of apps that can all talk to each other.</p>
<p>My website isn&rsquo;t connected yet because I haven&rsquo;t added support for it. I do already have ActivityPub on my Micro.blog account, although it&rsquo;s a little complicated.</p>
<h2 id="microblog">Micro.blog</h2>
<p><a href="https://micro.blog">Micro.blog</a>
 is a microblogging platform with both IndieWeb and Fediverse functionality. I use it at <a href="https://micro.paultibbetts.uk">micro.paultibbetts.uk</a>
 to post shorter content and links to my stuff around the web.</p>
<p>It speaks ActivityPub, so technically you can interact with me at <code>@paultibbetts@micro.blog</code>.</p>
<p>It also has an RSS importer, which I use to posts links from my main site to the Micro.blog timeline, so other users can follow my blog using Micro.blog.</p>
<p>What it does not do, unfortunately, is post those links to ActivityPub.</p>
<p>So I&rsquo;m going to use Micro.blog to cross-post to my own Mastodon account.</p>
<h2 id="bluesky">Bluesky</h2>
<p>So if I&rsquo;m going to check out the Fediverse, and other non-IndieWeb social networks, why not check out Bluesky?</p>
<p>Bluesky is taking a different approach to Mastodon and the rest of the Fediverse.</p>
<p>It does not use ActivityPub, it uses the <a href="https://docs.bsky.app/docs/advanced-guides/atproto">AT Protocol</a>
, something else I don&rsquo;t yet understand or want to add to my own site. So again I&rsquo;m using Micro.blog to cross-post for me.</p>
<h2 id="posse">POSSE</h2>
<p>It&rsquo;s all in an effort to <strong>P</strong>ost to my <strong>O</strong>wn <strong>S</strong>ite and <strong>S</strong>yndicate <strong>E</strong>lsewhere.</p>
<p>I&rsquo;m taking <a href="https://indieweb.org/POSSE">the IndieWeb approach</a>
 and putting my content in one place and then posting links to it in as many places and on as many platforms as I can.</p>
<p>There are more advanced approaches than this, ones where I can pull back comments to my own site, but I&rsquo;m not there just yet.</p>
<p>For now I&rsquo;m building an online presence and testing out the future of social networking.</p>
<h2 id="follow-me">Follow me</h2>
<p>Which changes how you can follow me, again.</p>
<p>You can follow my blog with <a href="https://paultibbetts.uk/feed.xml">RSS</a>
.</p>
<p>You can follow my microblog with <a href="https://micro.paultibbetts.uk/feed.xml">RSS</a>
.</p>
<p>You can follow both using <a href="https://micro.blog/paultibbetts">Micro.blog</a>
, <a href="https://indieweb.social/@paultibbetts">Mastodon</a>
 and <a href="https://bsky.app/profile/paultibbetts.uk">Bluesky</a>
.</p>]]></content:encoded><source:markdown>
We live in strange times, with strange social networks doing strange things.

So why, on the same day I said I was team IndieWeb, did I enter the Fediverse?

&lt;!--more--&gt;

## IndieWeb

I&#39;ve been following the [IndieWeb](https://indieweb.org/) for a decade now and have recently started blogging again, slowly adding IndieWeb features to my site.

Right now it&#39;s readable and followable, a bare minimum website experience, but with a little [webmention](https://indieweb.org/webmention) magic it can grow into something more social. That&#39;s the plan anyway.

I&#39;d be able to write and respond to others on the IndieWeb that also do webmentions.

But not everyone wants to make their own website, or buy into a platform with strange features they don&#39;t understand , so there&#39;s a limit to how social it can really end up being.

## Mastodon

Enter Mastodon, which erupted in popularity when Musk started to ruin Twitter. It&#39;s a decentralised social network that works a bit like Twitter did but without as many nazis.

Mastodon is powered by [ActivityPub](https://activitypub.rocks/), which means it&#39;s part of the Fediverse - a whole network of apps that can all talk to each other.

My website isn&#39;t connected yet because I haven&#39;t added support for it. I do already have ActivityPub on my Micro.blog account, although it&#39;s a little complicated.

## Micro.blog

[Micro.blog](https://micro.blog) is a microblogging platform with both IndieWeb and Fediverse functionality. I use it at [micro.paultibbetts.uk](https://micro.paultibbetts.uk) to post shorter content and links to my stuff around the web.

It speaks ActivityPub, so technically you can interact with me at `@paultibbetts@micro.blog`.

It also has an RSS importer, which I use to posts links from my main site to the Micro.blog timeline, so other users can follow my blog using Micro.blog.

What it does not do, unfortunately, is post those links to ActivityPub.

So I&#39;m going to use Micro.blog to cross-post to my own Mastodon account.

## Bluesky

So if I&#39;m going to check out the Fediverse, and other non-IndieWeb social networks, why not check out Bluesky?

Bluesky is taking a different approach to Mastodon and the rest of the Fediverse.

It does not use ActivityPub, it uses the [AT Protocol](https://docs.bsky.app/docs/advanced-guides/atproto), something else I don&#39;t yet understand or want to add to my own site. So again I&#39;m using Micro.blog to cross-post for me.

## POSSE

It&#39;s all in an effort to **P**ost to my **O**wn **S**ite and **S**yndicate **E**lsewhere.

I&#39;m taking [the IndieWeb approach](https://indieweb.org/POSSE) and putting my content in one place and then posting links to it in as many places and on as many platforms as I can.

There are more advanced approaches than this, ones where I can pull back comments to my own site, but I&#39;m not there just yet.

For now I&#39;m building an online presence and testing out the future of social networking.

## Follow me

Which changes how you can follow me, again.

You can follow my blog with [RSS](https://paultibbetts.uk/feed.xml).

You can follow my microblog with [RSS](https://micro.paultibbetts.uk/feed.xml).

You can follow both using [Micro.blog](https://micro.blog/paultibbetts), [Mastodon](https://indieweb.social/@paultibbetts) and [Bluesky](https://bsky.app/profile/paultibbetts.uk).
</source:markdown></item><item>
      <title>Getting My Own Site</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/09/09/getting-my-own-site/</link>
      <pubDate>Tue, 09 Sep 2025 20:00:00 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/09/09/getting-my-own-site/</guid>
      <category>IndieWeb</category>
      <category>meta</category>
      <description>The second step to getting on the IndieWeb is to have your own site.
This is easier said than done, and there are lots of different ways of doing it.
This is what I did.</description>
      <content:encoded><![CDATA[<p>The second step to <a href="https://indieweb.org/Getting_Started">getting on the IndieWeb</a>
 is to have your own site.</p>
<p>This is easier said than done, and there are lots of different ways of doing it.</p>
<p>This is what I did.</p>
<h2 id="as-a-web-developer">As a web developer</h2>
<p>There are three paths to getting your own site.</p>
<h3 id="service">Service</h3>
<p>You could use an existing service like <a href="https://micro.blog/">Micro.blog</a>
 to host your site.</p>
<p>I already do this for my <a href="https://micro.paultibbetts.uk/">microblog</a>
 because it comes with a bunch of features out of the box and gets me onto the IndieWeb, even while I don&rsquo;t yet support all the IndieWeb features on my main site.</p>
<h3 id="cms">CMS</h3>
<p>An alternative path is to use an existing content management system and add on IndieWeb features with plugins.</p>
<p>I&rsquo;ve done this in the past with <a href="https://wordpress.org/">WordPress</a>
 but I moved on from the WordPress world a while ago.</p>
<h3 id="self-starter">Self-starter</h3>
<p>I decided I was more comfortable writing and doing it myself.</p>
<h2 id="what-it-needs-to-do">What it needs to do</h2>
<p>Before I wrote any code I made sure to cover the basics.</p>
<h3 id="information-about-me">Information about me</h3>
<p>The homepage of my site needs to include a <a href="https://indieweb.org/h-card">h-card</a>
 with my name, an icon, and <a href="https://indieweb.org/rel-me">rel-me</a>
 links to my social network profiles.</p>
<p>This is not only useful for humans but I can also use it to sign in to other sites using <a href="https://indieweb.org/IndieAuth">IndieAuth</a>
.</p>
<p>I used <a href="https://indiewebify.me/">IndieWebify.me</a>
 to check I&rsquo;d done it correctly.</p>
<h3 id="publish-content">Publish content</h3>
<p>My posts should be marked up using <a href="https://indieweb.org/h-entry">h-entry</a>
 so that others can understand my content.</p>
<p>The original site design had words with titles, which makes them technically &ldquo;articles&rdquo;, although that could change in the fture.</p>
<h2 id="how-the-site-works">How the site works</h2>
<p>There are lots of different ways to do this.</p>
<p>In fact there are too many, so I kept it as simple as I could.</p>
<h3 id="static-site-generator">Static site generator</h3>
<p>I want to write my content in <a href="https://www.markdownguide.org/">Markdown</a>
, I would prefer doing that on my laptop instead of my phone, and I&rsquo;m ok running commands to publish things.</p>
<p>Which means I can use a <a href="https://en.wikipedia.org/wiki/Static_site_generator">static site generator</a>
 like <a href="https://gohugo.io">Hugo</a>
 to turn my markdown files into HTML.</p>
<h2 id="hosting">Hosting</h2>
<p>One of the best parts about using a static site generator is that it makes hosting simpler.</p>
<p>Once you have generated a static site you can upload the files to a web host and you&rsquo;re done.</p>
<p>And you can automate it, so all you do is update it and it gets deployed automatically.</p>
<h3 id="github-pages">GitHub pages</h3>
<p>I host copies of my code on GitHub so that others can view the source and I was planning on doing the same with my site.</p>
<p>This meant I could use one of GitHub&rsquo;s other features, which is <a href="https://pages.github.com/">free hosting for static content</a>
.</p>
<p>All I would need to do is add a <a href="https://github.com/paultibbetts/paultibbetts.uk/blob/a24aa42e17d0b8f059d9f12118fd69147b3fa12b/.github/workflows/hugo.yaml">workflow</a>
 for GitHub Actions to perform each time the code gets updated and the site gets deployed automatically.</p>
<h2 id="indiemark">IndieMark</h2>
<p>There&rsquo;s no official measure of a site&rsquo;s &ldquo;IndieWebness&rdquo; but the closest right now is the still-in-development <a href="https://indieweb.org/IndieMark">IndieMark</a>
 which would score me a level 1.</p>
<p>Level 0 involves owning your own domain, something I have <a href="https://paultibbetts.uk/2025/09/09/getting-my-own-domain/">written about before</a>
, and having a personal site at that domain.</p>
<p>Level 1 involves:</p>
<ul>
<li><strong>identity</strong>, I&rsquo;ll be using this at my primary domain</li>
<li><strong>authentication</strong> using <a href="https://indieweb.org/rel-me">rel-me</a>
 links to my external social network profiles</li>
<li><strong>posts</strong> with <strong>permalinks</strong>, <strong>h-entry</strong> markup and other <strong>microformats</strong></li>
<li><strong>searchable</strong> by <strong>allowing robots to index my site</strong>, my <strong>content is written in HTML</strong> and <strong>no JavaScript is required to read it</strong></li>
</ul>
<h2 id="view-source">View source</h2>
<p>You can <a href="https://github.com/paultibbetts/paultibbetts.uk">view the source code</a>
 to see how I&rsquo;ve abused Hugo and <a href="https://tailwindcss.com/">TailwindCSS</a>
 into creating my own site, hosted on GitHub Pages.</p>]]></content:encoded><source:markdown>
The second step to [getting on the IndieWeb](https://indieweb.org/Getting_Started) is to have your own site.

This is easier said than done, and there are lots of different ways of doing it.

This is what I did.

&lt;!--more--&gt;

## As a web developer

There are three paths to getting your own site.

### Service

You could use an existing service like [Micro.blog](https://micro.blog/) to host your site.

I already do this for my [microblog](https://micro.paultibbetts.uk/) because it comes with a bunch of features out of the box and gets me onto the IndieWeb, even while I don&#39;t yet support all the IndieWeb features on my main site.

### CMS

An alternative path is to use an existing content management system and add on IndieWeb features with plugins.

I&#39;ve done this in the past with [WordPress](https://wordpress.org/) but I moved on from the WordPress world a while ago.

### Self-starter

I decided I was more comfortable writing and doing it myself.

## What it needs to do

Before I wrote any code I made sure to cover the basics.

### Information about me

The homepage of my site needs to include a [h-card](https://indieweb.org/h-card) with my name, an icon, and [rel-me](https://indieweb.org/rel-me) links to my social network profiles.

This is not only useful for humans but I can also use it to sign in to other sites using [IndieAuth](https://indieweb.org/IndieAuth).

I used [IndieWebify.me](https://indiewebify.me/) to check I&#39;d done it correctly.

### Publish content

My posts should be marked up using [h-entry](https://indieweb.org/h-entry) so that others can understand my content.

The original site design had words with titles, which makes them technically &#34;articles&#34;, although that could change in the fture.

## How the site works

There are lots of different ways to do this.

In fact there are too many, so I kept it as simple as I could.

### Static site generator

I want to write my content in [Markdown](https://www.markdownguide.org/), I would prefer doing that on my laptop instead of my phone, and I&#39;m ok running commands to publish things.

Which means I can use a [static site generator](https://en.wikipedia.org/wiki/Static_site_generator) like [Hugo](https://gohugo.io) to turn my markdown files into HTML.

## Hosting

One of the best parts about using a static site generator is that it makes hosting simpler.

Once you have generated a static site you can upload the files to a web host and you&#39;re done.

And you can automate it, so all you do is update it and it gets deployed automatically.

### GitHub pages

I host copies of my code on GitHub so that others can view the source and I was planning on doing the same with my site.

This meant I could use one of GitHub&#39;s other features, which is [free hosting for static content](https://pages.github.com/).

All I would need to do is add a [workflow](https://github.com/paultibbetts/paultibbetts.uk/blob/a24aa42e17d0b8f059d9f12118fd69147b3fa12b/.github/workflows/hugo.yaml) for GitHub Actions to perform each time the code gets updated and the site gets deployed automatically.

## IndieMark

There&#39;s no official measure of a site&#39;s &#34;IndieWebness&#34; but the closest right now is the still-in-development [IndieMark](https://indieweb.org/IndieMark) which would score me a level 1.

Level 0 involves owning your own domain, something I have [written about before](https://paultibbetts.uk/2025/09/09/getting-my-own-domain/), and having a personal site at that domain.

Level 1 involves:

- **identity**, I&#39;ll be using this at my primary domain
- **authentication** using [rel-me](https://indieweb.org/rel-me) links to my external social network profiles
- **posts** with **permalinks**, **h-entry** markup and other **microformats**
- **searchable** by **allowing robots to index my site**, my **content is written in HTML** and **no JavaScript is required to read it**

## View source

You can [view the source code](https://github.com/paultibbetts/paultibbetts.uk) to see how I&#39;ve abused Hugo and [TailwindCSS](https://tailwindcss.com/) into creating my own site, hosted on GitHub Pages.
</source:markdown></item><item>
      <title>Getting My Own Domain</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/09/09/getting-my-own-domain/</link>
      <pubDate>Tue, 09 Sep 2025 15:53:46 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/09/09/getting-my-own-domain/</guid>
      <category>IndieWeb</category>
      <category>meta</category>
      <description>The first step to getting on the IndieWeb is to have your own domain.
I’ve already got one, so I thought I’d explain how I got it.</description>
      <content:encoded><![CDATA[<p>The first step to <a href="https://indieweb.org/Getting_Started">getting on the IndieWeb</a>
 is to have your own domain.</p>
<p>I&rsquo;ve already got one, so I thought I&rsquo;d explain how I got it.</p>
<h2 id="ruler">Ruler</h2>
<p>Years ago I registered my name as my domain.</p>
<p>I&rsquo;m not a company, so I didn&rsquo;t want <code>.com</code>, but there weren&rsquo;t many options, and so to make me &ldquo;Paul Tibbetts from the UK&rdquo; I went with <code>.co.uk</code>.</p>
<p>Some time later <code>.uk</code> was made available and to make it fair to the <code>.co.uk</code> owners who wanted to drop the <code>.co</code> you had to also own the <code>.co.uk</code> version to register it, so for a while I owned both.</p>
<p>This was all before the newer extensions like <code>.blog</code> and <code>.dev</code> came out. When they did I considered changing but decided against it.</p>
<h2 id="registrar">Registrar</h2>
<p>I registered my domain with <a href="https://www.heartinternet.uk/">Heart Internet</a>
 because that&rsquo;s who we used at work and I didn&rsquo;t have any kind of preference.</p>
<p>They don&rsquo;t expose my personal data when you run a <a href="https://www.whois.com/whois/paultibbetts.uk">whois query on the domain</a>
 so I haven&rsquo;t found a reason to move away from them.</p>
<p>Renewals are £9.99 per year.</p>
<h2 id="management">Management</h2>
<p>For a while I managed the domain using the interface on Heart Internet but since then I&rsquo;ve moved it to <a href="https://www.cloudflare.com/">Cloudflare</a>
.</p>
<p>To do this I changed the nameservers that the domain uses from Heart Internet&rsquo;s to Cloudflare&rsquo;s.</p>
<h2 id="records">Records</h2>
<p>There aren&rsquo;t that many records right now.</p>
<h3 id="heading">@</h3>
<p>The apex domain, as in the bare one without any subdomains (<code>https://paultibbetts.uk</code>), points to a really small landing page I made for myself.</p>
<p>I&rsquo;ll be redoing this soon so I won&rsquo;t talk about it yet.</p>
<h3 id="www">www</h3>
<p>I <strong>don&rsquo;t</strong> use the <code>www</code> subdomain.</p>
<p>I know there are people who think this is wrong but growing up sharing links with others the moment I discovered you could get rid of it I did and haven&rsquo;t looked back.</p>
<p>Maybe one day I will revisit this.</p>
<h3 id="micro">micro</h3>
<p><code>micro.paultibbetts.uk</code> is powered by <a href="https://micro.blog">Micro.blog</a>
.</p>
<p>I <em>could</em> run my main domain with Micro.blog but I want to have a go at doing that myself.</p>
<p>I followed <a href="https://help.micro.blog/t/custom-domain-names/53">the Micro.blog guide to set up a custom domain</a>
 and used a <code>CNAME</code> on my domain to point <code>micro</code> to <code>https://paultibbetts.micro.blog</code>.</p>
<h2 id="emails">Emails</h2>
<p>Once upon a time I hosted my own email server.</p>
<p>The company I worked at, Sixth Story, had an email address <code>iwanttowork @ sixthstory</code> that I thought was clever, so I wanted <code>hire @ paultibbetts</code> for myself. Applying for jobs as a web developer I thought it would help me stand out.</p>
<p>Even after all the work though I still found myself using my regular Apple email address because I knew it was more stable, and less likely to get marked as spam, so when Apple announced <a href="https://support.apple.com/en-us/102540">you can use custom domains with iCloud mail</a>
 I got rid of my email server and used Apple&rsquo;s instead.</p>
<p>This means I have some <code>MX</code> and <code>TXT</code> records to allow that to work.</p>
<h2 id="in-review">In review</h2>
<p>I have always been jealous of others with better domains than me.</p>
<h3 id="difficult-to-spell">Difficult to spell</h3>
<p>I have trouble getting people to spell my surname correctly.</p>
<p>I don&rsquo;t blame them, there&rsquo;s lots of Bs and Ts, and saying &ldquo;technically there&rsquo;s 3 Ts&rdquo; isn&rsquo;t helpful when they&rsquo;re typing the double T towards the end.</p>
<h4 id="potential-fix">Potential fix</h4>
<p>Aral&rsquo;s <code>ar.al</code> domain is much better than mine. It&rsquo;s short and easy to spell.</p>
<p>Maybe I could find a short domain and set it up to redirect?</p>
<p>Time for another disappointing trip to <a href="https://iwantmyname.com/">IWantMyName</a>
 to see all the good domains have been taken.</p>
<h3 id="did-you-mean-the-other-one">Did you mean the other one?</h3>
<p>I share a name with someone way more famous than me.</p>
<p>It&rsquo;s spelt a little different but search engines will assume you meant the other Paul.</p>
<h4 id="potential-fix-1">Potential fix</h4>
<p>There are a few ways I can improve my search engine rankings.</p>
<p>I&rsquo;m going to try writing lots of content to go on my domain, hoping that does it.</p>
<p>I will <strong>not</strong> do what the other guy did.</p>
<h3 id="dot-uk">dot UK</h3>
<p>My <code>.uk</code> address works whilst I live here but it wouldn&rsquo;t if I moved.</p>
<h4 id="potential-fix-2">Potential fix</h4>
<p>I don&rsquo;t have any immediate plans to move abroad so I&rsquo;m not too concerned with this.</p>
<p>If I ever did I can setup redirects.</p>
<h3 id="not-a-cool-internet-name">Not a cool internet name</h3>
<p>Jeremy Keith&rsquo;s <code>adactio.com</code> domain regularly reminds me I&rsquo;m not creative enough to come up with a cool internet name.</p>
<h4 id="potential-fix-3">Potential fix</h4>
<p>Maybe another decade of thinking about it will help.</p>
<h2 id="reposted">Reposted</h2>
<p>This was originally posted on my <a class="u-repost-of" href="https://micro.paultibbetts.uk/2025/01/02/getting-my-own-domain.html">microblog</a> before this site existed and I wanted a copy of it here.</p>]]></content:encoded><source:markdown>
The first step to [getting on the IndieWeb](https://indieweb.org/Getting_Started) is to have your own domain.

I&#39;ve already got one, so I thought I&#39;d explain how I got it.

&lt;!--more--&gt;

## Ruler

Years ago I registered my name as my domain.

I&#39;m not a company, so I didn&#39;t want `.com`, but there weren&#39;t many options, and so to make me &#34;Paul Tibbetts from the UK&#34; I went with `.co.uk`.

Some time later `.uk` was made available and to make it fair to the `.co.uk` owners who wanted to drop the `.co` you had to also own the `.co.uk` version to register it, so for a while I owned both.

This was all before the newer extensions like `.blog` and `.dev` came out. When they did I considered changing but decided against it.

## Registrar

I registered my domain with [Heart Internet](https://www.heartinternet.uk/) because that&#39;s who we used at work and I didn&#39;t have any kind of preference.

They don&#39;t expose my personal data when you run a [whois query on the domain](https://www.whois.com/whois/paultibbetts.uk) so I haven&#39;t found a reason to move away from them.

Renewals are £9.99 per year.

## Management

For a while I managed the domain using the interface on Heart Internet but since then I&#39;ve moved it to [Cloudflare](https://www.cloudflare.com/).

To do this I changed the nameservers that the domain uses from Heart Internet&#39;s to Cloudflare&#39;s.

## Records

There aren&#39;t that many records right now.

### @

The apex domain, as in the bare one without any subdomains (`https://paultibbetts.uk`), points to a really small landing page I made for myself.

I&#39;ll be redoing this soon so I won&#39;t talk about it yet.

### www

I **don&#39;t** use the `www` subdomain.

I know there are people who think this is wrong but growing up sharing links with others the moment I discovered you could get rid of it I did and haven&#39;t looked back.

Maybe one day I will revisit this.

### micro

`micro.paultibbetts.uk` is powered by [Micro.blog](https://micro.blog).

I _could_ run my main domain with Micro.blog but I want to have a go at doing that myself.

I followed [the Micro.blog guide to set up a custom domain](https://help.micro.blog/t/custom-domain-names/53) and used a `CNAME` on my domain to point `micro` to `https://paultibbetts.micro.blog`.

## Emails

Once upon a time I hosted my own email server.

The company I worked at, Sixth Story, had an email address `iwanttowork @ sixthstory` that I thought was clever, so I wanted `hire @ paultibbetts` for myself. Applying for jobs as a web developer I thought it would help me stand out.

Even after all the work though I still found myself using my regular Apple email address because I knew it was more stable, and less likely to get marked as spam, so when Apple announced [you can use custom domains with iCloud mail](https://support.apple.com/en-us/102540) I got rid of my email server and used Apple&#39;s instead.

This means I have some `MX` and `TXT` records to allow that to work.

## In review

I have always been jealous of others with better domains than me.

### Difficult to spell

I have trouble getting people to spell my surname correctly.

I don&#39;t blame them, there&#39;s lots of Bs and Ts, and saying &#34;technically there&#39;s 3 Ts&#34; isn&#39;t helpful when they&#39;re typing the double T towards the end.

#### Potential fix

Aral&#39;s `ar.al` domain is much better than mine. It&#39;s short and easy to spell.

Maybe I could find a short domain and set it up to redirect?

Time for another disappointing trip to [IWantMyName](https://iwantmyname.com/) to see all the good domains have been taken.

### Did you mean the other one?

I share a name with someone way more famous than me.

It&#39;s spelt a little different but search engines will assume you meant the other Paul.

#### Potential fix

There are a few ways I can improve my search engine rankings.

I&#39;m going to try writing lots of content to go on my domain, hoping that does it.

I will **not** do what the other guy did.

### dot UK

My `.uk` address works whilst I live here but it wouldn&#39;t if I moved.

#### Potential fix

I don&#39;t have any immediate plans to move abroad so I&#39;m not too concerned with this.

If I ever did I can setup redirects.

### Not a cool internet name

Jeremy Keith&#39;s `adactio.com` domain regularly reminds me I&#39;m not creative enough to come up with a cool internet name.

#### Potential fix

Maybe another decade of thinking about it will help.

## Reposted

This was originally posted on my &lt;a class=&#34;u-repost-of&#34; href=&#34;https://micro.paultibbetts.uk/2025/01/02/getting-my-own-domain.html&#34;&gt;microblog&lt;/a&gt; before this site existed and I wanted a copy of it here.
</source:markdown></item><item>
      <title>IndieWeb: Why</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/09/09/indieweb-why/</link>
      <pubDate>Tue, 09 Sep 2025 15:50:00 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/09/09/indieweb-why/</guid>
      <category>IndieWeb</category>
      <description>The IndieWeb is a people-focused alternative to the “corporate web”.
I’ve been a believer for over a decade.
Here’s why.</description>
      <content:encoded><![CDATA[<p>The <a href="https://indieweb.org/">IndieWeb</a>
 is a people-focused alternative to the &ldquo;corporate web&rdquo;.</p>
<p>I&rsquo;ve been a believer for over a decade.</p>
<p>Here&rsquo;s why.</p>
<h2 id="i-own-my-data">I own my data</h2>
<p>I used to use the social networks. They&rsquo;d make money from me being there, and that was my price of admission. Everybody did it. It was normal.</p>
<p>Except it isn&rsquo;t normal, and it shouldn&rsquo;t be thought of as normal.</p>
<p>We gave up our ownership for ease of use, and I think that&rsquo;s wrong.</p>
<p>The IndieWeb puts content ownership first.</p>
<h2 id="i-am-better-connected">I am better connected</h2>
<p>Since Musk bought Twitter I haven&rsquo;t been able to view any tweets on my phone. It complains about me using an adblocker and refuses to load the page, even though I&rsquo;m not using one.</p>
<p>I left Instagram before they stopped letting you include links in your posts.</p>
<p>And I&rsquo;ve ran out of free Medium articles to read this month, so I can&rsquo;t read your latest post on that platform.</p>
<p>But I&rsquo;ve never had a problem using other people&rsquo;s personal sites.</p>
<p>Everyone else being on the IndieWeb helps me, so I do the same for them.</p>
<h2 id="i-am-in-control">I am in control</h2>
<p>On the IndieWeb it&rsquo;s up to me to choose how I&rsquo;m represented online and what gets made available.</p>
<p>Which is how the web should be.</p>
<h2 id="i-want-the-web-to-be-more-open">I want the web to be more open</h2>
<p>The web was built on openness. Anyone could publish a page and send it to others.</p>
<p>Since then it&rsquo;s consolidated into a handful of platforms that dictate how things happen and how posts get seen, if they get seen at all.</p>
<p>Which means the IndieWeb is more than having your own site, it&rsquo;s a movement, to restore the web&rsquo;s original promise: to be a network that connects people.</p>
<h2 id="if-you-agree">If you agree</h2>
<p><a href="https://indieweb.org/Getting_Started">Here&rsquo;s how you can join in</a>
.</p>]]></content:encoded><source:markdown>
The [IndieWeb](https://indieweb.org/) is a people-focused alternative to the &#34;corporate web&#34;.

I&#39;ve been a believer for over a decade.

Here&#39;s why.

&lt;!--more--&gt;

## I own my data

I used to use the social networks. They&#39;d make money from me being there, and that was my price of admission. Everybody did it. It was normal.

Except it isn&#39;t normal, and it shouldn&#39;t be thought of as normal.

We gave up our ownership for ease of use, and I think that&#39;s wrong.

The IndieWeb puts content ownership first.

## I am better connected

Since Musk bought Twitter I haven&#39;t been able to view any tweets on my phone. It complains about me using an adblocker and refuses to load the page, even though I&#39;m not using one.

I left Instagram before they stopped letting you include links in your posts.

And I&#39;ve ran out of free Medium articles to read this month, so I can&#39;t read your latest post on that platform.

But I&#39;ve never had a problem using other people&#39;s personal sites.

Everyone else being on the IndieWeb helps me, so I do the same for them.

## I am in control

On the IndieWeb it&#39;s up to me to choose how I&#39;m represented online and what gets made available.

Which is how the web should be.

## I want the web to be more open

The web was built on openness. Anyone could publish a page and send it to others.

Since then it&#39;s consolidated into a handful of platforms that dictate how things happen and how posts get seen, if they get seen at all.

Which means the IndieWeb is more than having your own site, it&#39;s a movement, to restore the web&#39;s original promise: to be a network that connects people.

## If you agree

[Here&#39;s how you can join in](https://indieweb.org/Getting_Started).
</source:markdown></item><item>
      <title>Developer Environment Setup</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/07/07/developer-environment-setup/</link>
      <pubDate>Mon, 07 Jul 2025 16:36:59 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/07/07/developer-environment-setup/</guid>
      <category>dev-env</category>
      <category>digital garden</category>
      <description>I’ve pushed my developer environment setup scripts up to GitHub, and now they have their own little website to go with them.</description>
      <content:encoded><![CDATA[<p>I&rsquo;ve pushed my developer environment setup scripts up to GitHub, and now they have their own little website to go with them.</p>
<h2 id="dotfiles">Dotfiles</h2>
<p>Dotfiles are the files on your system that are hidden by default, which is done by adding a <code>.</code> in front of the name, hence &ldquo;dotfiles&rdquo;.</p>
<p>They&rsquo;re used for settings and preferences and between them they configure your system just the way you like it. They are arguably the most important part of a developer setup.</p>
<p>Lots of developers will store their dotfiles on GitHub, and <a href="https://dotfiles.github.io/bootstrap/">GitHub has a little site</a>
 telling you why and how to do so.</p>
<p>But they aren&rsquo;t enough to setup the whole system. What use is a dotfile that tells Git I want to use a certain tool for looking at diffs if that tool isn&rsquo;t even installed?</p>
<p>So together with my dotfiles are my setup scripts. They install the tools that the dotfiles reference and then they install the dotfiles. Between them my developer environment is prepared and ready to use.</p>
<h2 id="sharing-on-github">Sharing on GitHub</h2>
<p>I&rsquo;ve pushed my setup scripts up to GitHub. This is both a backup, in case I lose my local copy, and also part of the installation process.</p>
<p>If I had to setup a new machine from scratch I can download the install script from GitHub and use it to install the rest of the scripts. The script itself pulls other files down from GitHub, so it needs to be publicly available for me to do that.</p>
<p>But I also did it because I learnt so much about setting up my developer environment from other people sharing their setups and I wanted to do the same.</p>
<p>Usually it&rsquo;s just the code itself that&rsquo;s shared, since most devs will skip the readme and go straight to the code, but I added a little website as an experiment.</p>
<h2 id="digital-garden">Digital Garden</h2>
<p>The main reason I created a website for my scripts was to test out <a href="https://astro.build/">Astro</a>
 and <a href="https://starlight.astro.build/">Starlight</a>
 for creating documentation sites. It was pretty easy, and I liked the result, so I will most likely use them for something like this again in the future.</p>
<p>The other reason was to plant a seed in my digital garden.</p>
<p>Digital gardens are a bit like blogs, except they aren&rsquo;t usually sorted by date. They&rsquo;re more like notebooks that you share publicly. You might even go back to your notes and refine them over time, growing them from seedlings to full blown&hellip; idea bushes? I don&rsquo;t know what to call them. Some people call them trees, which can grow fruit. That sounds good. Let&rsquo;s go with that.</p>
<p>So the site is a seedbed in my digital garden. It&rsquo;s a place for me to share my notes on developer environment setups and dotfiles and stuff. It&rsquo;s not a project I want other people to use, in fact I&rsquo;ll probably write up what the alternatives are and which one you should pick instead of mine.</p>
<h2 id="seedling-">Seedling <span aria-hidden="true">🌱</span></h2>
<p>If you&rsquo;re curious you can <a href="https://dev.paultibbetts.uk/">check it out</a>
.</p>
<p>It&rsquo;s just a seedling right now, but who knows, maybe I can grow it into something fruitful.</p>]]></content:encoded><source:markdown>
I&#39;ve pushed my developer environment setup scripts up to GitHub, and now they have their own little website to go with them.

&lt;!--more--&gt;

## Dotfiles

Dotfiles are the files on your system that are hidden by default, which is done by adding a `.` in front of the name, hence &#34;dotfiles&#34;.

They&#39;re used for settings and preferences and between them they configure your system just the way you like it. They are arguably the most important part of a developer setup.

Lots of developers will store their dotfiles on GitHub, and [GitHub has a little site](https://dotfiles.github.io/bootstrap/) telling you why and how to do so.

But they aren&#39;t enough to setup the whole system. What use is a dotfile that tells Git I want to use a certain tool for looking at diffs if that tool isn&#39;t even installed?

So together with my dotfiles are my setup scripts. They install the tools that the dotfiles reference and then they install the dotfiles. Between them my developer environment is prepared and ready to use.

## Sharing on GitHub

I&#39;ve pushed my setup scripts up to GitHub. This is both a backup, in case I lose my local copy, and also part of the installation process.

If I had to setup a new machine from scratch I can download the install script from GitHub and use it to install the rest of the scripts. The script itself pulls other files down from GitHub, so it needs to be publicly available for me to do that.

But I also did it because I learnt so much about setting up my developer environment from other people sharing their setups and I wanted to do the same.

Usually it&#39;s just the code itself that&#39;s shared, since most devs will skip the readme and go straight to the code, but I added a little website as an experiment.

## Digital Garden

The main reason I created a website for my scripts was to test out [Astro](https://astro.build/) and [Starlight](https://starlight.astro.build/) for creating documentation sites. It was pretty easy, and I liked the result, so I will most likely use them for something like this again in the future.

The other reason was to plant a seed in my digital garden.

Digital gardens are a bit like blogs, except they aren&#39;t usually sorted by date. They&#39;re more like notebooks that you share publicly. You might even go back to your notes and refine them over time, growing them from seedlings to full blown... idea bushes? I don&#39;t know what to call them. Some people call them trees, which can grow fruit. That sounds good. Let&#39;s go with that.

So the site is a seedbed in my digital garden. It&#39;s a place for me to share my notes on developer environment setups and dotfiles and stuff. It&#39;s not a project I want other people to use, in fact I&#39;ll probably write up what the alternatives are and which one you should pick instead of mine.

## Seedling &lt;span aria-hidden=&#34;true&#34;&gt;🌱&lt;/span&gt;

If you&#39;re curious you can [check it out](https://dev.paultibbetts.uk/).

It&#39;s just a seedling right now, but who knows, maybe I can grow it into something fruitful.
</source:markdown></item><item>
      <title>Take Two</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/06/30/take-two/</link>
      <pubDate>Mon, 30 Jun 2025 21:31:09 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/06/30/take-two/</guid>
      <category>IndieWeb Carnival</category>
      <description>June’s IndieWeb Carnival prompt is “Take Two” and there’s a hundred different ways I could join in on this.
So here’s my first post joining in with an IndieWeb Carnival; Take Two.</description>
      <content:encoded><![CDATA[<p>June&rsquo;s <a href="https://indieweb.org/indieweb-carnival">IndieWeb Carnival</a>
 prompt is <a href="https://www.nicksimson.com/posts/2025-indieweb-carnival-take-two.html">&ldquo;Take Two&rdquo;</a>
 and there&rsquo;s a hundred different ways I could join in on this.</p>
<p>So here&rsquo;s my first post joining in with an IndieWeb Carnival; Take Two.</p>
<h2 id="regrets">Regrets?</h2>
<p>Right off the bat I&rsquo;ll say yes, there are plenty of things I&rsquo;d have liked a second take at.</p>
<p>I don&rsquo;t see this as bad, to me this is growth. Looking back on something and seeing how you could have done it better shows wisdom and maturity.</p>
<p>It&rsquo;d be easy to get stuck here though, wallowing in sadness and longing for what you could have had, so you have to accept it as take one and move on, which is the theme I&rsquo;ll be sticking with for this post.</p>
<h2 id="take-two-with-tech">Take Two with Tech</h2>
<p>I have a friend who wants to get into tech. They&rsquo;ve tried before, and it didn&rsquo;t stick, so this is their take two, and I want to let them know I&rsquo;m proud of them for coming back.</p>
<p>It isn&rsquo;t easy changing careers, or even considering it, when you have bills to pay and a lifestyle to maintain, but with tech it&rsquo;s possible.</p>
<p>I&rsquo;d even say a second take in tech is encouraged. Let&rsquo;s look at Apple for a second. They didn&rsquo;t invent the MP3 player, but they had a very popular second take on it.</p>
<p>First impressions matter and all that, but I think what counts in tech is the second take, and then the third, and all the improvements that reveal themselves with each iteration. My dad would probably include a reference here to WD-40 and the 39 complete and utter failures that came before it.</p>
<h2 id="personal-takes">Personal Takes</h2>
<p>You&rsquo;re reading this on take <del>seven</del> two of my blog, something that could have been running for 10 years if I had stuck at it when I started it.</p>
<p>I&rsquo;m no better with <a href="https://micro.paultibbetts.uk">microblogging</a>
, which constantly feels like take two for me. My &ldquo;how I got my website online&rdquo; series stalled after the first post and needs redoing.</p>
<p>This week I am finishing off some work I started last week. It was only meant to take a few days, but I didn&rsquo;t know what I was doing, so I had to make something just to find out what it was I was meant to be making in the first place.</p>
<p>All this whilst taking a second break from work. I tried it last year, went back to my job and then proceeded to burn myself out all over again.</p>
<p>So here I am, a year later, Taking a Break From Work and Switching Things Up: Take Two.</p>
<h2 id="kaizen-改善">Kaizen (改善)</h2>
<p>So it&rsquo;s on to the next iteration.</p>
<p>And then the next.</p>
<p>As long as I&rsquo;m continuously learning, always aiming to do better, that&rsquo;s ok with me.</p>]]></content:encoded><source:markdown>
June&#39;s [IndieWeb Carnival](https://indieweb.org/indieweb-carnival) prompt is [&#34;Take Two&#34;](https://www.nicksimson.com/posts/2025-indieweb-carnival-take-two.html) and there&#39;s a hundred different ways I could join in on this.

So here&#39;s my first post joining in with an IndieWeb Carnival; Take Two.

&lt;!--more--&gt;

## Regrets?

Right off the bat I&#39;ll say yes, there are plenty of things I&#39;d have liked a second take at.

I don&#39;t see this as bad, to me this is growth. Looking back on something and seeing how you could have done it better shows wisdom and maturity.

It&#39;d be easy to get stuck here though, wallowing in sadness and longing for what you could have had, so you have to accept it as take one and move on, which is the theme I&#39;ll be sticking with for this post.

## Take Two with Tech

I have a friend who wants to get into tech. They&#39;ve tried before, and it didn&#39;t stick, so this is their take two, and I want to let them know I&#39;m proud of them for coming back.

It isn&#39;t easy changing careers, or even considering it, when you have bills to pay and a lifestyle to maintain, but with tech it&#39;s possible.

I&#39;d even say a second take in tech is encouraged. Let&#39;s look at Apple for a second. They didn&#39;t invent the MP3 player, but they had a very popular second take on it.

First impressions matter and all that, but I think what counts in tech is the second take, and then the third, and all the improvements that reveal themselves with each iteration. My dad would probably include a reference here to WD-40 and the 39 complete and utter failures that came before it.

## Personal Takes

You&#39;re reading this on take ~~seven~~ two of my blog, something that could have been running for 10 years if I had stuck at it when I started it.

I&#39;m no better with [microblogging](https://micro.paultibbetts.uk), which constantly feels like take two for me. My &#34;how I got my website online&#34; series stalled after the first post and needs redoing.

This week I am finishing off some work I started last week. It was only meant to take a few days, but I didn&#39;t know what I was doing, so I had to make something just to find out what it was I was meant to be making in the first place.

All this whilst taking a second break from work. I tried it last year, went back to my job and then proceeded to burn myself out all over again.

So here I am, a year later, Taking a Break From Work and Switching Things Up: Take Two.

## Kaizen (改善)

So it&#39;s on to the next iteration.

And then the next.

As long as I&#39;m continuously learning, always aiming to do better, that&#39;s ok with me.
</source:markdown></item><item>
      <title>Homelab V1</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/06/13/homelab-v1/</link>
      <pubDate>Fri, 13 Jun 2025 16:00:00 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/06/13/homelab-v1/</guid>
      <category>homelab</category>
      <description>My Homelab has hit its first milestone 🚀</description>
      <content:encoded><![CDATA[<p>My Homelab has hit its first milestone 🚀</p>
<h2 id="cloud">Cloud</h2>
<p>The biggest problem with my homelab was that I kept breaking it.</p>
<p>I couldn&rsquo;t get used to using <a href="https://mealie.io/">mealie</a>
 as a meal planner because I would go to use it and it didn&rsquo;t work. Or it did, but I couldn&rsquo;t connect to it properly. Or something else went wrong.</p>
<p>So for my first &ldquo;release&rdquo; I aimed for a &ldquo;cloud&rdquo;. Some people call it &ldquo;HomeProd&rdquo;.</p>
<p>I used things I was used to, and a few things I wasn&rsquo;t, to make a simple, stable platform on which I could host my <em>actual</em> homelab.</p>
<p>Here&rsquo;s what&rsquo;s in it:</p>
<h3 id="nas">NAS</h3>
<p>I have a machine dedicated to running <a href="https://www.truenas.com/truenas-scale/">TrueNAS Scale</a>
.</p>
<p>I bought the hype that I needed <a href="https://www.truenas.com/docs/scale/gettingstarted/scalehardwareguide/">ECC RAM</a>
 so that my data would be safe and so I also needed a server-grade processor, which meant a server-grade motherboard, and all the other bits to go with it.</p>
<p>At the time Scale had built-in Kubernetes so I used TrueCharts to set up some apps. And then they stopped working. And then there were all the updates with breaking changes. And then I got fed up.</p>
<p>This was meant to be a machine that did storage and I didn&rsquo;t want to mess with it.</p>
<p>The TrueNAS devs thought the same thing and took out Kubernetes and replaced it with Docker, so I updated to follow and now I only run two storage apps on it.</p>
<h4 id="usage">Usage</h4>
<ul>
<li><strong>file storage</strong> over <strong>NFS</strong> to Linux and Mac and <strong>SMB</strong> for Windows</li>
<li><strong>object storage</strong> via <a href="https://min.io">MinIO</a>
 installed as a Docker container</li>
<li><strong>backups</strong> for virtual machines via <a href="https://www.proxmox.com/en/products/proxmox-backup-server/overview">Proxmox Backup Server</a>
</li>
</ul>
<p>and <strong>nothing else</strong>.</p>
<p>I just want it to do network attached storage, and I don&rsquo;t want to mess with it.</p>
<h4 id="storage">Storage</h4>
<ul>
<li>2 NVME for 4TB of fast storage</li>
<li>1 SSD for 500GB internal storage</li>
<li>4 HDD RAIDZ2 for 10TB of slow storage</li>
</ul>
<h3 id="nuc">NUC</h3>
<p>After getting burned trying to do server things on my NAS I looked for a replacement and found the <a href="https://turingpi.com">Turing Pi</a>
, and then in true Kickstarter fashion it got delayed, and then delayed again.</p>
<p>I impatiently bought an Intel NUC.</p>
<p>I <em>could</em> have installed Linux on it and used it as a server but I wanted to use Terraform for Infrastructure as Code, so instead I installed <a href="https://www.proxmox.com/en/products/proxmox-virtual-environment/overview">Proxmox VE</a>
 and used Terraform to create the virtual machines and LXC containers I needed with code.</p>
<p>This meant I could get some practise in using <a href="https://docs.ansible.com/ansible/latest/index.html">Ansible</a>
 to configure things and try out <a href="https://k3s.io">K3s</a>
 to run Kubernetes.</p>
<h4 id="usage-1">Usage</h4>
<ul>
<li><strong>compute</strong> for running services and apps</li>
<li>experience using <strong>Proxmox</strong></li>
<li>practise using IaC with <strong>Terraform</strong> and <strong>Ansible</strong></li>
<li>a place to try out <strong>Kubernetes</strong> stuff</li>
<li>a better GPU than the NAS for <strong>Jellyfin</strong> to use for transcoding</li>
</ul>
<h4 id="services">Services</h4>
<ul>
<li><strong><a href="https://pi-hole.net/">Pi-hole</a>
</strong> for blocking adverts as well as DNS and DHCP for my network</li>
<li><strong><a href="https://www.pivpn.io/">Wireguard</a>
</strong> for a VPN so I can connect from out the house</li>
<li><strong>MySQL</strong> and <strong>Postgres</strong> databases for &ldquo;cloud&rdquo; things to use</li>
<li><strong><a href="https://caddyserver.com/">Caddy</a>
</strong> as a reverse proxy to provide access to my &ldquo;cloud&rdquo; apps and services</li>
<li><strong><a href="https://about.gitea.com/">Gitea</a>
</strong> for storing my git repositories</li>
<li><strong><a href="https://jellyfin.org/">Jellyfin</a>
</strong> to watch my own media content</li>
</ul>
<h4 id="apps">Apps</h4>
<ul>
<li><strong><a href="https://mealie.io/">Mealie</a>
</strong> for recipe management and meal planning</li>
<li><strong><a href="https://www.freshrss.org/">FreshRSS</a>
</strong> for my RSS clients to sync from</li>
</ul>
<h3 id="deskpi">DeskPi</h3>
<p>The last part of my &ldquo;cloud&rdquo; is the Raspberry Pi 4 I have on my desk.</p>
<p>(I named it before I heard about the company with the same name)</p>
<p>I had future plans for it, since it&rsquo;s sat on my desk, so I installed the desktop version of Ubuntu and hooked it up to my second screen.</p>
<p>Then I installed <a href="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/06/13/homelab-v1/">Uptime Kuma</a>
 on it to monitor all my services and apps.</p>
<p>Apparently I cheaped out when I bought it and got the 2GB version, so it&rsquo;s really really slow and struggles with showing the dashboard.</p>
<p><img src="https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/06/13/homelab-v1/uptime-kuma-status-page.png" alt="screenshot of Uptime Kuma status page">
</p>
<p>I&rsquo;ve been looking for a good excuse to buy a Raspberry Pi 5 but technically this works, so for now it can stay.</p>
<h2 id="lab">Lab</h2>
<p>Now that the stable self-hosted part is out of the way I&rsquo;m going to leave it alone and use something else for the new ideas.</p>
<p>My Turing Pi 2.5 has finally arrived, along with its case, 3 x <a href="https://turingpi.com/product/turing-rk1/?attribute_ram=8&#43;GB">RK1</a>
 compute modules and 3 x 1 TB NVME drives. I&rsquo;m planning on using it as a cluster to run Kubernetes and potentially host my own website, but we&rsquo;ll see about that.</p>
<p>Right now it&rsquo;s on the bench in the lab.</p>]]></content:encoded><source:markdown>
My Homelab has hit its first milestone 🚀

&lt;!--more--&gt;

## Cloud

The biggest problem with my homelab was that I kept breaking it.

I couldn&#39;t get used to using [mealie](https://mealie.io/) as a meal planner because I would go to use it and it didn&#39;t work. Or it did, but I couldn&#39;t connect to it properly. Or something else went wrong.

So for my first &#34;release&#34; I aimed for a &#34;cloud&#34;. Some people call it &#34;HomeProd&#34;.

I used things I was used to, and a few things I wasn&#39;t, to make a simple, stable platform on which I could host my _actual_ homelab.

Here&#39;s what&#39;s in it:

### NAS

I have a machine dedicated to running [TrueNAS Scale](https://www.truenas.com/truenas-scale/).

I bought the hype that I needed [ECC RAM](https://www.truenas.com/docs/scale/gettingstarted/scalehardwareguide/) so that my data would be safe and so I also needed a server-grade processor, which meant a server-grade motherboard, and all the other bits to go with it.

At the time Scale had built-in Kubernetes so I used TrueCharts to set up some apps. And then they stopped working. And then there were all the updates with breaking changes. And then I got fed up.

This was meant to be a machine that did storage and I didn&#39;t want to mess with it.

The TrueNAS devs thought the same thing and took out Kubernetes and replaced it with Docker, so I updated to follow and now I only run two storage apps on it.

#### Usage

- **file storage** over **NFS** to Linux and Mac and **SMB** for Windows
- **object storage** via [MinIO](https://min.io) installed as a Docker container
- **backups** for virtual machines via [Proxmox Backup Server](https://www.proxmox.com/en/products/proxmox-backup-server/overview)

and **nothing else**.

I just want it to do network attached storage, and I don&#39;t want to mess with it.

#### Storage

- 2 NVME for 4TB of fast storage
- 1 SSD for 500GB internal storage
- 4 HDD RAIDZ2 for 10TB of slow storage

### NUC

After getting burned trying to do server things on my NAS I looked for a replacement and found the [Turing Pi](https://turingpi.com), and then in true Kickstarter fashion it got delayed, and then delayed again.

I impatiently bought an Intel NUC.

I _could_ have installed Linux on it and used it as a server but I wanted to use Terraform for Infrastructure as Code, so instead I installed [Proxmox VE](https://www.proxmox.com/en/products/proxmox-virtual-environment/overview) and used Terraform to create the virtual machines and LXC containers I needed with code.

This meant I could get some practise in using [Ansible](https://docs.ansible.com/ansible/latest/index.html) to configure things and try out [K3s](https://k3s.io) to run Kubernetes.

#### Usage

- **compute** for running services and apps
- experience using **Proxmox**
- practise using IaC with **Terraform** and **Ansible**
- a place to try out **Kubernetes** stuff
- a better GPU than the NAS for **Jellyfin** to use for transcoding

#### Services

- **[Pi-hole](https://pi-hole.net/)** for blocking adverts as well as DNS and DHCP for my network
- **[Wireguard](https://www.pivpn.io/)** for a VPN so I can connect from out the house
- **MySQL** and **Postgres** databases for &#34;cloud&#34; things to use
- **[Caddy](https://caddyserver.com/)** as a reverse proxy to provide access to my &#34;cloud&#34; apps and services
- **[Gitea](https://about.gitea.com/)** for storing my git repositories
- **[Jellyfin](https://jellyfin.org/)** to watch my own media content

#### Apps

- **[Mealie](https://mealie.io/)** for recipe management and meal planning
- **[FreshRSS](https://www.freshrss.org/)** for my RSS clients to sync from

### DeskPi

The last part of my &#34;cloud&#34; is the Raspberry Pi 4 I have on my desk.

(I named it before I heard about the company with the same name)

I had future plans for it, since it&#39;s sat on my desk, so I installed the desktop version of Ubuntu and hooked it up to my second screen.

Then I installed [Uptime Kuma]() on it to monitor all my services and apps.

Apparently I cheaped out when I bought it and got the 2GB version, so it&#39;s really really slow and struggles with showing the dashboard.

![screenshot of Uptime Kuma status page](uptime-kuma-status-page.png)

I&#39;ve been looking for a good excuse to buy a Raspberry Pi 5 but technically this works, so for now it can stay.

## Lab

Now that the stable self-hosted part is out of the way I&#39;m going to leave it alone and use something else for the new ideas.

My Turing Pi 2.5 has finally arrived, along with its case, 3 x [RK1](https://turingpi.com/product/turing-rk1/?attribute_ram=8+GB) compute modules and 3 x 1 TB NVME drives. I&#39;m planning on using it as a cluster to run Kubernetes and potentially host my own website, but we&#39;ll see about that.

Right now it&#39;s on the bench in the lab.
</source:markdown></item><item>
      <title>Using Terraform as the Inventory for Ansible</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/06/13/using-terraform-as-the-inventory-for-ansible/</link>
      <pubDate>Fri, 13 Jun 2025 11:30:21 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/06/13/using-terraform-as-the-inventory-for-ansible/</guid>
      <category>Ansible</category>
      <category>Terraform</category>
      <description>Terraform and Ansible are complementary tools with which you can do Infrastructure as Code. You would use Terraform to request machines from providers and then Ansible to configure them.
Using both of them together, with a dynamic inventory to link them, has been technically possible for years but never obvious enough for me to work out.
Until I found the Terraform provider.</description>
      <content:encoded><![CDATA[<p>Terraform and Ansible are complementary tools with which you can do Infrastructure as Code. You would use <a href="https://www.terraform.io/">Terraform</a>
 to request machines from providers and then <a href="https://docs.ansible.com/ansible/latest/index.html">Ansible</a>
 to configure them.</p>
<p>Using both of them together, with a dynamic inventory to link them, has been technically possible for years but never obvious enough for me to work out.</p>
<p>Until I found the Terraform provider.</p>
<h2 id="ansible-provider-for-terraform">Ansible provider for Terraform</h2>
<p>By using the <a href="https://registry.terraform.io/providers/ansible/ansible/latest">Ansible provider</a>
 you can teach Terraform what an Ansible inventory entry would look like next to the rest of its code.</p>
<p>You start by defining the provider:</p>
<pre><code class="language-terraform">terraform {
  required_providers {
    ansible = {
      source  = &#34;ansible/ansible&#34;
      version = &#34;1.3.0&#34;
    }
  }
}</code></pre>
<p>and then you can define the Ansible inventory entry right next to the resource:</p>
<pre><code class="language-terraform">resource &#34;proxmox_vm_qemu&#34; &#34;gitea&#34; {
  cores = 1
	...
}

resource &#34;ansible_host&#34; &#34;gitea&#34; {
  name   = proxmox_vm_qemu.gitea.ssh_host
  groups = [&#34;gitea&#34;]
  variables = {
    ansible_user = &#34;ansible&#34;
  }
}</code></pre>
<p>which means you don&rsquo;t need to hardcode IP addresses anywhere.</p>
<p>You would need to <code>terraform apply</code> this plan so that Terraform adds these resources to the state file so that Ansible can use them.</p>
<h2 id="terraform-plugin-for-ansible">Terraform plugin for Ansible</h2>
<p>On the Ansible side you use <a href="https://github.com/ansible-collections/cloud.terraform">a plugin</a>
 to tell it how to use Terraform as its inventory.</p>
<p>Add the following to your <code>requirements.yaml</code> file:</p>
<pre><code class="language-yaml">collections:
  - name: cloud.terraform
    version: 1.1.0</code></pre>
<p>and then install the collection using <code>ansible-galaxy collection install -r requirements.yaml</code>,</p>
<p>then create an <code>inventory.yaml</code> with the following content:</p>
<pre><code class="language-yaml">plugin: cloud.terraform.terraform_provider
# project_path: ../terraform # if your Terraform code is in a different directory
# state_file: mycustomstate.tfstate # if you wanted to define which state file</code></pre>
<p>and set it as the default inventory in <code>ansible.cfg</code>:</p>
<pre><code class="language-ini">[defaults]
inventory = inventory.yaml</code></pre>
<p>You can confirm the plugin works by doing:</p>
<pre><code class="language-sh">ansible-inventory --graph</code></pre>
<p>which should return something like the following:</p>
<pre><code class="language-sh">@all:
  |--@ungrouped:
  |--@gitea:
  |  |--192.168.1.211</code></pre>
<h2 id="heading">🎉</h2>
<p>What&rsquo;s good about this is now you can be hands-off with IP addresses.</p>
<p>Terraform can request a new machine, wait for it to receive an IP address and then make it available for Ansible to use, without you needing to do anything.</p>
<h3 id="remote-state">Remote state</h3>
<p>The Ansible plugin will default to using whichever backend you use for the Terraform state file, whether that&rsquo;s local or in an S3-compatible location.</p>
<p>This means Ansible is always running against the current infrastructure, which helps when colleagues and automations are updating things a <del>hundred</del> thousand times a day.</p>
<h2 id="in-conclusion">In Conclusion</h2>
<p>Using Terraform and Ansible together can cover all aspects of Infrastructure as Code.</p>
<p>The Ansible provider lets you keep all your resource code together in Terraform and then makes it available to Ansible, which uses a plugin to let it use the state file as its inventory.</p>]]></content:encoded><source:markdown>
Terraform and Ansible are complementary tools with which you can do Infrastructure as Code. You would use [Terraform](https://www.terraform.io/) to request machines from providers and then [Ansible](https://docs.ansible.com/ansible/latest/index.html) to configure them.

Using both of them together, with a dynamic inventory to link them, has been technically possible for years but never obvious enough for me to work out.

Until I found the Terraform provider.

&lt;!--more--&gt;

## Ansible provider for Terraform

By using the [Ansible provider](https://registry.terraform.io/providers/ansible/ansible/latest) you can teach Terraform what an Ansible inventory entry would look like next to the rest of its code.

You start by defining the provider:

```terraform
terraform {
  required_providers {
    ansible = {
      source  = &#34;ansible/ansible&#34;
      version = &#34;1.3.0&#34;
    }
  }
}
```

and then you can define the Ansible inventory entry right next to the resource:

```terraform
resource &#34;proxmox_vm_qemu&#34; &#34;gitea&#34; {
  cores = 1
	...
}

resource &#34;ansible_host&#34; &#34;gitea&#34; {
  name   = proxmox_vm_qemu.gitea.ssh_host
  groups = [&#34;gitea&#34;]
  variables = {
    ansible_user = &#34;ansible&#34;
  }
}
```

which means you don&#39;t need to hardcode IP addresses anywhere.

You would need to `terraform apply` this plan so that Terraform adds these resources to the state file so that Ansible can use them.

## Terraform plugin for Ansible

On the Ansible side you use [a plugin](https://github.com/ansible-collections/cloud.terraform) to tell it how to use Terraform as its inventory.

Add the following to your `requirements.yaml` file:

```yaml
collections:
  - name: cloud.terraform
    version: 1.1.0
```

and then install the collection using `ansible-galaxy collection install -r requirements.yaml`,

then create an `inventory.yaml` with the following content:

```yaml
plugin: cloud.terraform.terraform_provider
# project_path: ../terraform # if your Terraform code is in a different directory
# state_file: mycustomstate.tfstate # if you wanted to define which state file
```

and set it as the default inventory in `ansible.cfg`:

```ini
[defaults]
inventory = inventory.yaml
```

You can confirm the plugin works by doing:

```sh
ansible-inventory --graph
```

which should return something like the following:

```sh
@all:
  |--@ungrouped:
  |--@gitea:
  |  |--192.168.1.211
```

## 🎉

What&#39;s good about this is now you can be hands-off with IP addresses.

Terraform can request a new machine, wait for it to receive an IP address and then make it available for Ansible to use, without you needing to do anything.

### Remote state

The Ansible plugin will default to using whichever backend you use for the Terraform state file, whether that&#39;s local or in an S3-compatible location.

This means Ansible is always running against the current infrastructure, which helps when colleagues and automations are updating things a ~~hundred~~ thousand times a day.

## In Conclusion

Using Terraform and Ansible together can cover all aspects of Infrastructure as Code.

The Ansible provider lets you keep all your resource code together in Terraform and then makes it available to Ansible, which uses a plugin to let it use the state file as its inventory.
</source:markdown></item><item>
      <title>Homelab v0</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/06/04/homelab-v0/</link>
      <pubDate>Wed, 04 Jun 2025 22:30:59 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/06/04/homelab-v0/</guid>
      <category>homelab</category>
      <description>As my homelab is about to reach a milestone I thought I’d look back on where it started.</description>
      <content:encoded><![CDATA[<p>As my homelab is about to reach a milestone I thought I&rsquo;d look back on where it started.</p>
<h2 id="whats-a-homelab">What&rsquo;s a homelab?</h2>
<p>A homelab is a personal setup of computer stuff that some of us spend way too much time and money on.</p>
<h2 id="why-would-i-want-one">Why would I want one?</h2>
<p>Here&rsquo;s a few reasons:</p>
<h3 id="learning">Learning</h3>
<p>The biggest reason I wanted a homelab was for learning. I can get hands-on experience with tech I wouldn&rsquo;t normally use, and as a DevOps Engineer that often ends up being useful at work.</p>
<h3 id="applications">Applications</h3>
<p>There are loads of open source apps you can run for free at home.</p>
<p>You can <a href="https://pi-hole.net">block adverts</a>
, <a href="https://jellyfin.org">run your own Netflix</a>
 and <a href="https://www.awesome-homelab.com/">plenty more</a>
.</p>
<h3 id="services">Services</h3>
<p>You could backup your devices and centralise storage with a NAS, then setup a VPN and connect to it from anywhere.</p>
<h2 id="how-do-i-get-one">How do I get one?</h2>
<p>The biggest thing I want you to take away from this is that you already have one.</p>
<blockquote>
<p>the homelab was inside you all along</p>
</blockquote>
<p>Pretend for a moment you&rsquo;re a mad scientist, hacking away at some computery stuff. There are several blinking lights and a faint computery hum fills the air.</p>
<p>What kind of computery stuff did you imagine?</p>
<p>Did it involve loads of blinky lights? You might be interested in networking. I bought a <a href="https://techspecs.ui.com/unifi/switching/unifi-flex-xg?s=uk">Ubiquiti switch</a>
 and it&rsquo;s way blinkier than my old one. It&rsquo;s also 10 times faster.</p>
<p>Did you see yourself coding? Maybe you could setup your own git server, add on CI/CD and get your apps hosted on your own network. That&rsquo;s what I&rsquo;m doing.</p>
<p>Or maybe you want to try out AI on your own hardware?</p>
<p>Whatever it is, you can probably get started right now with the things that you already have. You don&rsquo;t need to go and buy anything just yet. Start acting like a mad scientist doing computery stuff and eventually you&rsquo;ll become one.</p>
<p>When I wanted to learn about Docker images I used my own laptop. Then when I was given a Raspberry Pi I tested out running Docker images on that and so my homelab doubled in size.</p>
<p>I didn&rsquo;t realise it was called a homelab at the time. I was just doing computery stuff.</p>
<h2 id="my-homelab-v0">My Homelab v0</h2>
<p><strong>Macbook Pro</strong> (2012)</p>
<p><strong>Raspberry Pi 1</strong></p>
<p><strong>Notebook</strong></p>
<p><strong>Pen</strong> for writing it all down, otherwise it&rsquo;s not science.</p>]]></content:encoded><source:markdown>
As my homelab is about to reach a milestone I thought I&#39;d look back on where it started.

&lt;!--more--&gt;

## What&#39;s a homelab?

A homelab is a personal setup of computer stuff that some of us spend way too much time and money on.

## Why would I want one?

Here&#39;s a few reasons:

### Learning

The biggest reason I wanted a homelab was for learning. I can get hands-on experience with tech I wouldn&#39;t normally use, and as a DevOps Engineer that often ends up being useful at work.

### Applications

There are loads of open source apps you can run for free at home.

You can [block adverts](https://pi-hole.net), [run your own Netflix](https://jellyfin.org) and [plenty more](https://www.awesome-homelab.com/).

### Services

You could backup your devices and centralise storage with a NAS, then setup a VPN and connect to it from anywhere.

## How do I get one?

The biggest thing I want you to take away from this is that you already have one.

&gt; the homelab was inside you all along

Pretend for a moment you&#39;re a mad scientist, hacking away at some computery stuff. There are several blinking lights and a faint computery hum fills the air.

What kind of computery stuff did you imagine?

Did it involve loads of blinky lights? You might be interested in networking. I bought a [Ubiquiti switch](https://techspecs.ui.com/unifi/switching/unifi-flex-xg?s=uk) and it&#39;s way blinkier than my old one. It&#39;s also 10 times faster.

Did you see yourself coding? Maybe you could setup your own git server, add on CI/CD and get your apps hosted on your own network. That&#39;s what I&#39;m doing.

Or maybe you want to try out AI on your own hardware?

Whatever it is, you can probably get started right now with the things that you already have. You don&#39;t need to go and buy anything just yet. Start acting like a mad scientist doing computery stuff and eventually you&#39;ll become one.

When I wanted to learn about Docker images I used my own laptop. Then when I was given a Raspberry Pi I tested out running Docker images on that and so my homelab doubled in size.

I didn&#39;t realise it was called a homelab at the time. I was just doing computery stuff.

## My Homelab v0

**Macbook Pro** (2012)

**Raspberry Pi 1**

**Notebook**

**Pen** for writing it all down, otherwise it&#39;s not science.
</source:markdown></item><item>
      <title>Technical Difficulties</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/06/02/technical-difficulties/</link>
      <pubDate>Mon, 02 Jun 2025 14:30:00 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/06/02/technical-difficulties/</guid>
      <category>meta</category>
      <description>I don’t like starting sentences with I.</description>
      <content:encoded><![CDATA[<p>I don&rsquo;t like starting sentences with I.</p>
<p>There were also a few real technical difficulties starting a blog, which were more frustrating because usually I was the one who had caused them, but they were fixed pretty quickly.</p>
<p>No, the biggest problem came instead from me, not getting past the awkward part and making it a regular habit.</p>
<p>I made a slight effort earlier this year publishing a <a href="https://micro.paultibbetts.uk/2025/01/02/the-plan.html">&ldquo;plan&rdquo;</a>
 on Micro.blog but apparently it wasn&rsquo;t the right time and I didn&rsquo;t follow it up.</p>
<p>I was already aware of it, and making an effort to fix it, but I was burnt out.</p>
<p>What I didn&rsquo;t write in my &ldquo;plan&rdquo; was that I would be taking some time off work for a while to recover. It&rsquo;s not like I was stuck in bed, unable to move, but I had lost interest in coding and computers, which isn&rsquo;t great when that&rsquo;s what you do for money.</p>
<p>So I handed in my notice and took a break.</p>
<hr>
<p>Two months later and I&rsquo;m definitely feeling better.</p>
<p>The fact you are reading this at all is a testament to my newly rediscovered ability to sit down at a computer and not hate it.</p>
<p>I&rsquo;ve also had the time to work on my homelab, on which I&rsquo;ve made more progress in the last two weeks than I have in the last two years.</p>
<p>So maybe now is a good time to start blogging.</p>]]></content:encoded><source:markdown>
I don&#39;t like starting sentences with I.

&lt;!--more--&gt;

There were also a few real technical difficulties starting a blog, which were more frustrating because usually I was the one who had caused them, but they were fixed pretty quickly.

No, the biggest problem came instead from me, not getting past the awkward part and making it a regular habit.

I made a slight effort earlier this year publishing a [&#34;plan&#34;](https://micro.paultibbetts.uk/2025/01/02/the-plan.html) on Micro.blog but apparently it wasn&#39;t the right time and I didn&#39;t follow it up.

I was already aware of it, and making an effort to fix it, but I was burnt out.

What I didn&#39;t write in my &#34;plan&#34; was that I would be taking some time off work for a while to recover. It&#39;s not like I was stuck in bed, unable to move, but I had lost interest in coding and computers, which isn&#39;t great when that&#39;s what you do for money.

So I handed in my notice and took a break.

---

Two months later and I&#39;m definitely feeling better.

The fact you are reading this at all is a testament to my newly rediscovered ability to sit down at a computer and not hate it.

I&#39;ve also had the time to work on my homelab, on which I&#39;ve made more progress in the last two weeks than I have in the last two years.

So maybe now is a good time to start blogging.
</source:markdown></item><item>
      <title>Hello, World!</title>
      <link>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/05/20/hello-world/</link>
      <pubDate>Tue, 20 May 2025 12:54:11 +0100</pubDate>
      <guid>https://paultibbetts-uk-feat-cooks.preview.lab.paultibbetts.uk/2025/05/20/hello-world/</guid>
      <category>meta</category>
      <description>👋</description>
      <content:encoded><![CDATA[<p>👋</p>]]></content:encoded><source:markdown>
👋
</source:markdown></item>
  </channel>
</rss>
