{
  "version": "https://jsonfeed.org/version/1",
  "title": "Ian's Digital Garden",
  "home_page_url": "https://ianwwagner.com/",
  "feed_url": "https://ianwwagner.com//tag-openbsd.json",
  "description": "",
  "items": [
    {
      "id": "https://ianwwagner.com//overview-of-my-new-homelab-setup.html",
      "url": "https://ianwwagner.com//overview-of-my-new-homelab-setup.html",
      "title": "Overview of my New Homelab Setup",
      "content_html": "<p>There are two things that tend to create explosions of ideas in my head,\nand often result in significant actions.\nThose are boredom (like being on &quot;vacation&quot; for more than 24h)\nand a sufficient level of annoyance with something.</p>\n<p>There are enough higher quality rants out there,\nso I'll spare you all the reasons,\nbut I recently decided to start running a homelab again due to the latter.</p>\n<p>In this post I'll give a broad overview of the architecture I settled on,\nand why I made specific choices.\nI'll try to keep things relatively high level here,\nand we'll dive into the implementation details in future posts.</p>\n<h1><a href=\"#my-requirements\" aria-hidden=\"true\" class=\"anchor\" id=\"my-requirements\"></a>My requirements</h1>\n<p>Let's start from the beginning with what I'm trying to achieve.\nI want to host multiple services, some of them internet-accessible,\nfrom a box on my desk running FreeBSD.\nSpecifically FreeBSD, because I find it has an uncommon trio of properties:</p>\n<ul>\n<li>It's easy to administer.</li>\n<li>It's rock solid stable.</li>\n<li>It has a huge collection of up-to-date packages.</li>\n</ul>\n<p>It's surprisingly rare to find all 3 in a system.</p>\n<p>I already have the hardware (an old NUC, which I've named <code>bsdcube</code> even though it's not a cube),\nand my home internet is rock solid, but I don't have a static IP.\nEven if I did, I'm not sure I want to run some services on a residential IP,\nor to invite attacks on my home network.</p>\n<h1><a href=\"#broad-architecture-overview\" aria-hidden=\"true\" class=\"anchor\" id=\"broad-architecture-overview\"></a>Broad architecture overview</h1>\n<p>The architecture that I chose keeps <code>bsdcube</code> on my home network behind the NAT firewall.\nTo make the server accessible from the internet,\nI use a WireGuard tunnel to a remote host that is internet-accessible.\nPF rules on the accessible server route certain traffic over the tunnel and block the rest.</p>\n<pre><code class=\"language-txt\"> +----------+       +---------+                         +---------+\n | Internet |------&gt;| proxbox |&lt;---WireGuard-Tunnel----&gt;| bsdcube |\n +----------+       +---------+                         +---------+\n</code></pre>\n<h1><a href=\"#choosing-a-host-for-proxbox\" aria-hidden=\"true\" class=\"anchor\" id=\"choosing-a-host-for-proxbox\"></a>Choosing a host for <code>proxbox</code></h1>\n<p>For my internet-accessible box, which I dubbed <code>proxbox</code>,\nI wanted a reliable host that supported a BSD.\nI was initially planning on getting a FreeBSD box for the job.\nI even had a box set up, as described in <a href=\"setting-up-a-wireguard-tunnel-on-freebsd-15.html\">my post on the subject</a>.\n(I guess I'll need to write a follow-up now!)</p>\n<p>I was somewhat discouraged by the poor support of FreeBSD on most cloud providers.\nThere is ironically a split where a lot of the mega-hosts like AWS have tier 1 support,\nbut there's nothing in the middle.\nMany of the midrange hosts which had good FreeBSD VM support now require a tedious manual install process.\nVultr seemed to be the only host that had at least some level of support,\nbut they were not my first choice.</p>\n<p>Then my friend <a href=\"https://andrewzah.com/blog/\">Andrew</a> mentioned <a href=\"https://openbsd.amsterdam/\">OpenBSD Amsterdam</a>.\nIt was love at first sight.\nI had, somehow (despite being a FreeBSD using since around 2002) never actually used OpenBSD.\nBut I loved their pitch: a community-focused host that donates a large part of every purchase\nto the OpenBSD foundation.</p>\n<p>The onboarding was fast and easy.\nI filled out the form, and within 2 hours I had an email with connection instructions!\nI hadn't even been given an invoice yet!\nReally fantastic service.</p>\n<h1><a href=\"#proxbox-setup-using-the-openbsd-base-system\" aria-hidden=\"true\" class=\"anchor\" id=\"proxbox-setup-using-the-openbsd-base-system\"></a><code>proxbox</code> setup using the OpenBSD base system</h1>\n<p>Now let's talk about the guts of <code>proxbox</code>.\nSurprisingly, almost everything running on this box is part of the OpenBSD base system!</p>\n<pre><code class=\"language-txt\">                            +-------+                                                     \n  /--\\      +-----HTTP-----&gt;| httpd |                                                     \n /    \\ ----+               |       |                                                     \n \\ PF /                     +-------+               +---------+                      \n  \\__/  ----+               +--------+  +---blog---&gt;| Varnish |--+   +---------------+\n            |               | relayd |--+           |         |  +--&gt;| WG -&gt; bsdcube |\n            +-----HTTPS----&gt;|        |--+           +---------+  +--&gt;|               |\n                            +--------+  |                        |   +---------------+\n                                        +-------matrix-----------+                   \n</code></pre>\n<p>It all starts with PF, the OpenBSD Packet Filter.\nIn addition to rejecting invalid / unwanted traffic,\nit's responsible for routing the legit packets to the right service.</p>\n<h2><a href=\"#serving-https-with-httpd8-and-relayd8\" aria-hidden=\"true\" class=\"anchor\" id=\"serving-https-with-httpd8-and-relayd8\"></a>Serving HTTP(S) with <code>httpd(8)</code> and <code>relayd(8)</code></h2>\n<p>An HTTP server sits on the box for handling the ACME challenges\n(e.g. from <a href=\"https://letsencrypt.org/\">Let's Encrypt</a>) to get a TLS cert.\nOpenBSD has <a href=\"https://man.openbsd.org/httpd.8\"><code>httpd(8)</code></a>,\nnot to be confused with the Apache <code>httpd</code>.\nIt can serve static files and FastCGI, but it's not a reverse proxy.</p>\n<p>Normally I'd reach for something like nginx, HAProxy, or Caddy,\nbut OpenBSD also has an excellent (load balancing) reverse proxy in the base system already:\n<a href=\"https://man.openbsd.org/relayd.8\"><code>relayd(8)</code></a>.</p>\n<p><code>relayd</code> in my setup is responsible for 2 things:\nrouting to the correct backend (e.g. based on SNI),\nand TLS termination.\nI'll write a longer post about this soon,\nincluding how to configure <a href=\"https://man.openbsd.org/acme-client.1\"><code>acme-client(1)</code></a>to get certs automatically,\nand configuring <code>relayd</code> to terminate TLS for multiple domains.</p>\n<h2><a href=\"#varnish-vinyl-cache\" aria-hidden=\"true\" class=\"anchor\" id=\"varnish-vinyl-cache\"></a>Varnish (Vinyl) cache</h2>\n<p>Since my blog is just a static site,\nit would be quite reasonable to host it with <code>httpd</code> on <code>proxbox</code>.\nThis <em>would</em> result in faster response times,\nbut I decided to host it on <code>bsdcube</code> to make the setup simpler.\nThe clear separation of concerns, with <code>bsdcube</code> being the final source of truth\nfor all data and services makes it easier for me to maintain.\nI'll never had to touch <code>proxbox</code> outside of routine system maintenance or adding a new service port.</p>\n<p>That said, I'm not going to just blindly forward <em>every single request</em> over to my home network.\nI've used <a href=\"https://vinyl-cache.org/\">Varnish Cache (now rebranded to Vinyl)</a>\nfor over a decade in various professional contexts,\nand it does an excellent job at absorbing traffic spikes,\nand smoothing over issues when the backend goes flaky.\nSince I'm tunneling everything over the internet,\nhalfway around the world,\nthis seems like table stakes.</p>\n<h1><a href=\"#the-middle-wireguard\" aria-hidden=\"true\" class=\"anchor\" id=\"the-middle-wireguard\"></a>The Middle: WireGuard</h1>\n<p>Whatever route a legitimate packet takes,\nif it needs to talk to a backend service,\nthis happens over a WireGuard tunnel.</p>\n<p>I could have done this with something like Tailscale or Cloudflare Warp,\nbut I wanted something that wasn't reliant on a &quot;free&quot; external service.\nSuch services are rarely free forever.\nRolling your own tunnel with a proven technology is <a href=\"setting-up-a-wireguard-tunnel-on-freebsd-15.html\">surprisingly easy</a>,\nand my intentional choice of a somewhat offbeat rest of the stack made the decision a no-brainer.\nBoth FreeBSD and OpenBSD have WireGuard support built-in at the kernel level already.</p>\n<p>PF rules on <em>both</em> sides of the tunnel ensure that only certain traffic passes through.\nI will probably write a post on this later as well as there's quite a lot here.</p>\n<h1><a href=\"#bsdcube\" aria-hidden=\"true\" class=\"anchor\" id=\"bsdcube\"></a><code>bsdcube</code></h1>\n<p>Let's look at the other side of the tunnel now.\n<code>bsdcube</code> sits on my home network behind a NAT firewall.\nIt only accepts traffic over the tunnel for specific service ports.\nAnd those ports are mapped to jailed services with a limited view of the system.\nIn case you're not familiar with FreeBSD, jails are a FreeBSD-native containerization technology\nwhich has been around for over 25 years (that's a long time before Docker popularized the term).\nWhile they have many differences with Linux containers,\nthat's a reasonable mental model for understanding the rest of this post.</p>\n<h2><a href=\"#appjail\" aria-hidden=\"true\" class=\"anchor\" id=\"appjail\"></a>AppJail</h2>\n<p>Jails are native to the FreeBSD kernel,\nand the base system has <code>jail(8)</code> for managing them.\nBut much as in the Linux world,\nmost people rely on higher level tools for management and orchestration.</p>\n<p>There are over a dozen such tools for FreeBSD,\neach with a different take on things.\nThat's both cool and bewildering ;)\nI ended up settling on <a href=\"https://appjail.readthedocs.io/en/latest/\">AppJail</a> for <code>bsdcube</code>, at least for now,\nsince it has excellent documentation,\nsupport for features I'm curious about later (like Linux jails),\nand a declarative, composable configuration format.</p>\n<h3><a href=\"#creating-jails-with-makejail-files\" aria-hidden=\"true\" class=\"anchor\" id=\"creating-jails-with-makejail-files\"></a>Creating jails with <code>Makejail</code> files</h3>\n<p>Here's a sample of a <code>Makejail</code> file.\nAs you might guess, it's a set of instructions for building a jail.\nIt looks a bit like a Dockerfile.</p>\n<pre><code># blog/Makejail\n\n# Include directives let you separate out sections of your config across files\n# and reuse common sections.\nINCLUDE options/options.makejail\nINCLUDE options/network.makejail\n\n# Install nginx from the FreeBSD package repository\nPKG nginx\n\n# A sysrc directive.\n# This is the equivalent of enabling a service on a Linux box with systemd\nSYSRC nginx_enable=YES\n\n# Copies nginx.conf from the current directory into the specified path in the jail.\nCOPY nginx.conf /usr/local/etc/nginx/nginx.conf\n\n# Install Marmite, the static site generator I use for my blog.\n# Despite being relatively obscure, there's also an up-to-date package for this!\nPKG marmite\n\nINCLUDE buildsite.makejail\n\nSERVICE nginx start\n</code></pre>\n<h2><a href=\"#jail-lifecycle-management\" aria-hidden=\"true\" class=\"anchor\" id=\"jail-lifecycle-management\"></a>Jail lifecycle management</h2>\n<p>&quot;Scripted&quot; setups like this can get pretty unwieldy if you're not careful.\nAnd for me, one of the worst parts of CLI tools is remembering the half dozen switches\nto get a particular task done.</p>\n<p>To solve this, I structured my jail deployments as a git repository\nwhere each subdirectory is a self-contained service.\nIn my setup, there are no dependencies, and I intend to keep it that way,\ndeploying the full application/service in isolated jails,\neven if I could theoretically have a &quot;common&quot; service like, say, a single Postgres database.\nAs such, it's easy to add a <a href=\"https://just.systems/man/en/introduction.html\"><code>justfile</code></a> to each directory\nwith deployment recipes so I don't have to remember them.</p>\n<p>The usual invocation is something like <code>just freshjail</code> to (re)create a jail.\nAnd for jails like my blog, where the <em>application</em> has no need to restart\nto get an update, I have recipe(s) that perform the necessary updates against the running jail.</p>\n<h1><a href=\"#conclusion-or-whats-next\" aria-hidden=\"true\" class=\"anchor\" id=\"conclusion-or-whats-next\"></a>Conclusion (or, what's next)</h1>\n<p>That was a lot... and there's still a lot I didn't cover!\nLike pointers on the AppJail host setup, PF, handling multiple hosts with SNI via relayd, and a lot more.\nSo stay tuned for the next post,\nand give a shout on Mastodon if you have any feedback.</p>\n<p>In the meantime, I've uploaded the AppJail part of my setup\nto a <a href=\"https://codeberg.org/ianthetechie/appjails/\">public repo on Codeberg</a>.\nHopefully they are useful for others!</p>\n",
      "summary": "",
      "date_published": "2026-04-11T00:00:00-00:00",
      "image": "",
      "authors": [
        {
          "name": "Ian Wagner",
          "url": "https://fosstodon.org/@ianthetechie",
          "avatar": "media/avi.jpeg"
        }
      ],
      "tags": [
        "homelab",
        "FreeBSD",
        "OpenBSD",
        "networking",
        "jails",
        "containerization"
      ],
      "language": "en"
    }
  ]
}