blog.iankulin.com

Posts

What's unfinished in your Udemy?

A rundown of the tech courses and tutorials I started but never finished, including Udemy purchases, 100 Days of SwiftUI, MIT’s Missing Semester lectures, and Stanford’s CS193p, with brief notes on why each was abandoned. I argue that quitting a course in favor of building real projects is usually a good trade, since courses are mainly valuable for discovering tools and concepts you didn’t know you needed...

Copying Objects in JS

I take a look at copying objects in JavaScript for React state updates, covering why simple assignment fails, how the spread operator creates copies, and how to selectively replace properties during a copy. I also show why shallow copies fall short with nested data, demonstrating how to manually spread nested arrays for add and remove operations, and end by noting how the approach gets messy at deeper nesting levels...

CSS for React Components

I compare approaches to styling React components, including global stylesheets, inline style objects, CSS and component libraries, per-component CSS files, CSS modules, and styled-components. I demonstrate each on a simple card component, noting trade-offs such as name clashes, hover effects, and unused CSS accumulating over time. I find styled-components the most elegant but lean toward per-component CSS files to avoid extra dependencies...

React - a To Do Example

My To Do app gets rebuilt in React, continuing a series that has already covered vanilla JavaScript and htmx versions backed by the same REST API. I cover React’s core ideas—components, declarative UI, and the virtual DOM—along with Vite build tooling, and walk through the app’s components, useState-based state management, and passing data via props. React’s tooling complexity and 150K of shipped JavaScript make it overkill at this scale, but I plan further React work, possibly with TypeScript...

htmx - A To Do Example

An introduction to htmx and the hypermedia and HATEOAS concepts behind it, followed by a conversion of a simple todo app from a Node/Express setup that serves JSON and renders client-side in JavaScript to an htmx version where the server returns HTML fragments instead. I compare the two implementations and discuss when htmx makes sense, particularly for server-side developers who want to avoid writing frontend JavaScript...

Testing Node.js apps - Mocha, Chai, and Supertest

I walk through testing a Node/Express API with mocha, chai, and supertest, using a simple maths-endpoint app as the example. I cover installing the tools as dev dependencies, exporting the app so tests can control it, writing test suites and cases, running tests via an npm script, and the role each tool plays. I had been using Bruno for lightweight endpoint checks and found the full setup trivial to configure...

Simple SQLite in Express

In this walkthrough, I build a minimal Node/Express REST API backed by SQLite as a learning exercise for migrating an app away from Mongoose. I cover query strings versus request bodies, CRUD endpoints for a users table, and rewriting string-interpolated SQL as parameterized queries to prevent injection. I close with a table of REST conventions for HTTP methods and a link to the finished project on GitHub...

Using LXC templates in Proxmox

A guide to automating the setup of new self-hosted services on Proxmox by converting a fully configured LXC container into a reusable template. I cover preparing the base container with standard software such as Docker and Tailscale, addressing clone-specific issues like host names, machine IDs, and SSH host keys, then cloning the template to spin up new containers quickly...

Practice your restore strategy

My routine quarterly disaster-recovery test in my Proxmox homelab—shutting down the production node and restoring its backups onto a second machine—turned up a few minor issues: the backup node ran out of disk space, was still on an old Proxmox version, and was missing a config fix for backing up LXCs to NFS. The exercise also prompted me to reflect on sticking with Proxmox, Synology’s reliability, and the Uptime Kuma and ntfy monitoring that confirmed services came back up...

Gogs, Gitea, Forgejo

A security vulnerability announced for Forgejo, which also affects Gitea and Gogs, prompted me to look into the fork history behind my self-hosted Gogs setup: Gitea split from Gogs in 2016 over project management disagreements, and Forgejo forked from Gitea in 2022 after its trademarks moved to a company. Impressed by Forgejo’s handling of the issue and only lightly invested in Gogs, I’m considering a switch. The takeaway is that project governance matters alongside features, and security announcements for the tools you rely on are worth following...

Git - pushing to two remotes

A guide to pushing a single git repository to two remotes, prompted by my running a private Gogs instance alongside GitHub. I cover naming remotes meaningfully rather than relying on the convention of origin, pushing to each remote explicitly, and using the -u flag to set a default remote, which becomes the source of truth for both pushing and pulling. My typical setup treats Gogs as the source of truth, with manual pushes to GitHub when code needs to be shared...

Concurrency and channels in Go

I offer a beginner-oriented walkthrough of Go’s concurrency basics, built around a small demo program with a worker function that sleeps and reports. I introduce goroutines, then channels for passing values between the worker and the main program, explain why plain channel reads block, and show how a select statement with a default case enables non-blocking checks, finishing with closing the channel to release resources. The sample code is available on GitHub...

Date formatting in Go is quirky

I ask ChatGPT for a Go date-formatting example and assume its answer of Format(“02012006”) is a hallucination, until discovering it was actually correct: Go bases its date format strings on the reference time 1/2 3:04:05 2006, with each number mapping to month, day, hour, minute, second, and year...

Gogs - your own tiny GitHub

I wanted a private git server for version-controlling my plain-text financial accounts (kept with beancount), rather than pushing them to GitHub. I walk through installing Gogs — chosen over Gitea for being simpler — in Docker on an LXC container, including a storage driver fix needed to get it running, and finish with the first-run setup and pushing a repo...

New Self-Hosted Service Workflow

A walkthrough of my workflow for adding a new self-hosted service to my Proxmox homelab, using audiobookshelf—set up after abandoning Audible—as the worked example. It covers running the service as a Docker container inside an unprivileged Debian LXC, mounting NAS storage via fstab, Tailscale access, Ansible automation, monitoring with Uptime Kuma, and Proxmox backups...

Ansible - Importing a Playbook

I look at structuring Ansible playbooks, starting with the distinction between plays and tasks, then covering how to import one playbook into another with import_playbook. I include a note on where imports can be placed — at the top level of the YAML, not inside a play...

ViewTube

ViewTube is a self-hosted YouTube front end that strips out ads and tracking, with local accounts for subscribing to channels and keeping video progress. I got it running in a VM with Docker Compose in about five minutes, along with a workaround of using MongoDB 4.4 because newer versions require AVX CPU support my VM didn’t expose...

Adding Front Matter To mdserver

The mdserver project needed page titles defined as YAML front matter inside markdown files, in the style of Jekyll and Hugo, instead of being generated from the file name. Rather than writing custom extraction code, I use Showdown’s built-in metadata support, enabled with a converter flag, and confirm the front matter leaves the HTML output unchanged...

Building Docker images for multiple architectures

I look at replacing a shell script that built and pushed separate Docker images for amd64 and arm64 with docker buildx, which produces a single multi-platform image of my mdserver app. My attempt to also target 32-bit ARM for the Raspberry Pi runs extremely slowly because buildx emulates foreign architectures with QEMU, prompting me to consider offloading builds to GitHub Actions...

Docker volume backup is more complicated than it should be

I explain how to back up and migrate a Docker named volume between hosts using the official method: stopping the container, then running a temporary container that mounts the volume and tars its data to a file. I work through an example with Uptime Kuma that covers moving the data to a new VM and restoring it into a fresh install, including a gotcha where restoring to the wrong path leaves the data nested and ignored. I end by questioning why simply copying the volume’s files from the local filesystem isn’t recommended...