<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Nick Bodmer</title><description>Enthusiastically sharing curiosity.</description><link>https://nickbodmer.com/</link><language>en-us</language><item><title>As Built: The Homelab That Runs This Site</title><link>https://nickbodmer.com/posts/as-built-how-this-site-came-together/</link><guid isPermaLink="true">https://nickbodmer.com/posts/as-built-how-this-site-came-together/</guid><description>Notes to future me: what this site runs on, how the pieces fit, and the two things I&apos;ll actually need later — how to change this site, and how to stand up a ne…</description><pubDate>Sat, 18 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Notes to future me: what this site runs on, how the pieces fit, and the two things I&apos;ll actually need later — how to change this site, and how to stand up a new one.&lt;/p&gt;
&lt;h2&gt;The stack&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hardware:&lt;/strong&gt; an Intel NUC running &lt;strong&gt;Proxmox&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platform:&lt;/strong&gt; &lt;strong&gt;Coolify&lt;/strong&gt; in a VM on that Proxmox host&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Each site:&lt;/strong&gt; a Docker container — a multi-stage build (a Node image builds the Astro site, an nginx image serves the static files) — built and run by Coolify from a GitHub repo&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ingress:&lt;/strong&gt; a &lt;strong&gt;Cloudflare Tunnel&lt;/strong&gt; into Coolify&apos;s built-in Traefik proxy&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DNS + TLS:&lt;/strong&gt; Cloudflare (nameservers, proxied records, edge certificates)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deploys:&lt;/strong&gt; &lt;code&gt;git push&lt;/code&gt; plus one Coolify API call&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No ports are forwarded on the router. Nothing in the homelab is reachable from the internet except through the tunnel.&lt;/p&gt;
&lt;h2&gt;How it was built&lt;/h2&gt;
&lt;p&gt;Two Claude sessions did the work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Claude (design)&lt;/strong&gt; — I wrote a brief: the mission statement, the pages I wanted, real content, and hard constraints (system fonts, one accent color, light + dark, vanilla CSS). It produced a design system as CSS tokens plus HTML mockups of every page. That handoff is committed in the repo under &lt;code&gt;design/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Claude Code&lt;/strong&gt; — translated the design into an Astro static site, migrated every old post (Hugo + micro.blog) into &lt;code&gt;src/content/posts/&lt;/code&gt;, and wired up the deploy. I made the decisions; it did the typing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Source is public: &lt;a href=&quot;https://github.com/ampsonic/nickbodmer.com&quot;&gt;github.com/ampsonic/nickbodmer.com&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;How Cloudflare routes a request&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;DNS for the domain lives at Cloudflare, proxied (orange cloud).&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Cloudflare Tunnel&lt;/strong&gt; links my network to Cloudflare. The &lt;code&gt;cloudflared&lt;/code&gt; connector dials &lt;em&gt;out&lt;/em&gt;, so there&apos;s no inbound hole into the homelab.&lt;/li&gt;
&lt;li&gt;In the tunnel&apos;s &lt;strong&gt;public hostname&lt;/strong&gt; routes, the domain points at Coolify&apos;s Traefik proxy on the LAN.&lt;/li&gt;
&lt;li&gt;Traefik reads the &lt;code&gt;Host&lt;/code&gt; header and hands the request to the right site&apos;s container. Every site shares the one tunnel and the one proxy; the hostname is what separates them.&lt;/li&gt;
&lt;li&gt;TLS is terminated at Cloudflare&apos;s edge, so the internal hop is plain HTTP. &lt;strong&gt;Always Use HTTPS&lt;/strong&gt; is on, and &lt;code&gt;www&lt;/code&gt; is a proxied CNAME with a redirect rule to the apex.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Because Traefik routes by hostname, I can test any site from the LAN before its DNS exists — just send the request to Coolify with the right &lt;code&gt;Host&lt;/code&gt; header.&lt;/p&gt;
&lt;h2&gt;Changing this site&lt;/h2&gt;
&lt;p&gt;Everything lives in a local checkout of the repo.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;New post:&lt;/strong&gt; add a &lt;code&gt;.md&lt;/code&gt; (or &lt;code&gt;.mdx&lt;/code&gt; for a YouTube embed) to &lt;code&gt;src/content/posts/&lt;/code&gt;. Frontmatter needs &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;date&lt;/code&gt;; set &lt;code&gt;draft: false&lt;/code&gt; when it&apos;s ready.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Preview locally:&lt;/strong&gt; &lt;code&gt;npm run dev&lt;/code&gt;, or &lt;code&gt;npm run build &amp;amp;&amp;amp; npm run preview&lt;/code&gt; for the real static output.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ship it:&lt;/strong&gt; &lt;code&gt;npm run deploy&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;npm run deploy&lt;/code&gt; pushes to GitHub and then calls Coolify&apos;s deploy API directly. A plain &lt;code&gt;git push&lt;/code&gt; won&apos;t deploy on its own: Coolify isn&apos;t exposed to the internet, so GitHub&apos;s webhook can&apos;t reach it — the script is what tells Coolify to rebuild. (The API token lives in &lt;code&gt;.env.deploy&lt;/code&gt;, gitignored — single-quote it, the token contains a &lt;code&gt;|&lt;/code&gt;.)&lt;/p&gt;
&lt;h2&gt;Standing up a new site&lt;/h2&gt;
&lt;p&gt;The repeatable version of what I just did:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Build it&lt;/strong&gt; — Claude Code, into a GitHub repo. Include the &lt;code&gt;Dockerfile&lt;/code&gt; + &lt;code&gt;nginx.conf&lt;/code&gt; from this repo (styled 404, gzip, cached assets).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloudflare zone&lt;/strong&gt; — point the domain&apos;s nameservers at Cloudflare.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coolify&lt;/strong&gt; — new app from the repo → Build Pack: &lt;strong&gt;Dockerfile&lt;/strong&gt; → expose port 80 → set the domain → deploy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tunnel route&lt;/strong&gt; — add a public hostname for the new domain, pointing at Coolify&apos;s Traefik. Same tunnel, same proxy; Traefik sorts it out by Host.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Polish&lt;/strong&gt; — turn on Always Use HTTPS; add a &lt;code&gt;www&lt;/code&gt; CNAME and a WWW→root redirect if wanted.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;One-command deploys&lt;/strong&gt; — copy &lt;code&gt;scripts/deploy.sh&lt;/code&gt; and the &lt;code&gt;.env.deploy&lt;/code&gt; pattern, set the new app&apos;s UUID (from its Coolify URL) and an API token.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&apos;s the whole machine. Future me: if a deploy doesn&apos;t show up, it&apos;s almost always the webhook — just run &lt;code&gt;npm run deploy&lt;/code&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Taming the Home Lab: Friendly Local Domains with UniFi + Pi‑hole + NPM</title><link>https://nickbodmer.com/posts/taming-the-home-lab-friendly/</link><guid isPermaLink="true">https://nickbodmer.com/posts/taming-the-home-lab-friendly/</guid><description>Last week I finally hit my breaking point with URLs like Sure, I can remember that Plex runs on port 32400… but what about Home Assistant? Or my random test co…</description><pubDate>Sat, 28 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last week I finally hit my breaking point with URLs like &lt;code&gt;http://192.168.1.10:32400&lt;/code&gt;. Sure, I &lt;em&gt;can&lt;/em&gt; remember that Plex runs on port 32400… but what about Home Assistant? Or my random test container from three months ago? My brain already holds enough useless trivia—memorizing port numbers doesn’t need to be part of the collection.&lt;/p&gt;
&lt;p&gt;I wanted a clean, memorable way to reach every self‑hosted service on my network—&lt;code&gt;plex.home.arpa&lt;/code&gt;, &lt;code&gt;pihole.home.arpa&lt;/code&gt;, &lt;code&gt;npm.home.arpa&lt;/code&gt;, you name it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First stop: Nginx Proxy Manager (NPM).&lt;/strong&gt; It’s the brains that maps each friendly hostname to the right internal port so I never type &lt;code&gt;:32400&lt;/code&gt; again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The snag:&lt;/strong&gt; my UniFi Cloud Gateway Fiber can’t point a wildcard domain (&lt;code&gt;*.home.arpa&lt;/code&gt;) straight at the NPM container, so NPM alone didn’t get me there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Enter Pi‑hole.&lt;/strong&gt; By taking over DNS, Pi‑hole answers every &lt;code&gt;*.home.arpa&lt;/code&gt; query with the IP of my Mac mini—the box where NPM is listening. UniFi forwards DNS to Pi‑hole, Pi‑hole hands out the single IP, and NPM does the port‑mapping magic. Two tools, one neat solution.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Side note:&lt;/strong&gt; All my containers run inside &lt;a href=&quot;https://orbstack.dev/&quot;&gt;OrbStack&lt;/a&gt; using &lt;code&gt;docker compose&lt;/code&gt;. &lt;strong&gt;HTTP‑only for simplicity&lt;/strong&gt; – I’m keeping everything on plain HTTP inside the LAN.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Below is the final setup, why I chose each piece, and enough breadcrumbs for &lt;em&gt;Future Me&lt;/em&gt; (and maybe &lt;em&gt;Present You&lt;/em&gt;) to recreate it without the usual home‑lab tinkering.&lt;/p&gt;
&lt;h2&gt;Why I bothered&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Human‑friendly URLs&lt;/strong&gt; – I can type “plex” instead of an IP:port combo.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Single entry point&lt;/strong&gt; – NPM puts every service behind one memorable domain.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ad‑blocking for free&lt;/strong&gt; – If Pi‑hole is already answering DNS, why not?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;One place to grow&lt;/strong&gt; – Adding a new service is a 10‑second NPM host rule.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Gear &amp;amp; high‑level layout&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Box&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Role&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Key Detail&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UniFi Cloud Gateway Fiber (UCG)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Router / DHCP&lt;/td&gt;
&lt;td&gt;Hands out itself (&lt;code&gt;192.168.1.1&lt;/code&gt;) as DNS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mac mini (192.168.1.10)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Docker host&lt;/td&gt;
&lt;td&gt;Runs Pi‑hole + NPM + everything else&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;DNS path in one breath: &lt;strong&gt;Client → UCG → Pi‑hole → wildcard → NPM → internal service&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Step‑by‑step&lt;/h2&gt;
&lt;h3&gt;1. Deploy Pi‑hole &amp;amp; Nginx Proxy Manager containers&lt;/h3&gt;
&lt;p&gt;Spin up both services using OrbStack + &lt;code&gt;docker compose&lt;/code&gt; (or your container runtime of choice).&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Pi‑hole defaults to port 80 for its admin UI, but that clashes with NPM’s reverse‑proxy listener, so I remapped Pi‑hole’s web interface to port 82 in my&lt;/em&gt; &lt;code&gt;docker‑compose.yml&lt;/code&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The only ports you need exposed are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pi‑hole&lt;/strong&gt;: 53/udp + 53/tcp (DNS) and 82/tcp (web UI)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NPM&lt;/strong&gt;: 80/tcp (reverse proxy) and 81/tcp (admin UI)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s it—we’ll skip the YAML here to keep things short.&lt;/p&gt;
&lt;h3&gt;2. Point the UCG at Pi‑hole&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Settings → Internet → DNS&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Primary&lt;/em&gt;: &lt;code&gt;192.168.1.10&lt;/code&gt; (Pi‑hole)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Secondary&lt;/em&gt;: &lt;em&gt;(leave blank)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I originally tried adding Cloudflare (&lt;code&gt;1.1.1.1&lt;/code&gt;) as a backup so the household would stay online if the Mac mini went down. &lt;strong&gt;Bad idea.&lt;/strong&gt; UniFi doesn’t strictly prefer the primary resolver—it will query the secondary even when the primary is healthy. Each time that happened Cloudflare returned &lt;strong&gt;NXDOMAIN&lt;/strong&gt; for my internal hosts, the gateway cached the negative answer, and local lookups failed until I rebooted the gateway.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Settings → Network → LAN → DNS Mode&lt;/strong&gt;: &lt;strong&gt;Auto&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;DHCP keeps handing out &lt;code&gt;192.168.1.1&lt;/code&gt; to clients. Behind the scenes, the gateway forwards everything to Pi‑hole, so if Pi‑hole ever goes down the network still feels alive.&lt;/p&gt;
&lt;h3&gt;3. Add a wildcard override in Pi‑hole&lt;/h3&gt;
&lt;p&gt;In the Pi‑hole Admin UI, go to &lt;strong&gt;Settings → All Settings → Miscellaneous → misc.dnsmasq_lines&lt;/strong&gt; and paste:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;address=/.home.arpa/192.168.1.10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Click &lt;strong&gt;Save &amp;amp; Restart DNS&lt;/strong&gt;. From now on, every &lt;code&gt;*.home.arpa&lt;/code&gt; hostname resolves to the Mac mini.&lt;/p&gt;
&lt;h3&gt;4. Create proxy hosts in NPM&lt;/h3&gt;
&lt;p&gt;Inside the NPM admin UI (&lt;code&gt;http://192.168.1.10:81&lt;/code&gt;), add a &lt;strong&gt;Proxy Host&lt;/strong&gt; for each service:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Domain&lt;/td&gt;
&lt;td&gt;Forward To&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;plex.home.arpa&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;http://192.168.1.10:32400&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm.home.arpa&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;http://192.168.1.10:81&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pihole.home.arpa&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;http://192.168.1.10:82&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Because we’re sticking with HTTP internally, there’s no SSL checkbox to worry about. It Just Works.&lt;/p&gt;
&lt;p&gt;Open the browser—no ports, no IPs, just &lt;strong&gt;plex.home.arpa&lt;/strong&gt;. Victory.&lt;/p&gt;
&lt;h3&gt;TL;DR Config Recap&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Clients            → DNS 192.168.1.1 (UCG)
UCG (forward DNS)  → 192.168.1.10 (Pi‑hole)
Pi‑hole wildcard   → *.home.arpa → 192.168.1.10
NPM port 80        → Reverse‑proxy to service ports
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simple, memorable hostnames and one less mental lookup table.&lt;/p&gt;
&lt;p&gt;Future Nick, if you’re troubleshooting this at 2 a.m., you’re welcome.&lt;/p&gt;
</content:encoded></item><item><title>Spacial Vision Livestream - The Talk Show: Live from WWDC 2024</title><link>https://nickbodmer.com/posts/spacial-vision-livestream/</link><guid isPermaLink="true">https://nickbodmer.com/posts/spacial-vision-livestream/</guid><description>Last night, I watched the entirety of The Talk Show Live from my Vision Pro, and it was an incredible experience. As always, the show was top-notch; I loved he…</description><pubDate>Wed, 12 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last night, I watched the entirety of The Talk Show Live from my Vision Pro, and it was an incredible experience. As always, the show was top-notch; I loved hearing the Apple team speak with such passion about their work.&lt;/p&gt;
&lt;p&gt;The common narrative that Apple is lagging in AI seems misguided. The sophisticated privacy initiative involving private cloud computing appears to be the result of years of development. Considering the complexity and potential costs, a subscription component for AI services wouldn’t surprise me in the future.&lt;/p&gt;
&lt;p&gt;Streaming the show in spatial vision was fantastic. Using Sandwich’s Theater app, the virtual screen displayed a smaller rectangle, making the on-stage presenters appear life-sized rather than disproportionately large. This setup created the illusion of sitting in the front row and added to the intimacy of the event. It was a significant improvement over the YouTube replays I’ve watched previously. The combination of stereo video and the knowledge that it was live heightened the experience, making me wish for live sports broadcasts on Vision Pro.&lt;/p&gt;
&lt;p&gt;Although there were some framerate hiccups, the minimal movement on stage meant they didn’t detract from the overall experience much. It’s a minor issue that can be improved.&lt;/p&gt;
&lt;p&gt;The audio seemed to be in stereo rather than surround. While there was plenty of audience interaction, the sound came solely from in front of me. It would have been amazing to hear cheers and applause from behind, enhancing the feeling of being seated in the front row. The video was limited to a rectangle in front of me. Being able to look around and see the audience would have made the experience even more immersive, truly replicating the feeling of attending the event in person.&lt;/p&gt;
&lt;p&gt;Overall, participating in the show was an amazing experience. Kudos to the teams at SpacialGen, Sandwich, and, of course, John Gruber. It sounds like everyone was holding their breath, hoping it would all work out, and it truly did!&lt;/p&gt;
</content:encoded></item><item><title>MagBak RimCase for Model 3 Uberturbine Quick Review</title><link>https://nickbodmer.com/posts/magbak-rimcase-for/</link><guid isPermaLink="true">https://nickbodmer.com/posts/magbak-rimcase-for/</guid><description>I love my Tesla Model 3 Performance, but I have found the wheels incredibly easy to damage against curbs. Ouch I ordered a set of the new RimCase covers for th…</description><pubDate>Tue, 05 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I love my Tesla Model 3 Performance, but I have found the wheels incredibly easy to damage against curbs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://nickbodmer.com/images/posts/magbak-rimcase-for/img-1053.jpeg&quot; alt=&quot;Tesla Ubertine wheel with curb rash&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ouch&lt;/p&gt;
&lt;p&gt;I ordered a set of the new RimCase covers for these wheels, and I have to say I’m pretty impressed. They cover the existing damage perfectly, and unless you examine the wheels closely, I don’t think you can even tell that they are there. I ordered it with a set of “spares”, so if I do some more damage, I can just replace a single section.&lt;/p&gt;
&lt;p&gt;Installation wasn’t difficult, but it was a bit time-consuming. MagBak includes a plastic film that goes on each spoke and around each edge, which took some time to install. I’d say the total install was 10-15 minutes per wheel.&lt;/p&gt;
&lt;p&gt;In the end, I think they look great. I do wonder if there will be any issues when I need to get new tires, I hope I don’t have to remove and reinstall them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://nickbodmer.com/images/posts/magbak-rimcase-for/img-1056.jpeg&quot; alt=&quot;Tesla Model 3 with RimBak installed&quot; /&gt;&lt;/p&gt;
&lt;p&gt;RimBak installed&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://nickbodmer.com/images/posts/magbak-rimcase-for/img-1055.jpeg&quot; alt=&quot;My helper&quot; /&gt;&lt;/p&gt;
&lt;p&gt;My helper&lt;/p&gt;
</content:encoded></item><item><link>https://nickbodmer.com/posts/today-i-was/</link><guid isPermaLink="true">https://nickbodmer.com/posts/today-i-was/</guid><description>Today, I was installing a few outlets that have built-in USB-C ports. I was having trouble identifying the correct breaker, so I had a Zoom meeting between mys…</description><pubDate>Tue, 05 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Today, I was installing a few outlets that have built-in USB-C ports. I was having trouble identifying the correct breaker, so I had a Zoom meeting between myself and this lamp I plugged in.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://nickbodmer.com/images/posts/today-i-was/img-1058.jpeg&quot; alt=&quot;iOS Screenshot of zoom meeting with a lit lamp&quot; /&gt; &lt;img src=&quot;https://nickbodmer.com/images/posts/today-i-was/img-1060.jpeg&quot; alt=&quot;Wall outlet with two USB-C ports in addtiion to the standard AC outlets&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Replacing the battery in the Eufy Spaceview Pro baby monitor</title><link>https://nickbodmer.com/posts/replacing-the-battery/</link><guid isPermaLink="true">https://nickbodmer.com/posts/replacing-the-battery/</guid><description>We really like our Eufy Spaceview Pro baby monitor (Model 8321-M). We’ve had this one for a few years, and the battery life has slowly drained to the point tha…</description><pubDate>Mon, 14 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;div class=&quot;video-embed&quot;&gt;&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/EBr0Q5nnMa8&quot; title=&quot;YouTube video&quot; loading=&quot;lazy&quot; allowfullscreen&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;p&gt;We really like our Eufy Spaceview Pro baby monitor (Model 8321-M). We’ve had this one for a few years, and the battery life has slowly drained to the point that it shuts off immediately when you unplug it.&lt;/p&gt;
&lt;p&gt;About six months ago, I used the trick found on Reddit where you unplug the battery for about 30 seconds and plug it back in. This seemed to give it some additional life, but now the battery is dead. (Not surprising after two years of daily full charge/discharge cycles).&lt;/p&gt;
&lt;p&gt;In this video I open up the monitor, remove the old battery and replace it with a new one.&lt;/p&gt;
&lt;p&gt;Links:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://a.co/d/dOuXoV3&quot;&gt;Amazon Page for the Eufy Spaceview Pro&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://a.co/d/4Ur11kz&quot;&gt;Amazon Page for the battery pack&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.reddit.com/r/techsupport/comments/jzmp83/spaceview_monitor_battery_replacement/&quot;&gt;Reddit Discussion of unplugging the battery for extra life&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>SwiftData errors in Xcode 15 Beta 6: Type &apos;PropertyOptions&apos; has no member &apos;cascade&apos;</title><link>https://nickbodmer.com/posts/swiftdata-errors-in/</link><guid isPermaLink="true">https://nickbodmer.com/posts/swiftdata-errors-in/</guid><description>EDIT: As of XCode Beta 7, you need to add the default value back in. Original Post: I upgraded to Xcode 15 Beta 6, and found some new errors in my SwiftData Pr…</description><pubDate>Thu, 10 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;EDIT: As of XCode Beta 7, you need to add the default value back in.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://nickbodmer.com/images/posts/swiftdata-errors-in/cleanshot-2023-08-22-at-15.54.392x.png&quot; alt=&quot;CleanShot 2023 08 22 at 15 54 39 2x&quot; /&gt; Original Post:&lt;/p&gt;
&lt;p&gt;I upgraded to Xcode 15 Beta 6, and found some new errors in my SwiftData Project.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://nickbodmer.com/images/posts/swiftdata-errors-in/cleanshot-2023-08-10-at-13.13.432x.png&quot; alt=&quot;Xcode error: Type &apos;PropertyOptions&apos; has no member &apos;cascade&apos;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;On my @Relationship macros, using .cascade, I now have the error “Type ‘PropertyOptions’ has no member ‘cascade’”&lt;/p&gt;
&lt;p&gt;We need to add deleteRule: .cascade instead.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://nickbodmer.com/images/posts/swiftdata-errors-in/cleanshot-2023-08-10-at-13.19.452x.png&quot; alt=&quot;CleanShot 2023 08 10 at 13 19 45 2x&quot; /&gt;&lt;/p&gt;
&lt;p&gt;But now we see a new error. “Variable ‘self._$backingData’ used before being initialized”&lt;/p&gt;
&lt;p&gt;We can no longer set default values where we declare the variables. Removing the = [] takes care of the error. (And I did not need to add it to the initializer.)&lt;/p&gt;
&lt;p&gt;Final, working code:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://nickbodmer.com/images/posts/swiftdata-errors-in/cleanshot-2023-08-10-at-13.23.402x.png&quot; alt=&quot;CleanShot 2023 08 10 at 13 23 40 2x&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Deploying your app on-device after becoming a paid Apple Developer</title><link>https://nickbodmer.com/posts/deploying-your-app/</link><guid isPermaLink="true">https://nickbodmer.com/posts/deploying-your-app/</guid><description>I’ve been working on an app, and as a ‘free’ Apple Developer, I’ve just been running my app on-device. This leaves the app on my phone for a few days, but you…</description><pubDate>Sun, 09 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve been working on an app, and as a ‘free’ Apple Developer, I’ve just been running my app on-device. This leaves the app on my phone for a few days, but you are quickly hit with this error:&lt;/p&gt;
&lt;h3&gt;&quot;App Name&quot; is no longer available.&lt;/h3&gt;
&lt;p&gt;Since my app was starting to come along, I decided it was time to pay the $99 fee and become a full Apple Developer. Took a few days after submiting my application, but soon enough I was approved.&lt;/p&gt;
&lt;p&gt;I continued working on my app, and was surprised to quickly run into the same error message. I thought that becoming a paid developer would “automatically” fix this, but there’s a process to deploying your app on a device for testing.&lt;/p&gt;
&lt;p&gt;I found my way to the &lt;a href=&quot;https://developer.apple.com/documentation/xcode/distributing-your-app-to-registered-devices&quot;&gt;Distributing your app to registered devices&lt;/a&gt; guide.&lt;/p&gt;
&lt;p&gt;It breaks down into a few steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Collect Device identifier&lt;/li&gt;
&lt;li&gt;Register device on the Apple Developer website&lt;/li&gt;
&lt;li&gt;Update Provisioning profile&lt;/li&gt;
&lt;li&gt;Archive the app&lt;/li&gt;
&lt;li&gt;Export the app&lt;/li&gt;
&lt;li&gt;Install app on the device&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To make matters easier, some of these steps were already completed.&lt;/p&gt;
&lt;p&gt;My device identifier was already registered (perhaps because the device is tied to my apple id already?)&lt;/p&gt;
&lt;p&gt;No provisioning profile was needed, as by default, you get “automatic signing”.&lt;/p&gt;
&lt;h3&gt;Steps needed&lt;/h3&gt;
&lt;p&gt;Archive and distribute:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Product &amp;gt; Archive&lt;/li&gt;
&lt;li&gt;Distribute App &amp;gt; Ad hoc&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://nickbodmer.com/images/posts/deploying-your-app/f11974a638.png&quot; alt=&quot;CleanShot 2023 07 09 at 20 09 00 2x&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ad hoc distribution options: keep defaults&lt;/li&gt;
&lt;li&gt;Automatically manage signing&lt;/li&gt;
&lt;li&gt;Export to a location on disk.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;When you save the exported app, Xcode creates a folder that contains a few files, including the iOS App file, which is a file with an .ipa filename extension. Distribute that file to your users so they can install it on their devices by using Xcode or Apple Configurator 2.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://nickbodmer.com/images/posts/deploying-your-app/cleanshot-2023-07-09-at-20.22.472x.png&quot; alt=&quot;CleanShot 2023 07 09 at 20 22 47 2x&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Window &amp;gt; Devices and Simulator&lt;/li&gt;
&lt;li&gt;Select your iOS device&lt;/li&gt;
&lt;li&gt;Click the + icon under &quot;installed apps&quot; (even if your app is already listed)&lt;/li&gt;
&lt;li&gt;Select the .ipa file that you exported earlier.&lt;/li&gt;
&lt;li&gt;Done!&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Should I wait for SwiftData?</title><link>https://nickbodmer.com/posts/should-i-wait/</link><guid isPermaLink="true">https://nickbodmer.com/posts/should-i-wait/</guid><description>I’m working on an app idea, and the newest features of iOS 17 seem like they will make my life a lot easier, especially SwiftData. Is it a bad idea to start us…</description><pubDate>Mon, 03 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’m working on an app idea, and the newest features of iOS 17 seem like they will make my life a lot easier, especially SwiftData. Is it a bad idea to start using these features knowing I’ll need to limit the app to iOS 17? I think not, but I’m not sure.&lt;/p&gt;
</content:encoded></item><item><link>https://nickbodmer.com/posts/busy-day/</link><guid isPermaLink="true">https://nickbodmer.com/posts/busy-day/</guid><description>Busy day.</description><pubDate>Sun, 28 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Busy day.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://nickbodmer.com/images/posts/busy-day/a56fbf17a6.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Photochromic Sunglasses: Generic vs Transitions XTRActive</title><link>https://nickbodmer.com/posts/photochromic-sunglasses-generic/</link><guid isPermaLink="true">https://nickbodmer.com/posts/photochromic-sunglasses-generic/</guid><description>I have been buying all my prescription glasses at eyebuydirect.com for years, and about a year ago, I decided to try their “photochromatic” lenses. These are g…</description><pubDate>Wed, 17 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have been buying all my prescription glasses at &lt;a href=&quot;http://eyebuydirect.com&quot;&gt;eyebuydirect.com&lt;/a&gt; for years, and about a year ago, I decided to try their “photochromatic” lenses. These are glasses that are clear indoors, but when you are outside, they darken automatically to become sunglasses.&lt;/p&gt;
&lt;p&gt;Eyebuydirect offers multiple options for photochromic lens upgrades:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;‘Generic’ Photochromic - ~$36&lt;/li&gt;
&lt;li&gt;Transitions Signature - ~$100&lt;/li&gt;
&lt;li&gt;Transitions XTRActive: ~$140&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My first pair was the generic photochromic lenses, and they worked really well. I had only two complaints: They did not get dark in the car (the UV-blocking glass of the car prevents it from changing), and they were slow to ‘transition’ back to clear when you walked indoors after being outside.&lt;/p&gt;
&lt;p&gt;I broke the frame of those glasses and when reordering, decided to give the “Transitions XTRActive” a try. The eyebuydirect website says this about Transitions XTRActive:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Transitions® XTRActive® lenses deliver the best extra darkness and the best extra light protection indoors &amp;amp; outdoors. They’re clear indoors with a hint of protective tint and darken outdoors, even in the car. They also block 100% UVA/UVB and filter harmful blue light indoors &amp;amp; outdoors.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I was really excited to have them work in the car! However, real-life results haven’t been that impressive. They darken a bit in the car, but not to “full sunglasses” darkness. Also, they have a slight tint &lt;em&gt;all the time&lt;/em&gt;. The description sort of points this out, but I didn’t understand that fully until I got them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;My recommendation, skip the Transitions branded stuff; the generic photochromic for $36 bucks is a great deal.&lt;/strong&gt;&lt;/p&gt;
</content:encoded></item><item><title>Which Mac Desktop should I buy?</title><link>https://nickbodmer.com/posts/which-mac-desktop/</link><guid isPermaLink="true">https://nickbodmer.com/posts/which-mac-desktop/</guid><description>For the past few weeks, I’ve been debating between a Mac Mini with M2 Pro or a Mac Studio with M1 Max. I eventually decided on the M2 Pro with 16GB of RAM and…</description><pubDate>Wed, 17 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://nickbodmer.micro.blog/uploads/2023/467e12a571.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;For the past few weeks, I’ve been debating between a Mac Mini with M2 Pro or a Mac Studio with M1 Max. I eventually decided on the M2 Pro with 16GB of RAM and 512GB of storage. It felt like the right balance for me - I know 16GB has been enough on my M1 MacBook Air, and maxing out the CPU performance has been my only issue. However, 512GB of storage is more than enough for me now. I’ll add an external SSD to keep my entire iCloud Photo Library locally.&lt;/p&gt;
&lt;p&gt;I’m confident this Mac Mini with M2 Pro will last me a couple of years, and if the M3 makes a big difference when it comes out, I can always look at the Mac Studio. But for now, this feels like the right decision!&lt;/p&gt;
&lt;p&gt;I decided to buy the base Mac Mini with M2 Pro (16GB, 512GB), after waffling between a spec’d up version and a Mac Studio. Here’s my reasoning:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;16GB has been just fine on my M1 MBA&lt;/li&gt;
&lt;li&gt;Maxing out the CPU has been the only issue I’ve had&lt;/li&gt;
&lt;li&gt;I’m using less than 512TB currently, and switching to a desktop means I can very easily add a 2TB SSD. Finally, I can keep my entire iCloud photo library locally.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now I’ll feel fine for the next year or two, and if the M3 with the process shrink makes a big difference, I can look at a Mac Studio or something.&lt;/p&gt;
&lt;p&gt;When it comes to choosing the best computer for my needs, I recently decided to purchase an Apple Mac Mini with the M2 Pro processor (16GB, 512GB).&lt;/p&gt;
&lt;p&gt;This was not an easy decision to make, as I had to consider my current computing needs as well as my future needs. The choice was ultimately between purchasing the specced-up version of the Mac Mini, or going for a Mac Studio. After a lot of thought, I decided on the Mac Mini. Here’s why:&lt;/p&gt;
&lt;p&gt;Firstly, currently I own an M1 Macbook Air and with 16GB of RAM, I feel that I have enough memory to comfortably work with. While I have experienced some slowdowns and lags at times, it has been due to the processor, not the RAM.&lt;/p&gt;
&lt;p&gt;Secondly, I currently only use 512 GB of storage, meaning that I do not need a bigger storage capacity as I do not store too much data. Once I switch to a desktop computer, I can easily add in a 2TB SSD, and this will be perfectly suitable for me. I love being able to keep my entire iCloud photo library stored locally.&lt;/p&gt;
&lt;p&gt;Finally, I am confident that the Mac Mini M2 Pro will serve me well for the next couple of years. I believe that the M2 Pro is the perfect processor for my needs, and I do not need to worry about upgrading straight away. If in the future the Mac Mini M3 and its process shrink makes a difference, then I can look into buying a Mac Studio or something else.&lt;/p&gt;
&lt;p&gt;All things considered, I am really happy with my decision to go for the Mac Mini M2 Pro. It has all the features I need and I am confident that it will provide me with the computing power I need for the foreseeable future. I am looking forward to seeing the amazing things I can do with the Mac Mini.&lt;/p&gt;
</content:encoded></item><item><title>Synology Replication with ZeroTier</title><link>https://nickbodmer.com/posts/synology-replication-with-zerotier/</link><guid isPermaLink="true">https://nickbodmer.com/posts/synology-replication-with-zerotier/</guid><description>Introduction What I want is to backup my Synology NAS without huge cloud storage costs. In a perfect world, I&apos;d take a 2nd Synology, drop it off at a friend&apos;s…</description><pubDate>Sun, 18 Apr 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;What I want is to backup my Synology NAS without huge cloud storage costs. In a perfect world, I&apos;d take a 2nd Synology, drop it off at a friend&apos;s house, and it would just work. No messing with their network, setting up dynamic dns services, static IP addresses or port forwarding. Just plug it in and turn it on, and it works. And it&apos;s possible using a service called ZeroTier. Using ZeroTier, the two devices will appear to be on the same local network, no matter where they are in the world.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/NB5CQcw1qmg&quot;&gt;Watch on YouTube&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Instructions&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Primary NAS uses a static IP on local network&lt;/li&gt;
&lt;li&gt;Remote NAS set to DHCP&lt;/li&gt;
&lt;li&gt;Create Zero Tier Account&lt;/li&gt;
&lt;li&gt;Create Network&lt;/li&gt;
&lt;li&gt;Download correct ZeroTier package for your synology models
&lt;ul&gt;
&lt;li&gt;Architecture per Synology model: &lt;a href=&quot;https://github.com/SynoCommunity/spksrc/wiki/Architecture-per-Synology-model&quot;&gt;https://github.com/SynoCommunity/spksrc/wiki/Architecture-per-Synology-model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Zerotier Synology download: &lt;a href=&quot;https://download.zerotier.com/dist/synology/&quot;&gt;https://download.zerotier.com/dist/synology/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;On Primary NAS
&lt;ul&gt;
&lt;li&gt;Package center &amp;gt; manual install &amp;gt; select ZeroTier package&lt;/li&gt;
&lt;li&gt;Launch ZeroTier, add network ID&lt;/li&gt;
&lt;li&gt;Approve device from ZeroTier dashboard&lt;/li&gt;
&lt;li&gt;Label Device Name&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Repeat for Remote NAS&lt;/li&gt;
&lt;li&gt;On Primary and Remote NAS
&lt;ul&gt;
&lt;li&gt;Package manager &amp;gt; search snapshot &amp;gt; install &quot;snapshot replication&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;On Primary NAS
&lt;ul&gt;
&lt;li&gt;Open snapshot replication&lt;/li&gt;
&lt;li&gt;Replication &amp;gt; create&lt;/li&gt;
&lt;li&gt;Select remote, next&lt;/li&gt;
&lt;li&gt;Server name: use zerotier IP of Secondary NAS&lt;/li&gt;
&lt;li&gt;Advanced Settings
&lt;ul&gt;
&lt;li&gt;Source Server&lt;/li&gt;
&lt;li&gt;Server name or IP address drop down, select zerotier IP, click OK&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Enter Secondary NAS username / password, click next&lt;/li&gt;
&lt;li&gt;Select destination volume&lt;/li&gt;
&lt;li&gt;Select share to replicate&lt;/li&gt;
&lt;li&gt;Send initial copy over the network&lt;/li&gt;
&lt;li&gt;Select replication schedule&lt;/li&gt;
&lt;li&gt;Set retention schedule&lt;/li&gt;
&lt;li&gt;Apply&lt;/li&gt;
&lt;li&gt;Done!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/SynoCommunity/spksrc/wiki/Architecture-per-Synology-model&quot;&gt;https://github.com/SynoCommunity/spksrc/wiki/Architecture-per-Synology-model&lt;/a&gt;
&lt;a href=&quot;https://download.zerotier.com/dist/synology/&quot;&gt;https://download.zerotier.com/dist/synology/&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>iRacing</title><link>https://nickbodmer.com/posts/iracing/</link><guid isPermaLink="true">https://nickbodmer.com/posts/iracing/</guid><description>I&apos;ve recently fallen in love with a racing simulator called iRacing. I already had a wheel and pedal tucked away in a closet, so I pulled them out and set up i…</description><pubDate>Sat, 09 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve recently fallen in love with a racing simulator called &lt;a href=&quot;https://www.iracing.com/&quot;&gt;iRacing&lt;/a&gt;. I already had a wheel and pedal tucked away in a closet, so I pulled them out and set up iRacing with my Oculus Rift. What a blast! I love the competitive nature and the ability to work to slowly raise my skill level.&lt;/p&gt;
&lt;p&gt;I quickly found that I didn&apos;t like having to attach and detach the racing wheel to my desk, so I picked up the &lt;a href=&quot;https://www.playseatstore.com/playseat-challenge.html&quot;&gt;Playseat Challenge&lt;/a&gt;, which is a folding chair designed just for racing. It makes it very simple to setup for racing and tear down when I&apos;m done.&lt;/p&gt;
&lt;p&gt;Here&apos;s a quick video I put together after one of my Rookie Mazda Global Cup races.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/eRLykE1qtLc&quot;&gt;Watch on YouTube&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Install youtube-dl with homebrew for Mac</title><link>https://nickbodmer.com/posts/install-youtube-dl-with-homebrew/</link><guid isPermaLink="true">https://nickbodmer.com/posts/install-youtube-dl-with-homebrew/</guid><description>Here is a short tutorial video on how to install youtube-dl via homebrew for Mac.</description><pubDate>Mon, 23 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Here is a short tutorial video on how to install youtube-dl via homebrew for Mac.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/NhOkYXB2_QQ&quot;&gt;Watch on YouTube&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Setting up a website with Ansible</title><link>https://nickbodmer.com/posts/setting-up-a-website-with-ansible/</link><guid isPermaLink="true">https://nickbodmer.com/posts/setting-up-a-website-with-ansible/</guid><description>Create VPS Create a new VPS (virtual private server), I chose to host my VPS on DigitalOcean. I&apos;ve already uploaded my Mac&apos;s public key to DigitalOcean, so it…</description><pubDate>Sun, 15 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Create VPS&lt;/h2&gt;
&lt;p&gt;Create a new VPS (virtual private server), I chose to host my VPS on &lt;a href=&quot;https://www.digitalocean.com&quot;&gt;DigitalOcean&lt;/a&gt;. I&apos;ve already uploaded my Mac&apos;s public key to DigitalOcean, so it is installed automatically. This will allow ansible to connect without a password.&lt;/p&gt;
&lt;p&gt;The ansible and config files I use are at the bottom of this post.&lt;/p&gt;
&lt;h2&gt;Initial config&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;ansible-playbook initial_server_setup.yml -i hosts -l nickbodmer.com -u root
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This initial configuration playbook will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;create &apos;wheel&apos; group (which probably already exists)&lt;/li&gt;
&lt;li&gt;enable passwordless sudo&lt;/li&gt;
&lt;li&gt;create my user &apos;nick&apos; with sudo privileges&lt;/li&gt;
&lt;li&gt;add my ssh key to that new user&lt;/li&gt;
&lt;li&gt;disable password authentication for root&lt;/li&gt;
&lt;li&gt;update apt&lt;/li&gt;
&lt;li&gt;install a few packages, most importantly UFW firewall&lt;/li&gt;
&lt;li&gt;allow SSH through the firewall&lt;/li&gt;
&lt;li&gt;enable the firewall&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;nginx setup&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;ansible-playbook -i hosts -l nickbodmer.com nginx.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This playbook will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install and start nginx&lt;/li&gt;
&lt;li&gt;open ports 80 and 443 for http and https&lt;/li&gt;
&lt;li&gt;copy the nginx config file to the server&lt;/li&gt;
&lt;li&gt;symlink the config file to sites-enabled&lt;/li&gt;
&lt;li&gt;restart nginx&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this point we have a fully running http webserver.&lt;/p&gt;
&lt;h2&gt;letsencrypt using certbot&lt;/h2&gt;
&lt;p&gt;certbot will automatically configure nginx to use https and set a cron job to automatically renew your certificates.&lt;/p&gt;
&lt;p&gt;Add certbot repository:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo add-apt-repository ppa:certbot/certbot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install certbot for nginx:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install python-certbot-nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install cert for domain:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo certbot --nginx -d nickbodmer.com -d www.nickbodmer.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Files needed&lt;/h2&gt;
&lt;p&gt;hosts&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[cloud]
nickbodmer.com

[cloud:vars]
ansible_python_interpreter=/usr/bin/python3

[local]
192.168.1.206
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;initial_server_setup.yml&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;###########################################################################################################
# DO Community Playbooks
# Playbook: Initial Server Setup
# Based on: https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04
# Dedicated Guide: https://www.digitalocean.com/community/tutorials/automating-initial-server-setup-with-ansible
###################################################################################################################

---
- hosts: all
  remote_user: root
  gather_facts: false
  vars:
    create_user: nick
    copy_local_key: &quot;{{ lookup(&apos;file&apos;, lookup(&apos;env&apos;,&apos;HOME&apos;) + &apos;/.ssh/id_rsa.pub&apos;) }}&quot;
    sys_packages: [ &apos;curl&apos;, &apos;vim&apos;, &apos;git&apos;, &apos;ufw&apos;]

  tasks:
    - name: Make sure we have a &apos;wheel&apos; group
      group:
        name: wheel
        state: present

    - name: Allow &apos;wheel&apos; group to have passwordless sudo
      lineinfile:
        path: /etc/sudoers
        state: present
        regexp: &apos;^%wheel&apos;
        line: &apos;%wheel ALL=(ALL) NOPASSWD: ALL&apos;
        validate: &apos;/usr/sbin/visudo -cf %s&apos;

    - name: Create a new regular user with sudo privileges
      user:
        name: &quot;{{ create_user }}&quot;
        state: present
        groups: wheel
        append: true
        create_home: true
        shell: /bin/bash

    - name: Set authorized key for remote user
      authorized_key:
        user: &quot;{{ create_user }}&quot;
        state: present
        key: &quot;{{ copy_local_key }}&quot;

    - name: Disable password authentication for root
      lineinfile:
        path: /etc/ssh/sshd_config
        state: present
        regexp: &apos;^#?PermitRootLogin&apos;
        line: &apos;PermitRootLogin prohibit-password&apos;

    - name: Update apt
      apt: update_cache=yes

    - name: Install required system packages
      apt: name={{ sys_packages }} state=latest

    - name: UFW - Allow SSH connections
      ufw:
        rule: allow
        name: OpenSSH

    - name: UFW - Deny all other incoming traffic by default
      ufw:
        state: enabled
        policy: deny
        direction: incoming
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;nginx_config.cfg&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server {
       listen 80;
       listen [::]:80;

       server_name nickbodmer.com www.nickbodmer.com;

       root /home/nick/nickbodmer.com/;
       index index.html;

       location / {
               try_files $uri $uri/ =404;
       }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;nginx.yml&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
- hosts: all
  tasks:
    - name: ensure nginx is at the latest version
      apt: name=nginx state=latest
      become: yes
    - name: start nginx
      service:
          name: nginx
          state: started
      become: yes
    - name: UFW - Allow http/https connections
      ufw:
        rule: allow
        name: &apos;Nginx Full&apos;
      become: yes
    - name: copy the nginx config file and restart nginx
      copy:
        src: /Users/nick/Google Drive/Sync/Documents/Ansible/nginx_config.cfg
        dest: /etc/nginx/sites-available/nginx_config.cfg
      become: yes
    - name: create symlink
      file:
        src: /etc/nginx/sites-available/nginx_config.cfg
        dest: /etc/nginx/sites-enabled/default
        state: link
      become: yes
    - name: restart nginx
      service:
        name: nginx
        state: restarted
      become: yes
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><link>https://nickbodmer.com/posts/unboxed-a-new/</link><guid isPermaLink="true">https://nickbodmer.com/posts/unboxed-a-new/</guid><description>Unboxed a new Benq 4K HDR monitor!</description><pubDate>Thu, 19 Apr 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Unboxed a new Benq 4K HDR monitor!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://nickbodmer.com/images/posts/unboxed-a-new/153b1a3d3e.jpg&quot; alt=&quot;BenQ 4K HDR monitor, fresh out of the box&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>How to Shuck the WD RED drive from an Easystore</title><link>https://nickbodmer.com/posts/shucking-wd-easystore-drives/</link><guid isPermaLink="true">https://nickbodmer.com/posts/shucking-wd-easystore-drives/</guid><description>A tutorial video I created on how to remove the internal hard drives from the Western Digital Easystore external hard drive. These are often found for a much l…</description><pubDate>Mon, 07 Aug 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A tutorial video I created on how to remove the internal hard drives from the Western Digital Easystore external hard drive. These are often found for a much lower price than you would pay for the internal drive by itself.&lt;/p&gt;
&lt;p&gt;I have four of these drives in my Synology NAS, and they work great.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/b6VCQ64DkfM&quot;&gt;Watch on YouTube&lt;/a&gt;&lt;/p&gt;
</content:encoded></item></channel></rss>