{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "Series of posts about my homelab",
  "home_page_url": "https://pype.dev/homelab/",
  "feed_url": "https://pype.dev/homelab/feed.json",
  "description": "My thoughts and streams of consciousness organized into barely coherent posts about things",
  "authors": [
    {
      "name": "Nic Payne"
    }
  ],
  "items": [
    {
      "id": "https://pype.dev/harbor-faulted-disk-replacement/",
      "url": "https://pype.dev/harbor-faulted-disk-replacement/",
      "title": "The Faulted Disk: harbor Replacement Writeup",
      "content_html": "\u003cp\u003eThe sequel to \u003ca href=\"/panicking-led-to-losing-my-desktop\"\u003epanicking-led-to-losing-my-desktop\u003c/a\u003e — this time the monitoring actually caught the disk dying, and nothing was lost.\u003c/p\u003e\n\u003ch2 id=\"what-happened\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eWhat happened\u003c/span\u003e \u003ca href=\"#what-happened\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eharbor\u003c/code\u003e is my replica pool — a 10.9T mirror (2x 12TB) that receives syncoid snapshots from \u003ccode\u003etank\u003c/code\u003e. One side of the mirror, a Seagate Exos \u003ccode\u003eST12000NM0127\u003c/code\u003e (serial \u003ccode\u003eZJV4QFLB\u003c/code\u003e, \u003ccode\u003e/dev/sdb\u003c/code\u003e), went \u003cstrong\u003eFAULTED\u003c/strong\u003e with 14 read + 22 checksum errors.\u003c/p\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emirror-0                              DEGRADED     0     0     0\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  ata-ST12000VN0008-2PH103_ZTM0NFDW  ONLINE       0     0     0\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  ata-ST12000NM0127_ZJV4QFLB         FAULTED     14     0    22  too many errors\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe IronWolf mirror side carried the pool — \u003ccode\u003eNo known data errors\u003c/code\u003e. ZFS redundancy did exactly its job.\u003c/p\u003e\n\u003ch2 id=\"the-difference-from-last-time\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eThe difference from last time\u003c/span\u003e \u003ca href=\"#the-difference-from-last-time\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eLast failure: no monitoring, found out by accident months later, desktop died.\u003c/p\u003e\n\u003cp\u003eThis failure: SigNoz + node-exporter\u0026rsquo;s ZFS collector → \u003ccode\u003enode_zfs_zpool_state{state=\u0026quot;degraded\u0026quot;}\u003c/code\u003e → alert rule → Gotify → my phone. The gotify notification fired \u003cem\u003ebefore\u003c/em\u003e I knew anything was wrong.\u003c/p\u003e\n\u003ch2 id=\"diagnosis\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eDiagnosis\u003c/span\u003e \u003ca href=\"#diagnosis\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eBefore \u003ccode\u003ezpool clear\u003c/code\u003e or replace — check SMART. \u003ccode\u003esmartctl-exporter\u003c/code\u003e already scrapes all disks into SigNoz, so I didn\u0026rsquo;t even need sudo:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eReallocated_Sector_Ct\u003c/code\u003e raw = \u003cstrong\u003e3024\u003c/strong\u003e and counting\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eOffline_Uncorrectable\u003c/code\u003e value/worst still 100 but raw errors climbing\u003c/li\u003e\n\u003cli\u003eSMART overall: still PASS (SMART\u0026rsquo;s overall bit is conservative until threshold)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e3k+ remapped sectors is a platter going bad — not a cable blip. Verdict: replace, don\u0026rsquo;t clear.\u003c/p\u003e\n\u003ch2 id=\"the-swap-the-annoying-part\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eThe swap (the annoying part)\u003c/span\u003e \u003ca href=\"#the-swap-the-annoying-part\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eHot-plug is never as smooth as it should be:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003ccode\u003ezpool offline harbor \u0026lt;old\u0026gt;\u003c/code\u003e to stop writes to the dead drive — actually skipped; drive fell off the bus on its own\u003c/li\u003e\n\u003cli\u003eNew IronWolf \u003ccode\u003eZTN1CQ07\u003c/code\u003e in hand → plugged into ghost\u0026rsquo;s SATA → \u003cstrong\u003edidn\u0026rsquo;t enumerate\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003eForced rescan (\u003ccode\u003eecho \u0026quot;- - -\u0026quot; | sudo tee /sys/class/scsi_host/host*/scan\u003c/code\u003e) → nothing\u003c/li\u003e\n\u003cli\u003eMoved it to the \u003cem\u003eold drive\u0026rsquo;s port\u003c/em\u003e → nothing\u003c/li\u003e\n\u003cli\u003eSabrent USB dock on aurora → dock enumerated as \u003ccode\u003e0B\u003c/code\u003e device, no disk behind it → reseated + replugged → \u003cstrong\u003edrive spun up and appeared\u003c/strong\u003e: \u003ccode\u003e/dev/sdd\u003c/code\u003e, 10.9T, old ZFS partition table on it (used drive — \u003ccode\u003epart1\u003c/code\u003e/\u003ccode\u003epart9\u003c/code\u003e layout)\u003c/li\u003e\n\u003cli\u003eBack to ghost, direct SATA → enumerated as \u003ccode\u003esdb\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eLesson: \u0026ldquo;spins up but doesn\u0026rsquo;t enumerate\u0026rdquo; on direct SATA + dock-shows-0B = seating/power problem, not DOA. The drive was fine all along.\u003c/p\u003e\n\u003ch2 id=\"the-replace\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eThe replace\u003c/span\u003e \u003ca href=\"#the-replace\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cpre class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo zpool replace harbor \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  ata-ST12000NM0127_ZJV4QFLB \u003cspan class=\"se\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  ata-ST12000VN0008-2PH103_ZTN1CQ07\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eResilver: \u003cstrong\u003e8.80T in 18h38m, 0 errors\u003c/strong\u003e (~154M/s). Pool stayed online and usable the whole time.\u003c/p\u003e\n\u003cp\u003eOne gotcha: after resilver, \u003ccode\u003ezpool status\u003c/code\u003e showed \u003ccode\u003eerrors: 1 data errors\u003c/code\u003e — but \u003ccode\u003ezpool status -v\u003c/code\u003e showed an \u003cstrong\u003eempty error list\u003c/strong\u003e. The corrupted data was already repaired; the counter was just stale. \u003ccode\u003esudo zpool clear harbor\u003c/code\u003e → clean.\u003c/p\u003e\n\u003ch2 id=\"the-monitoring-that-made-this-possible\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eThe monitoring that made this possible\u003c/span\u003e \u003ca href=\"#the-monitoring-that-made-this-possible\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eWired during this session (all now in SigNoz):\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003enode_zfs_zpool_state\u003c/code\u003e — pool health (node-exporter zfs collector)\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003esmartctl-exporter\u003c/code\u003e — SMART attributes incl. reallocated sectors (the early-death signal)\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ezfs-metrics.sh\u003c/code\u003e textfile bridge — sanoid \u003ccode\u003e--monitor-*\u003c/code\u003e exit codes, zpool error counters, scrub ages, resilver %, syncoid last-success\u003c/li\u003e\n\u003cli\u003eAlert rules → Gotify → phone\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThe replication alerting even proved itself live: syncoid failed twice during the resilver window and I got paged on both. Turned out to be send-time contention — self-healed once resilver finished — but the notification path works end to end.\u003c/p\u003e\n\u003ch2 id=\"remaining-homework\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eRemaining homework\u003c/span\u003e \u003ca href=\"#remaining-homework\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eBuy the replacement spare (the shelf\u0026rsquo;s empty now)\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e10Fold\u003c/code\u003e datasets are garbage + have zero snapshots — destroy\u003c/li\u003e\n\u003cli\u003eEvery scheduled job emits \u003ccode\u003ecron_last_success_epoch\u003c/code\u003e now — if a job dies silently again, I\u0026rsquo;ll know\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eSame failure as April, opposite outcome. Redundancy did the protection; monitoring did the \u003cem\u003edetection\u003c/em\u003e. You need both.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cem\u003eCo-authored with Devin, who ran the monitoring stack, the SMART diagnosis, and the alerting loop.\u003c/em\u003e\u003c/p\u003e\n",
      "content_text": "\nThe sequel to [panicking-led-to-losing-my-desktop](/panicking-led-to-losing-my-desktop) — this time the monitoring actually caught the disk dying, and nothing was lost.\n\n## What happened\n\n`harbor` is my replica pool — a 10.9T mirror (2x 12TB) that receives syncoid snapshots from `tank`. One side of the mirror, a Seagate Exos `ST12000NM0127` (serial `ZJV4QFLB`, `/dev/sdb`), went **FAULTED** with 14 read + 22 checksum errors.\n\n``` text\nmirror-0                              DEGRADED     0     0     0\n  ata-ST12000VN0008-2PH103_ZTM0NFDW  ONLINE       0     0     0\n  ata-ST12000NM0127_ZJV4QFLB         FAULTED     14     0    22  too many errors\n```\n\nThe IronWolf mirror side carried the pool — `No known data errors`. ZFS redundancy did exactly its job.\n\n## The difference from last time\n\nLast failure: no monitoring, found out by accident months later, desktop died.\n\nThis failure: SigNoz + node-exporter's ZFS collector → `node_zfs_zpool_state{state=\"degraded\"}` → alert rule → Gotify → my phone. The gotify notification fired *before* I knew anything was wrong.\n\n## Diagnosis\n\nBefore `zpool clear` or replace — check SMART. `smartctl-exporter` already scrapes all disks into SigNoz, so I didn't even need sudo:\n\n- `Reallocated_Sector_Ct` raw = **3024** and counting\n- `Offline_Uncorrectable` value/worst still 100 but raw errors climbing\n- SMART overall: still PASS (SMART's overall bit is conservative until threshold)\n\n3k+ remapped sectors is a platter going bad — not a cable blip. Verdict: replace, don't clear.\n\n## The swap (the annoying part)\n\nHot-plug is never as smooth as it should be:\n\n1. `zpool offline harbor \u003cold\u003e` to stop writes to the dead drive — actually skipped; drive fell off the bus on its own\n2. New IronWolf `ZTN1CQ07` in hand → plugged into ghost's SATA → **didn't enumerate**\n3. Forced rescan (`echo \"- - -\" | sudo tee /sys/class/scsi_host/host*/scan`) → nothing\n4. Moved it to the *old drive's port* → nothing\n5. Sabrent USB dock on aurora → dock enumerated as `0B` device, no disk behind it → reseated + replugged → **drive spun up and appeared**: `/dev/sdd`, 10.9T, old ZFS partition table on it (used drive — `part1`/`part9` layout)\n6. Back to ghost, direct SATA → enumerated as `sdb`\n\nLesson: \"spins up but doesn't enumerate\" on direct SATA + dock-shows-0B = seating/power problem, not DOA. The drive was fine all along.\n\n## The replace\n\n``` bash\nsudo zpool replace harbor \\\n  ata-ST12000NM0127_ZJV4QFLB \\\n  ata-ST12000VN0008-2PH103_ZTN1CQ07\n```\n\nResilver: **8.80T in 18h38m, 0 errors** (~154M/s). Pool stayed online and usable the whole time.\n\nOne gotcha: after resilver, `zpool status` showed `errors: 1 data errors` — but `zpool status -v` showed an **empty error list**. The corrupted data was already repaired; the counter was just stale. `sudo zpool clear harbor` → clean.\n\n## The monitoring that made this possible\n\nWired during this session (all now in SigNoz):\n\n- `node_zfs_zpool_state` — pool health (node-exporter zfs collector)\n- `smartctl-exporter` — SMART attributes incl. reallocated sectors (the early-death signal)\n- `zfs-metrics.sh` textfile bridge — sanoid `--monitor-*` exit codes, zpool error counters, scrub ages, resilver %, syncoid last-success\n- Alert rules → Gotify → phone\n\nThe replication alerting even proved itself live: syncoid failed twice during the resilver window and I got paged on both. Turned out to be send-time contention — self-healed once resilver finished — but the notification path works end to end.\n\n## Remaining homework\n\n- Buy the replacement spare (the shelf's empty now)\n- `10Fold` datasets are garbage + have zero snapshots — destroy\n- Every scheduled job emits `cron_last_success_epoch` now — if a job dies silently again, I'll know\n\nSame failure as April, opposite outcome. Redundancy did the protection; monitoring did the *detection*. You need both.\n\n---\n\n*Co-authored with Devin, who ran the monitoring stack, the SMART diagnosis, and the alerting loop.*\n",
      "summary": "The sequel to panicking-led-to-losing-my-desktop — this time the monitoring actually caught the disk dying, and nothing was lost.",
      "date_published": "2026-10-06T21:30:00Z",
      "date_modified": "2026-10-06T21:30:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "zfs",
        "tech",
        "backup"
      ]
    },
    {
      "id": "https://pype.dev/thoughts-871/",
      "url": "https://pype.dev/thoughts-871/",
      "title": "💭 Docker | Atuin Docs",
      "content_text": "\n\u003ca href=\"https://docs.atuin.sh/self-hosting/docker/#using-systemd-to-manage-your-atuin-server\"\u003e\n    \u003cimg\n        src=\"https://shots.wayl.one/shot/?url=https://docs.atuin.sh/self-hosting/docker/#using-systemd-to-manage-your-atuin-server\u0026height=450\u0026width=800\u0026scaled_width=800\u0026scaled_height=450\u0026selectors=\"\n        alt=\"shot of post - Docker | Atuin Docs\"\n        height=450\n        width=800\n    \u003e\n\u003c/a\u003e\n\nHere's my thought on \u003ca href=\"https://docs.atuin.sh/self-hosting/docker/#using-systemd-to-manage-your-atuin-server\"\u003eDocker | Atuin Docs\u003c/a\u003e\n\n---\n\nA 2-fer... So I want to host atuin and share shell history across my machines on my tailnet I think... it's such a nice interface but I barely use it so I'm sure the features will improve some aspects of my life. And secondly, this section on using systemd is interesting... I hadn't thought to use systemd to schedule container upgrades - sounds incredibly trivial and probably a widely practiced thing now that I say it out loud... Saving here for implementing something... sometime...\n\n---\n\n!!! note\n    This is one of [[ my-thoughts ]]. I picked this up from [Waylon Walker](https://waylonwalker.com)(https://thoughts.waylonwalker.com). It's a short note that I make about someone else's content online.  Learn more about the process [[ thoughts ]]\n",
      "summary": "A 2-fer... So I want to host atuin and share shell history across my machines on my tailnet I think... it's such a nice",
      "date_published": "2025-11-22T20:58:59Z",
      "date_modified": "2025-11-22T20:58:59Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "upgrades",
        "docker",
        "ssh",
        "thoughts"
      ]
    },
    {
      "id": "https://pype.dev/homelabbing-realization-configs-and-git/",
      "url": "https://pype.dev/homelabbing-realization-configs-and-git/",
      "title": "Homelabbing Realization - Configs and Git",
      "content_html": "\u003ch2 id=\"realization\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eRealization\u003c/span\u003e \u003ca href=\"#realization\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;m back to brainstorming migrations away from Docker Compose to something that\nsolves multi-node networking and secrets but I don\u0026rsquo;t want to go to k8s\u0026hellip; so my\noptions are probably Docker Swarm or Nomad. As I wrestle mentally here I was\nthinking about one of my issues with multi-node setup is that I still keep\neverything out of one git repo, so if something gets managed remotely then I\nget out of sync. Best example is dashy, Due to some constraint of wanting to be\nas close to zero-day deployment-ready as possible, I think I overcomplicate\nsome of the simple stuff - like it\u0026rsquo;s ok to just have to put a file on a server\nfor a container to start the first time\u0026hellip; That doesn\u0026rsquo;t mean ansible is a\nrequirement, in fact lately I\u0026rsquo;ve just leaned into just recipes. But even then,\nI cornered myself into tracking my dashy config in git, so I\u0026rsquo;m hesitant to ever\nupdate it in the browser cause I\u0026rsquo;ll have to redownload the config, reconcile\nthe formatting differences in my repo (there\u0026rsquo;s a reason for this that a linter\ndoesn\u0026rsquo;t ssolve\u0026hellip;), etc\u0026hellip;. but in all of my brainstorming I just decided that\nmy dashy config doesn\u0026rsquo;t have to live in git\u0026hellip; it can just be on the zfs\nstorage, which is backed up, and configured via the app itself\u0026hellip; the config\ncan simply be apart of the data and container lifecycle, it doesn\u0026rsquo;t have to be\napart of the infra lifecycle.\u003c/p\u003e\n",
      "content_text": "\n## Realization\n\nI'm back to brainstorming migrations away from Docker Compose to something that\nsolves multi-node networking and secrets but I don't want to go to k8s... so my\noptions are probably Docker Swarm or Nomad. As I wrestle mentally here I was\nthinking about one of my issues with multi-node setup is that I still keep\neverything out of one git repo, so if something gets managed remotely then I\nget out of sync. Best example is dashy, Due to some constraint of wanting to be\nas close to zero-day deployment-ready as possible, I think I overcomplicate\nsome of the simple stuff - like it's ok to just have to put a file on a server\nfor a container to start the first time... That doesn't mean ansible is a\nrequirement, in fact lately I've just leaned into just recipes. But even then,\nI cornered myself into tracking my dashy config in git, so I'm hesitant to ever\nupdate it in the browser cause I'll have to redownload the config, reconcile\nthe formatting differences in my repo (there's a reason for this that a linter\ndoesn't ssolve...), etc.... but in all of my brainstorming I just decided that\nmy dashy config doesn't have to live in git... it can just be on the zfs\nstorage, which is backed up, and configured via the app itself... the config\ncan simply be apart of the data and container lifecycle, it doesn't have to be\napart of the infra lifecycle.\n",
      "summary": "I'm back to brainstorming migrations away from Docker Compose to something that solves multi-node networking and secrets but I don't want to go to k8s... so...",
      "date_published": "2025-11-13T05:48:15Z",
      "date_modified": "2025-11-13T05:48:15Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "note"
      ]
    },
    {
      "id": "https://pype.dev/repoflow-robot-user/",
      "url": "https://pype.dev/repoflow-robot-user/",
      "title": "RepoFlow Robot User",
      "content_html": "\u003cp\u003eI\u0026rsquo;m trying to lean into \u003ca href=\"https://repoflow.io\"\u003erepoFlow\u003c/a\u003e at home, and model some slightly better\npatterns than open borders to artifacts across all my services. As I\u0026rsquo;m getting repoflow\ngoing I see that I can make users in the console, but then I do have to invite\nusers to workspaces with a manually generate invite link.\u003c/p\u003e\n\u003cp\u003eThe workspaces an abstraction layer, and in each workspace can be many kinds of\nrepositories. For my purposes I\u0026rsquo;ll probably have 2 workspaces, even though 1\nwould almost certainly suffice.\u003c/p\u003e\n\u003cp\u003eThere doesn\u0026rsquo;t seem to be a way to add a user to a workspace from the admin\nsetting so for a while I was pretty confused why I couldn\u0026rsquo;t add my robot user\nto a docker repository in my main workspace\u0026hellip;\u003c/p\u003e\n\u003cp\u003eAfter generating the invite link and accepting though then I see in the workspace settings a\nsimple click to give the robot user access to all repos (if you want)\u003c/p\u003e\n\u003cfigure\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20251111122419_580b2ad4.png\" alt=\"20251111122419_580b2ad4.png\"\u003e\n\u003c/figure\u003e\n\u003cfigure\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20251110162252_6c7b2fe9.png\" alt=\"20251110162252_6c7b2fe9.png\"\u003e\n\u003c/figure\u003e\n\u003cp\u003e\u003ccode\u003eCan manage\u003c/code\u003e allows the user to mutate tags (ie. push \u003ccode\u003elatest\u003c/code\u003e docker tags). If\nthe user only has \u003ccode\u003eCan deploy\u003c/code\u003e then they can only create new artifacts (ie. not\nmutate any tags)\u003c/p\u003e\n",
      "content_text": "\nI'm trying to lean into [repoFlow](https://repoflow.io) at home, and model some slightly better\npatterns than open borders to artifacts across all my services. As I'm getting repoflow\ngoing I see that I can make users in the console, but then I do have to invite\nusers to workspaces with a manually generate invite link.\n\nThe workspaces an abstraction layer, and in each workspace can be many kinds of\nrepositories. For my purposes I'll probably have 2 workspaces, even though 1\nwould almost certainly suffice.\n\nThere doesn't seem to be a way to add a user to a workspace from the admin\nsetting so for a while I was pretty confused why I couldn't add my robot user\nto a docker repository in my main workspace...\n\nAfter generating the invite link and accepting though then I see in the workspace settings a\nsimple click to give the robot user access to all repos (if you want)\n\n![20251111122419_580b2ad4.png](https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20251111122419_580b2ad4.png)\n\n![20251110162252_6c7b2fe9.png](https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20251110162252_6c7b2fe9.png)\n\n`Can manage` allows the user to mutate tags (ie. push `latest` docker tags). If\nthe user only has `Can deploy` then they can only create new artifacts (ie. not\nmutate any tags)\n",
      "summary": "I'm trying to lean into repoFlow at home, and model some slightly better patterns than open borders to artifacts across all my services. As I'm getting...",
      "date_published": "2025-11-10T09:24:25Z",
      "date_modified": "2025-11-10T09:24:25Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/proxy-pull-docker-images-from-self-hosted-container-registry-through-self-hosted-repoflow/",
      "url": "https://pype.dev/proxy-pull-docker-images-from-self-hosted-container-registry-through-self-hosted-repoflow/",
      "title": "Proxy Pull Docker Images From Self-Hosted Container Registry Through Self-Hosted Repoflow",
      "content_html": "\u003cp\u003eThis post is a short write-up of an issue I had while exploring \u003ca href=\"https://repoflow.io\"\u003eReploflow\u003c/a\u003e - a\nsuper solid artifactory-esq replacement for the homelab (and enterprise!). I\u0026rsquo;ve been playing with\nit and have tried to setup a few artifact repos that I\u0026rsquo;m familiar with - docker\nand pypi.\u003c/p\u003e\n\u003ch2 id=\"pypi\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003ePyPi\u003c/span\u003e \u003ca href=\"#pypi\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThis isn\u0026rsquo;t a post about repoflow, but I had an issue with pypi and Tomer was\nEXTREMELY responsive in helping to fix my issue and patch repoflow within a day\nof me reporting the issue via email!\u003c/p\u003e\n\u003ch2 id=\"docker\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eDocker\u003c/span\u003e \u003ca href=\"#docker\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eFor docker I have a few options I just wanted to see work - obviously\nproxy pull from Dockerhub for ease, but also in the interim I\u0026rsquo;d like to proxy\npull from my original container registry to avoid having to update any homelab config just yet.\nNow, obviously I\u0026rsquo;ll have to update all my build pipelines to start using\nrepoflow, and my homelab configuration to reference the repoflow docker\nrepository rather than my existing registry.\u003c/p\u003e\n\u003cp\u003eBut there\u0026rsquo;s something to bear in mind if maintaining that middle registry -\nturns out a there\u0026rsquo;s a default namespace in dockerhub \u003ccode\u003e/library/\u003c/code\u003e and repoflow\nexpects this standard across any docker registry, so it\u0026rsquo;s pull paths fully\nresolve to an incorrect tag if you don\u0026rsquo;t take this into account in your\nexisting infrastructure.\u003c/p\u003e\n\u003cp\u003eTLDR: if you tag an image \u003ccode\u003emyregistry/ubuntu:latest\u003c/code\u003e and push it\nthen try to pull it back through repoflow then repoflow expects the image to be\n\u003ccode\u003emyregistry/library/ubuntu:latest\u003c/code\u003e. I think repoflow should support\nconfigurating the namespace/org for the images - but in the meantime I can\ngo find all my images and update my build scripts to add /library/ to my\ntags\u0026hellip; or while I\u0026rsquo;m doing that I can just push to repoflow directly\u0026hellip; All in\nall, nice little lesson on namespaces in docker repos and third-party tooling.\u003c/p\u003e\n",
      "content_text": "\nThis post is a short write-up of an issue I had while exploring [Reploflow](https://repoflow.io) - a\nsuper solid artifactory-esq replacement for the homelab (and enterprise!). I've been playing with\nit and have tried to setup a few artifact repos that I'm familiar with - docker\nand pypi. \n\n## PyPi\n\nThis isn't a post about repoflow, but I had an issue with pypi and Tomer was\nEXTREMELY responsive in helping to fix my issue and patch repoflow within a day\nof me reporting the issue via email!\n\n## Docker\n\nFor docker I have a few options I just wanted to see work - obviously\nproxy pull from Dockerhub for ease, but also in the interim I'd like to proxy\npull from my original container registry to avoid having to update any homelab config just yet.\nNow, obviously I'll have to update all my build pipelines to start using\nrepoflow, and my homelab configuration to reference the repoflow docker\nrepository rather than my existing registry. \n\nBut there's something to bear in mind if maintaining that middle registry -\nturns out a there's a default namespace in dockerhub `/library/` and repoflow\nexpects this standard across any docker registry, so it's pull paths fully\nresolve to an incorrect tag if you don't take this into account in your\nexisting infrastructure.\n\nTLDR: if you tag an image `myregistry/ubuntu:latest` and push it\nthen try to pull it back through repoflow then repoflow expects the image to be\n`myregistry/library/ubuntu:latest`. I think repoflow should support\nconfigurating the namespace/org for the images - but in the meantime I can\ngo find all my images and update my build scripts to add /library/ to my\ntags... or while I'm doing that I can just push to repoflow directly... All in\nall, nice little lesson on namespaces in docker repos and third-party tooling. \n\n",
      "summary": "This post is a short write-up of an issue I had while exploring Reploflow - a super solid artifactory-esq replacement for the homelab (and enterprise!). I've...",
      "date_published": "2025-08-12T06:59:02Z",
      "date_modified": "2025-08-12T06:59:02Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech",
        "repoflow"
      ]
    },
    {
      "id": "https://pype.dev/thoughts-764/",
      "url": "https://pype.dev/thoughts-764/",
      "title": "💭 My Ultimate Self-hosting Setup",
      "content_text": "\n\u003ca href=\"https://codecaptured.com/blog/my-ultimate-self-hosting-setup/\"\u003e\n    \u003cimg\n        src=\"https://shots.wayl.one/shot/?url=https://codecaptured.com/blog/my-ultimate-self-hosting-setup/\u0026height=450\u0026width=800\u0026scaled_width=800\u0026scaled_height=450\u0026selectors=\"\n        alt=\"shot of post - My Ultimate Self-hosting Setup\"\n        height=450\n        width=800\n    \u003e\n\u003c/a\u003e\n\nHere's my thought on \u003ca href=\"https://codecaptured.com/blog/my-ultimate-self-hosting-setup/\"\u003eMy Ultimate Self-hosting Setup\u003c/a\u003e\n\n---\n\ngreat post on self-hosting. I think in a lot of similar ways and had a siar journey. There's things I want to accomplish as time goes on, looking for motivation to get a post like this going myself\n\n---\n\n!!! note\n    This is one of [[ my-thoughts ]]. I picked this up from [Waylon Walker](https://waylonwalker.com)(https://thoughts.waylonwalker.com). It's a short note that I make about someone else's content online.  Learn more about the process [[ thoughts ]]\n",
      "summary": "great post on self-hosting. I think in a lot of similar ways and had a siar journey. There's things I want to accomplish",
      "date_published": "2025-07-25T19:56:38Z",
      "date_modified": "2025-07-25T19:56:38Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "thoughts"
      ]
    },
    {
      "id": "https://pype.dev/using-restic-to-backup-my-home-directory/",
      "url": "https://pype.dev/using-restic-to-backup-my-home-directory/",
      "title": "Using restic to backup my home directory",
      "content_text": "\n# Intro\n\nI need to backup my personal $HOME to my NAS cause there's a lot in there, and\nmostly my git projects with .env files all over. Plus some docker data\n\n## Why Not ZFS?\n\nGREAT QUESTION! It's because I struggled getting all the syncoid/sanoid\nrequirements installed on my Aurora OS.... I like the immutable desktop trend,\nbut I don't understand rpm-ostree enough to properly get all the lower level\nkernel stuff setup in a way I trust. So because I don't have ZFS at $HOME\nwhat's a boy to do?\n\n`rsync` would probably work fine especially if I [[rsync-like-a-pro]] but I figured I could cobble together some other tech and broaden my horizons...\n\n## Enter restic\n\nApparently [restic](https://restic.net/) has been around for a while, and you can run it in a container... awesome that's all I needed to hear...\n\n## How?\n\nI'll use a docker compose stack for this... typically I think running `restic`\nright on the host is the way to go, but on my desktop I float between my host\nand distroboxes and to keep things as simple as possible, even though less\nefficient, I want to manage everything through containers as my homelab gets\nbuilt out and eventually I'll have a more full-featured ecosystem.\n\n## The Value\n\nYou can find code below and an example repo that more or less is what I use for my desktop backup but that's primarily what I was after.... backing up the data on my desktop to my NAS in a way that:\n\n1. I wouldn't have to \"remember\" how I was doing it\n\n- I accomplish this by having good git repo organization and README files that explain my own usage patterns\n\n2. Tracked snapshots\n\n- I need some kind of snapshotting - `zfs` has spoiled me, and turns out `restic` has some amount of support for it\n\n3. Running in docker would be ideal for now so that I don't need to worry about installing specific binaries across machine while I build out my patterns\n\nWith this setup I get to backup my $HOME to my NAS which contains 1. docker\nvolume info for all the AI workloads I run on my desktop and 2. all my\nsensitive info in my git repos is at least backed up in a way that I can\nrecover `.env` files with relative ease...\n\n# Code\n\nCheck out the [Example GH\nRepo](https://github.com/pypeaday/docker-compose-restic) but I'll drop key file\ncontents here for a quick read if you're interested in some of the setup... but\nthe blog post mostly ends here\n\n## The Files\n\nWe have a `docker-compose.yml` of course\n\n```yaml\nservices:\n  backup:\n    image: restic/restic:latest\n    network_mode: host\n    container_name: restic_backup\n    hostname: restic-backup-runner\n    env_file:\n      - .env\n    environment:\n      # The location of the backup repository inside the container\n      - RESTIC_REPOSITORY=/target\n      # The location of the password file inside the container\n      - RESTIC_PASSWORD_FILE=/password\n    volumes:\n      # --- Source and Config Mounts ---\n      - \"${BACKUP_SOURCE}:/source:ro\"\n      - \"${RESTIC_PASSWORD_FILE}:/password:ro\"\n      - \"./backup-and-prune.sh:/usr/local/bin/backup-and-prune:ro\"\n\n      # --- Persistent Data Mounts ---\n      - \"./.ssh:/root/.ssh:rw\"\n      - \"./.cache:/root/.cache:rw\"\n\n    # Set the default entrypoint to our new script. This will be executed when\n    # the container starts, unless overridden.\n    entrypoint: [\"/usr/local/bin/backup-and-prune\"]\n```\n\nYour `.env` file will need to look like this\n\n```bash\n``# HOST related variables\n# The source directory to back up (absolute path)\nBACKUP_SOURCE=/home/nic\n\n# --- Restic Configuration ---\n# The file containing the restic repository password (absolute path on host)\nHOST_RESTIC_PASSWORD_FILE=/home/nic/projects/personal/homelab-mono/dataops/docker/.restic-password\n# The SSH private key to use for connecting to the NAS (absolute path on host)\n# It's recommended to use a dedicated key for this purpose.\nSSH_PRIVATE_KEY_FILE=/home/nic/.skm/ghost/id_rsa\n\n# Container Env Vars\n# the container makes the sftp connection using my credentials, but nonetheless the container needs them, so this isn't envrc stuff\n\n# --- SFTP/SSH Connection Details for the NAS ---\nSFTP_USER=nic\nSFTP_HOST=ghost\n# The path on the NAS where the restic repository will be stored\nSFTP_PATH=/tank/encrypted/nas/nic-home/\n\n# --- Restic Configuration ---\n\nRESTIC_REPOSITORY=/target\nRESTIC_PASSWORD_FILE=/password\n```\n\nAnd then the backup script that the container will execute is something like this:\n\n```bash\n\n#!/bin/sh\nset -e # Exit immediately if a command exits with a non-zero status.\n\n# Construct the repository path from environment variables passed by docker-compose\nREPO=\"sftp:${SFTP_USER}@${SFTP_HOST}:${SFTP_PATH}\"\n\n# 1. Run the backup\n# -----------------\necho \"--- Starting backup for ${BACKUP_SOURCE} ---\"\nrestic backup /source --verbose -r \"${REPO}\"\necho \"--- Backup complete ---\"\n\n# 2. Clean up old snapshots according to the policy\n# --------------------------------------------------\necho \"--- Pruning old snapshots ---\n(Policy: keep last 7 daily, 4 weekly, 6 monthly)\"\nrestic forget \\\n    --prune \\\n    --keep-daily 7 \\\n    --keep-weekly 4 \\\n    --keep-monthly 6 \\\n    -r \"${REPO}\"\n\necho \"--- Backup and prune process finished successfully ---\"\n```\n\n## Systemd\n\nFinally there's a systemd unit and timer file in there so you can setup a systemd service for the backup\n\n### service\n\n```bash\n\n[Unit]\nDescription=Run Restic backup to NAS using Docker Compose\n# We are running this as a user service, so we assume that the system-level\n# docker.service is already running.\nAfter=network-online.target\n\n[Service]\nType=oneshot\n# Set the working directory to where your docker-compose.yml and .env file are located\nWorkingDirectory=/home/user/nas-backup/docker\n\n# The command to execute. We use the full path to docker-compose for reliability.\n# You may need to adjust this path if 'docker compose' is installed elsewhere.\nExecStart=/usr/bin/docker compose run --rm backup\n\n[Install]\nWantedBy=default.target\n```\n\n### timer\n\n```bash\n\n[Unit]\nDescription=Run Restic backup job daily\n\n[Timer]\n# Run daily at 2:00 AM\nOnCalendar=daily\n# Or uncomment for a specific time:\n# OnCalendar=*-*-* 02:00:00\n\n# Run the backup immediately if the last scheduled run was missed (e.g., if the computer was off)\nPersistent=true\n\n[Install]\nWantedBy=timers.target\n```\n",
      "summary": "I need to backup my personal $HOME to my NAS cause there's a lot in there, and mostly my git projects with .env files all over. Plus some docker data",
      "date_published": "2025-07-16T09:12:26Z",
      "date_modified": "2025-07-16T09:12:26Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/jellyfin/",
      "url": "https://pype.dev/jellyfin/",
      "title": "Jellyfin",
      "content_html": "\u003cp\u003eJellyfin is the media server software I run at home\u003c/p\u003e\n",
      "content_text": "\nJellyfin is the media server software I run at home\n",
      "summary": "Jellyfin is the media server software I run at home",
      "date_published": "2025-07-11T20:39:00Z",
      "date_modified": "2025-07-11T20:39:00Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "note"
      ]
    },
    {
      "id": "https://pype.dev/ghost/",
      "url": "https://pype.dev/ghost/",
      "title": "Ghost",
      "content_text": "\n# Intro\n\nGhost is my primary application server. These are the specs...\n\n\u003e I wrote about some of the specs in [[homelab-journey-part-1#current-homelab-setup]] but here is where I will keep up to date information\n\n## Why\n\nWhy `ghost`? I don't know... I was going for a theme at one point it time, or\nseveral points rather... and after giving up on the theatrics of a fully\nconsistent harry potter naming scheme across all my homelab entities, I decided\nto just come up with some short aliases that were easy to type. So `ghost` was\nborn, and the backup server is `ghost-vault`.\n\n## CPU\n\nI have a `AMD Ryzen 7 5700G with Radeon Graphics @ 16x 4.426GHz` in this bad\nboy... why this chip? Well I knew I wanted many cores, and I was previously on\na Ryzn 5 3600, which was fine but I noticed that as I started to develop with\ncontainers more that I was going to want to take advnaatage of more cores...\n\nI also was pretty sure that the built-in graphics would be enough for the\nacceleration requirements I had in mind - which was primarily [[jellyfin]].\n\nAt this point in time, I think AMD was still beating Intel on most of the\nbenchmarks I tended to care about (I was watching channels like Bitwit and LTT\nat the time of building this machine)\n\nSo because of the moderate amount of cores and built-in GPU, I paid $229 for it\naccording to my Amazon history, and it's currently priced at $174, so that's a\npretty sweet deal if you're on AM4 and looking to upgrade in July of 2025\n\n## Memory\n\nI maxxed her out at 128 Gee Beez. I snagged that kit of Crucial that's all\nblack that we've all seen on Amazon. I paid aboaut $90 for it, it's currently\npriced way high for some reason, which is unfortunate. It's been totally fine\nfor me as far as memory performance goes - in that it's stable and I don't\nthink about it...\n\n## Boot\n\nI have a 1 TB NVMe in here to boot from. I think it's a Crucial P3\n\n## Storage\n\nStorage is the interesting bit, it's supposed to be the most rock solid but\nit's also been the most fluid for me from a hardware perspective, and as of my\nmost recent case migration I think I'm in a good spot..\n\n\u003e The case is a Sagitatius 8-bay NAS Chassis from aliexpress\n\nI use\n[zfs](https://openzfs.github.io/openzfs-docs/Getting%20Started/index.html)\nand recommend you do to.... but this isn't a post about that\n\nMy primary zpool is a 12TB ZFS Mirror, and I have another 12TB ZFS Mirror as an\non-prem replica plugged into this same box.\n\nThere is a 4TB drive that serves as frigate's media directory - I wanted a\ndedicated drive due to the write-intensive nature of the NVR.\n\n\u003e I backup to an offsite box using syncoid\n\n## OS\n\nI am currently running Ubuntu Server 24.04\n\nI am eager for Ubuntu to get zfs in their release that contains the zfs data\ncorruption with encrypted datasets but I don't expect to see it until 26.04 at\nthe earliest _fingers crossed_.\n\n## KVM\n\nI recently purchased a [jetkvm](https://www.jetkvm.com/) and it's exactly what\nI need for both my main server and my backup off-site... An amazingly simple\nand elegant solution to remote KVM.\n\n## Applications\n\nCurrently I use `docker compose` to manage practically everything. Stay tuned\nfor more about those things\n\nI also have a few ansible playbooks for setting up my shell and some utilities\non my computers, including the server. They're in\n[github](https://github.com/pypeaday/ansible-playbooks)\n\nFind more at [[the-homelab]]\n",
      "summary": "Ghost is my primary application server. These are the specs...",
      "date_published": "2025-07-11T20:23:35Z",
      "date_modified": "2025-07-11T20:23:35Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/the-homelab/",
      "url": "https://pype.dev/the-homelab/",
      "title": "The Homelab",
      "content_text": "\n# The Lab\n\nThis is the landing page for my homelab posts. It isn't a feed because I will\nwrite things here and link out to relevant parts of the homelab.\n\nI'll probably have notes about the hardware mostly here.\n\n## Primary Application Server\n\nMy main server is called [[ghost]]\n\n## Current Homelab Setup\n\n\u003e I copied this our of [[homelab-journey-part-1#current-homelab-setup]] for now to stub out what this page will link to\n\n## Ad blocking | Pi-Hole\n\nHardware: pi-3\n\nI have a Raspberry Pi 3 hardwired in that is running pi-hole so that's been a\npart of my infrastructure now for a long time.\n\n## Router | OPNSense\n\n!!! note \"stats\"\n\n    CPU: i5-8700\n\n    Memory: 8GB of whatever was in the box\n\n    Storage: 1TB SSD\n\nMy router is an old Optiplex SFF 5060 I got from Amazon for less than $100.\nSo I'm running a x86 system for my router with\nOpenSense on it. Just seemed it to be another home lab standard and I'd like to\nsomeday bring pi-hole over there or use AdGuard or integrate those two things\nmaybe a little bit more completely but right now they are mostly separate.\n\n## Networking | VLANs\n\nThere's a\nmanaged switch in there for some VLAN tomfoolery for my cameras and an IOT\nnetwork type of a thing.\n\n## Networking | VPN | Tailscale\n\nThey're on tailscale as well so my DNS happens over tailscale.\n\n## My Desktop\n\n!!! note \"stats\"\n\n    CPU: Ryzen 7 5700X\n\n    GPU: Nvidia 3090\n\n    Memory: 64 GB\n\n    Storage: 4TB SSD + 2 TB HDD for zfs backup\n\n    OS: Universal Blue Aurora\n\nMy daily-driver desktop is somewhat apart of my homelab now as I have a 3090 in\nthere with 20 GB of VRAM so I can run some heavier LLMs for self-hosted AI\nworkloads. Everything in that space runs in a container and then I manage\ndocker containers with compose stacks and portainer for visibility.\n\n### Applications\n\nSome of the apps I run here are ollama, open-webui, automatic1111's stable diffusion webui, whisper-webui\n",
      "summary": "This is the landing page for my homelab posts. It isn't a feed because I will write things here and link out to relevant parts of the homelab.",
      "date_published": "2025-07-11T20:22:51Z",
      "date_modified": "2025-07-11T20:22:51Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/thoughts-746/",
      "url": "https://pype.dev/thoughts-746/",
      "title": "💭 Docker Brings Compose to the AI Agent Era | Docker",
      "content_text": "\n\u003ca href=\"https://www.docker.com/blog/build-ai-agents-with-docker-compose/\"\u003e\n    \u003cimg\n        src=\"https://shots.wayl.one/shot/?url=https://www.docker.com/blog/build-ai-agents-with-docker-compose/\u0026height=450\u0026width=800\u0026scaled_width=800\u0026scaled_height=450\u0026selectors=\"\n        alt=\"shot of post - Docker Brings Compose to the AI Agent Era | Docker\"\n        height=450\n        width=800\n    \u003e\n\u003c/a\u003e\n\nHere's my thought on \u003ca href=\"https://www.docker.com/blog/build-ai-agents-with-docker-compose/\"\u003eDocker Brings Compose to the AI Agent Era | Docker\u003c/a\u003e\n\n---\n\nSeems like docker is leaning harder into compose - which is great for me as a heavy compose user. I had heard about some of the LLM enablements directly through docker desktop - for example taking fuller advantage of the host's compute power without paying the penalty of the nvidia runtime or something... I can't claim to have followed it all but I heard \"run LLMs in docker directly for a bigger boost\" and I'm in.\n\n---\n\n!!! note\n    This is one of [[ my-thoughts ]]. I picked this up from [Waylon Walker](https://waylonwalker.com)(https://thoughts.waylonwalker.com). It's a short note that I make about someone else's content online.  Learn more about the process [[ thoughts ]]\n",
      "summary": "Seems like docker is leaning harder into compose - which is great for me as a heavy compose user. I had heard about some",
      "date_published": "2025-07-11T11:49:41Z",
      "date_modified": "2025-07-11T11:49:41Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "docker",
        "ai",
        "homelab",
        "thoughts"
      ]
    },
    {
      "id": "https://pype.dev/thoughts-740/",
      "url": "https://pype.dev/thoughts-740/",
      "title": "💭 restic · Backups done right!",
      "content_text": "\n\u003ca href=\"https://restic.net/\"\u003e\n    \u003cimg\n        src=\"https://shots.wayl.one/shot/?url=https://restic.net/\u0026height=450\u0026width=800\u0026scaled_width=800\u0026scaled_height=450\u0026selectors=\"\n        alt=\"shot of post - restic · Backups done right!\"\n        height=450\n        width=800\n    \u003e\n\u003c/a\u003e\n\nHere's my thought on \u003ca href=\"https://restic.net/\"\u003erestic · Backups done right!\u003c/a\u003e\n\n---\n\nWelp, I need to be backing up my desktop home directory, specifically a directory of docker volume data... I thought duplicati made sense, but that mostly targets cloud-based backends, and rsync is great but I wasn't sure how i wanted to manage the job. \nThrough a little AI chat I learned about restic and am working on a compose stack to run restic in a container to regularly backup my home directory via sftp to my NAS to then be swept up in my NAS backup workflow\n\n---\n\n!!! note\n    This is one of [[ my-thoughts ]]. I picked this up from [Waylon Walker](https://waylonwalker.com)(https://thoughts.waylonwalker.com). It's a short note that I make about someone else's content online.  Learn more about the process [[ thoughts ]]\n",
      "summary": "Welp, I need to be backing up my desktop home directory, specifically a directory of docker volume data... I thought dup",
      "date_published": "2025-07-08T11:53:41Z",
      "date_modified": "2025-07-08T11:53:41Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "nas",
        "homelab",
        "backups",
        "thoughts"
      ]
    },
    {
      "id": "https://pype.dev/thoughts-to-nostr/",
      "url": "https://pype.dev/thoughts-to-nostr/",
      "title": "Thoughts To Nostr",
      "content_text": "\nI am trying to use [[temporal]] at home for running automated pipelines. It's\ndefinitely overkill for my use case but this is America...\n\nSo my first project is a system that takes my [[thoughts]] and posts them to\n[[nostr]] as I briefly discussed in [[testing-a-postiz-change-locally]].\n\nI have temporal infrastructure setup in homelab-compose and then\nhomelab-temporal should probably be homelab-temporal-pipelines or something,\nbut it's the app code. I also have a repo homelab-social-media-pipelines -\nwhich has been consolidated down into homelab-temporal. once that all works I\ncan destroy the homelab-social-media-pipelines repo\n\nNOTE: everything is moving to homelab-mono\n\n## Issue 01\n\nI am currently hitting a stupid python issue:\n\n```\nnic in homelab-temporal   main    ×2  ×4  ×7 via   v3.12.8(homelab-temporal)   (dev) 󰒄\n⬢ [devbox] ❯ python -c \"from homelab_temporal.postiz.postiz_client import PostizClient\"\n(homelab-temporal)\nnic in homelab-temporal   main    ×2  ×4  ×7 via   v3.12.8(homelab-temporal)   (dev) 󰒄\n⬢ [devbox] ❯ python scripts/postiz/simple_post.py\nTraceback (most recent call last):\n  File \"/home/nic/projects/personal/homelab-temporal/scripts/postiz/simple_post.py\", line 3, in \u003cmodule\u003e\n    from homelab_temporal.postiz.postiz_client import PostizClient, process_for_nostr, process_for_x\nModuleNotFoundError: No module named 'homelab_temporal'\n```\n\nvery dumb venv problem...\n\n## Resolution\n\nneed to use `python -m` because the script is outside the project root? see [[2025-07-08-notes]] and the chatgpt notes for more on this but it works now\n\n## Thoughts Order\n\nas of [[2025-07-11-notes]] I have the thoughts ordering correct - we post the oldest thoughts up to the newest ones. Now I want more intros for the notes so they don't get too repetitive\n\n## TODOs\n\n[x] - destroy the homelab-social-media-pipelines repo\n\n[] - think about how to structure things more... monorepo in homelab-temporal might be hard to manage with AI tools due to context management... unsure though\n\n[] - create workflow and activities for thoughts to nostr... revisit diagram to see what you need to build out in the temporal repo\n\n[] - do something like this for github stars and then blog posts\n\n---\n\nI need to update my templates for nostr og images using this as fodder\n\n```html\n\u003c!-- HTML Meta Tags --\u003e\n\u003ctitle\u003eNostr Design\u003c/title\u003e\n\u003cmeta\n  name=\"description\"\n  content=\"A comprehensive resource for designers and developers to build successful nostr products\"\n/\u003e\n\n\u003c!-- Facebook Meta Tags --\u003e\n\u003cmeta property=\"og:url\" content=\"https://nostrdesign.org\" /\u003e\n\u003cmeta property=\"og:type\" content=\"website\" /\u003e\n\u003cmeta property=\"og:title\" content=\"Nostr Design\" /\u003e\n\u003cmeta\n  property=\"og:description\"\n  content=\"A comprehensive resource for designers and developers to build successful nostr products\"\n/\u003e\n\u003cmeta\n  property=\"og:image\"\n  content=\"https://nostrdesign.org/img/nostr-cover.jpg\"\n/\u003e\n\n\u003c!-- Twitter Meta Tags --\u003e\n\u003cmeta name=\"twitter:card\" content=\"summary_large_image\" /\u003e\n\u003cmeta property=\"twitter:domain\" content=\"nostrdesign.org\" /\u003e\n\u003cmeta property=\"twitter:url\" content=\"https://nostrdesign.org\" /\u003e\n\u003cmeta name=\"twitter:title\" content=\"Nostr Design\" /\u003e\n\u003cmeta\n  name=\"twitter:description\"\n  content=\"A comprehensive resource for designers and developers to build successful nostr products\"\n/\u003e\n\u003cmeta\n  name=\"twitter:image\"\n  content=\"https://nostrdesign.org/img/nostr-cover.jpg\"\n/\u003e\n```\n\n## Dockerizing\n\nI think I wnt to dockerize my workers and workflows... build them out of the `homelab-temporal` repo but then run them in compose stacks... that can get stitched together better but for now that'll probably be a good target.\nI have things to think about though... for example the blog builder needs my ssh keys, or an ssh key for a user... also git is a little mess up anyways\n\n- temporal [link](https://temporal-ui.paynepride.com/namespaces/default/workflows/blog-build-ee488b69-6cdf-4819-bccd-df183cf8ec13/01981101-e48b-712a-96db-328ce720c13c/history)\n\n## Temporal\n\nI haev temporal workflows and stuff that all seem to work\n\non [[ 2025-08-08-notes ]] I had issues with my nostr relay being too public, so I shut it down, reconfigured postiz and stuff and got it workign again, more restricted and in-memory database\n",
      "summary": "I am trying to use temporal at home for running automated pipelines. It's definitely overkill for my use case but this is America...",
      "date_published": "2025-07-08T08:30:35Z",
      "date_modified": "2025-07-08T08:30:35Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "projects",
        "nostr",
        "homelab",
        "pipelines"
      ]
    },
    {
      "id": "https://pype.dev/thoughts-737/",
      "url": "https://pype.dev/thoughts-737/",
      "title": "💭 HomeBox",
      "content_text": "\n\u003ca href=\"https://homebox.software/en/\"\u003e\n    \u003cimg\n        src=\"https://shots.wayl.one/shot/?url=https://homebox.software/en/\u0026height=450\u0026width=800\u0026scaled_width=800\u0026scaled_height=450\u0026selectors=\"\n        alt=\"shot of post - HomeBox\"\n        height=450\n        width=800\n    \u003e\n\u003c/a\u003e\n\nHere's my thought on \u003ca href=\"https://homebox.software/en/\"\u003eHomeBox\u003c/a\u003e\n\n---\n\nI stumbled onto homebox this morning scrolling a newsletter and it looks like something I've been lightly thinking about for a while. I have wanted a way to track all the things in my house, but I haven't given it enough thought to come up with the specific manner in which to do it. I've seen other asset trackers but today this one excited me. \nI spun it up at home quick and it looks great - It'll take quite a lot of time to fill it out but here's a few features I love out of the gate:\n\n1. the default setup seems very useful - pre-populated locations and tags for cataloging assets reduces the barrier to entry for me\n2. it's pretty nice lookin\n3. there's a label generator including qr codes that take you to the record in your homebox... I had an idea for something like this a while ago - qr codes to link to instructions for things around the house (like cleaning the oven or something). So it's neat to see that similar idea played out in another more useful way!\n\n---\n\n!!! note\n    This is one of [[ my-thoughts ]]. I picked this up from [Waylon Walker](https://waylonwalker.com)(https://thoughts.waylonwalker.com). It's a short note that I make about someone else's content online.  Learn more about the process [[ thoughts ]]\n",
      "summary": "I stumbled onto homebox this morning scrolling a newsletter and it looks like something I've been lightly thinking about",
      "date_published": "2025-07-07T10:48:12Z",
      "date_modified": "2025-07-07T10:48:12Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "asset-management",
        "thoughts"
      ]
    },
    {
      "id": "https://pype.dev/thoughts-735/",
      "url": "https://pype.dev/thoughts-735/",
      "title": "💭 Self-Hosting A Cluster On Old Phones | Hackaday",
      "content_text": "\n\u003ca href=\"https://hackaday.com/2025/04/09/self-hosting-a-cluster-on-old-phones/?ref=dailydev\"\u003e\n    \u003cimg\n        src=\"https://shots.wayl.one/shot/?url=https://hackaday.com/2025/04/09/self-hosting-a-cluster-on-old-phones/?ref=dailydev\u0026height=450\u0026width=800\u0026scaled_width=800\u0026scaled_height=450\u0026selectors=\"\n        alt=\"shot of post - Self-Hosting A Cluster On Old Phones | Hackaday\"\n        height=450\n        width=800\n    \u003e\n\u003c/a\u003e\n\nHere's my thought on \u003ca href=\"https://hackaday.com/2025/04/09/self-hosting-a-cluster-on-old-phones/?ref=dailydev\"\u003eSelf-Hosting A Cluster On Old Phones | Hackaday\u003c/a\u003e\n\n---\n\nCame across a blog and article on using a mobile OS to self-host a cluster on old phones... I happen to keep several old android phones lying around and although I'm not presently super interested in figuring out an android/arm based homelab, I am excited for the next coming years when I'm sure it'll only get easier, phones will only be more powerful, and perhaps we'll be running full blown CV pipeline in k8s clusters running on Pixel devices!\n\n---\n\n!!! note\n    This is one of [[ my-thoughts ]]. I picked this up from [Waylon Walker](https://waylonwalker.com)(https://thoughts.waylonwalker.com). It's a short note that I make about someone else's content online.  Learn more about the process [[ thoughts ]]\n",
      "summary": "Came across a blog and article on using a mobile OS to self-host a cluster on old phones... I happen to keep several old",
      "date_published": "2025-07-06T11:30:17Z",
      "date_modified": "2025-07-06T11:30:17Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "k8s",
        "linux",
        "android",
        "homelab",
        "thoughts"
      ]
    },
    {
      "id": "https://pype.dev/github-star-sorter/",
      "url": "https://pype.dev/github-star-sorter/",
      "title": "Github Star Sorter",
      "content_html": "\u003cp\u003eI wrote a simple app to sort, tag, and create tickets at home for my github\nstars\u0026hellip; As of right now I have 1.3k stars and no convenient way to view them\nor remember which ones were ones I actually wanted to do something with. This\nsimple UI makes scrolling the stars list easy, updates is easy, and the kicker\nfor me is Kanboard integration so I can click a button from the stars UI and\nget a ticket made in my kanboard instance so that the work I want to do is in\nfront of me and not hidden by Github\u0026rsquo;s shockingly HORRIBLE UI.\u003c/p\u003e\n\u003cfigure\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20250701125804_c41ec692.png\" alt=\"20250701125804_c41ec692.png\"\u003e\n\u003c/figure\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/pypeaday/gh-star-sorter\"\u003eGH Link\u003c/a\u003e\u003c/p\u003e\n",
      "content_text": "\nI wrote a simple app to sort, tag, and create tickets at home for my github\nstars... As of right now I have 1.3k stars and no convenient way to view them\nor remember which ones were ones I actually wanted to do something with. This\nsimple UI makes scrolling the stars list easy, updates is easy, and the kicker\nfor me is Kanboard integration so I can click a button from the stars UI and\nget a ticket made in my kanboard instance so that the work I want to do is in\nfront of me and not hidden by Github's shockingly HORRIBLE UI.\n\n![20250701125804_c41ec692.png](https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20250701125804_c41ec692.png)\n\n[GH Link](https://github.com/pypeaday/gh-star-sorter)\n",
      "summary": "I wrote a simple app to sort, tag, and create tickets at home for my github stars... As of right now I have 1.3k stars and no convenient way to view them or...",
      "date_published": "2025-07-01T08:01:31Z",
      "date_modified": "2025-07-01T08:01:31Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech"
      ]
    },
    {
      "id": "https://pype.dev/temporal/",
      "url": "https://pype.dev/temporal/",
      "title": "temporal",
      "content_html": "\u003cp\u003e\u003ca href=\"https://temporal.io/\"\u003eTemporal\u003c/a\u003e is a workflow orchestrator that I\u0026rsquo;m interested in\u003c/p\u003e\n",
      "content_text": "\n[Temporal](https://temporal.io/) is a workflow orchestrator that I'm interested in\n",
      "summary": "Temporal is a workflow orchestrator that I'm interested in",
      "date_published": "2025-06-24T21:21:43Z",
      "date_modified": "2025-06-24T21:21:43Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech",
        "temporal"
      ]
    },
    {
      "id": "https://pype.dev/traefik-and-grpc-for-temporal-at-home/",
      "url": "https://pype.dev/traefik-and-grpc-for-temporal-at-home/",
      "title": "Traefik and gRPC for Temporal at home",
      "content_text": "\n# Temporal Networking Woes\n\nSomeday I will potentially write up some stuff about [[temporal]] but for now\nit's a workflow orchestrator that I'm interested in and I wanted to PoC it in\nmy homelab by replacing a simple cron job to build my blog with a temporal\nworkflow and activity. I was IMMEDIATELY thwarted by a networking error that\nfeels crazy to me but maybe won't be to others... so enjoy the problem and if\nyou're here from a Google search, I hope it's helpful.\n\nGetting started with Temporal I wanted to have the server and associated\ncomponents on my server, but I expected to be running workflows and workers\non another computer. \n\nFor most of my apps I slap some [[traefik]] labels on\nand we're good to go. But I got a wild set of errors trying to run a worker on\nmy desktop and connect it to the temporal server on my server over https.... it\nended up being because I needed to setup a special protocol in traefik for the\nreverse proxy to appropriately proxy the grpc call.\n\nSo I had a normal set of labels on my temporal container, went to run a worker\nand got this nasty work of art\n\n```\nRuntimeError: Failed client connect: `get_system_info` call error after connection: Status { code: Internal, message: \"protocol error: received message with invalid compression flag: 73 (valid flags are 0 and 1) while receiving response with status: 500 Internal Server Error\", metadata: MetadataMap { headers: {\"content-type\": \"text/plain; charset=utf-8\", \"content-length\": \"21\", \"date\": \"Wed, 25 Jun 2025 02:12:44 GMT\"} }, source: None }\n```\n\nThank God I had kind of seen this at work one other time in my life, and we\nnever fixed the issue there but I knew what it was... we had traefik acting as\na load balancer (as do I at home) and the traefik load balancer was \"balancing\ntraffic using https\"... no, I do not really know what I'm saying. But what I do\nknow is that in order for the Temporal Server and Worker to communicate,\ntraefik has to properly route the traffic for gRPC instead of https.\n\n!!! warning \"\"\n    I can't stress enough how little I know about the difference between https and grpc or why the load balancer cares... there's clearly something in the networking stack that is extremely relevant here\n\nBut the fix is NOT hard thankfully, and it's even on [traefik's website](https://doc.traefik.io/traefik/user-guides/grpc/#traefik-configuration) - the warning annotation there tells you what you need - which is one more traefik label...\n\n```yaml\ntraefik.http.services.\u003cmy-service-name\u003e.loadbalancer.server.scheme=h2c\n```\n\nSo after setting the loadbalancer.server.scheme to `h2c` I could run my worker just fine!\n\n![20250625021838_4d7104c2.png](https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20250625021838_4d7104c2.png)\n",
      "summary": "Someday I will potentially write up some stuff about temporal but for now it's a workflow orchestrator that I'm interested in and I wanted to PoC it in my...",
      "date_published": "2025-06-24T20:55:42Z",
      "date_modified": "2025-06-24T20:55:42Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech",
        "temporal",
        "grpc"
      ]
    },
    {
      "id": "https://pype.dev/nextcloud-php-opache-memory-consumption/",
      "url": "https://pype.dev/nextcloud-php-opache-memory-consumption/",
      "title": "Nextcloud PHP Opache Memory Consumption",
      "content_html": "\u003ch1 id=\"intro\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eIntro\u003c/span\u003e\u003c/h1\u003e\n\u003cp\u003eToday I was combing through Nextcloud, just taking a gander at the apps, updates, etc.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eThe OPcache buffer is nearly full. To assure that all scripts can be hold in\ncache, it is recommended to apply \u0026ldquo;opcache.memory_consumption\u0026rdquo; to your PHP\nconfiguration with a value higher than \u0026ldquo;128\u0026rdquo;\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eSo I know \u003cem\u003enothing\u003c/em\u003e about \u003ccode\u003ephp\u003c/code\u003e and I had no idea where to set this\u0026hellip; Also I\u0026rsquo;m\nrunning Nextcloud in a container so any updates will be blown away when the\ncontainer is removed (say next time I update Nextcloud)\u0026hellip;\u003c/p\u003e\n\u003cp\u003eAfter a little duckduckgoing I came across \u003ca href=\"https://www.reddit.com/r/NextCloud/comments/1gpxl5b/need_help_with_php_opcache_module_warning/\"\u003ethis reddit\npost\u003c/a\u003e\nwhere a guy had the same issue\u0026hellip;\u003c/p\u003e\n\u003cp\u003eThe long and short of it is that on \u003cem\u003esingle instance nextcloud deployments\u003c/em\u003e\nthis php memory consumption value can be too low for Nextcloud to keep up with\nthe demand\u0026hellip; You can see in the message they just recommend upping the value.\u003c/p\u003e\n\u003cp\u003eSo I had to think about how to persist the change and have it take effect now\nideally without affecting any users too much\u0026hellip; So here\u0026rsquo;s what I did - made the\nchange in 2 places:\u003c/p\u003e\n\u003ch2 id=\"in-the-container\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eIn the container\u003c/span\u003e \u003ca href=\"#in-the-container\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI exec\u0026rsquo;d into the container and used this handy \u003ccode\u003esed\u003c/code\u003e command \u003ccode\u003esed -i 's/^opcache\\.memory_consumption\\s*=\\s*128$/opcache.memory_consumption=256/' /usr/local/etc/php/conf.d/opcache-recommended.ini\u003c/code\u003e to update the value in the\nrunning container - and I guess \u003ccode\u003ephp\u003c/code\u003e maybe is loaded up dynamically or\nsomething? but the warning did go away\u0026hellip;.\u003c/p\u003e\n\u003cp\u003eSo how to persist this change when the filesystem is blown away when the\ncontainer is updated?\u003c/p\u003e\n\u003ch2 id=\"in-the-config\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eIn the config\u003c/span\u003e \u003ca href=\"#in-the-config\" class=\"heading-anchor\"\u003e#\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI set the env var \u003ccode\u003ePHP_OPCACHE_MEMORY_CONSUMPTION\u003c/code\u003e in the \u003ccode\u003edocker-compose.yml\u003c/code\u003e\nfile to 256 and voila\u0026hellip; the change should persist\u0026hellip;\u003c/p\u003e\n",
      "content_text": "\n# Intro\n\nToday I was combing through Nextcloud, just taking a gander at the apps, updates, etc.\n\n\u003e The OPcache buffer is nearly full. To assure that all scripts can be hold in\n\u003e cache, it is recommended to apply \"opcache.memory_consumption\" to your PHP\n\u003e configuration with a value higher than \"128\"\n\nSo I know _nothing_ about `php` and I had no idea where to set this... Also I'm\nrunning Nextcloud in a container so any updates will be blown away when the\ncontainer is removed (say next time I update Nextcloud)...\n\nAfter a little duckduckgoing I came across [this reddit\npost](https://www.reddit.com/r/NextCloud/comments/1gpxl5b/need_help_with_php_opcache_module_warning/)\nwhere a guy had the same issue...\n\nThe long and short of it is that on _single instance nextcloud deployments_\nthis php memory consumption value can be too low for Nextcloud to keep up with\nthe demand... You can see in the message they just recommend upping the value.\n\nSo I had to think about how to persist the change and have it take effect now\nideally without affecting any users too much... So here's what I did - made the\nchange in 2 places:\n\n## In the container\n\nI exec'd into the container and used this handy `sed` command `sed -i\n's/^opcache\\.memory_consumption\\s*=\\s*128$/opcache.memory_consumption=256/'\n/usr/local/etc/php/conf.d/opcache-recommended.ini` to update the value in the\nrunning container - and I guess `php` maybe is loaded up dynamically or\nsomething? but the warning did go away....\n\nSo how to persist this change when the filesystem is blown away when the\ncontainer is updated?\n\n## In the config\n\nI set the env var `PHP_OPCACHE_MEMORY_CONSUMPTION` in the `docker-compose.yml`\nfile to 256 and voila... the change should persist...\n",
      "summary": "Today I was combing through Nextcloud, just taking a gander at the apps, updates, etc.",
      "date_published": "2025-06-23T08:46:20Z",
      "date_modified": "2025-06-23T08:46:20Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech",
        "nextcloud"
      ]
    },
    {
      "id": "https://pype.dev/quit-issueing-200s-for-500s/",
      "url": "https://pype.dev/quit-issueing-200s-for-500s/",
      "title": "Quit issueing 200s for 500s",
      "content_html": "\u003ch1 id=\"stop-this-nonsense\"\u003e\u003cspan class=\"heading-wear-glyph\"\u003eStop this nonsense\u003c/span\u003e\u003c/h1\u003e\n\u003cfigure\u003e\n\u003cimg src=\"https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20250622115500_5bd3bc21.png\" alt=\"20250622115500_5bd3bc21.png\"\u003e\n\u003c/figure\u003e\n",
      "content_text": "\n# Stop this nonsense\n\n![20250622115500_5bd3bc21.png](https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20250622115500_5bd3bc21.png)\n",
      "date_published": "2025-06-22T06:55:21Z",
      "date_modified": "2025-06-22T06:55:21Z",
      "authors": [
        {
          "name": "Nic Payne",
          "url": "https://pype.dev"
        }
      ],
      "tags": [
        "homelab",
        "tech",
        "nonsense",
        "google",
        "veo3"
      ]
    }
  ]
}