blog.iankulin.com

Possibly-Useful

Quick & Dirty auth with nginx & Node

I walk through protecting a Node/Express app with nginx basic auth, aimed at simple utilities on public servers: firewall off the app’s port so only nginx can reach it, configure nginx to proxy requests and require htpasswd credentials, and pass the authenticated username to the app via a request header. I also cover the approach’s limitations, such as plaintext passwords without SSL, no logout mechanism, and no brute-force protection...

User Sessions & Cookies in Node

In this tutorial, I build a user login and session management system from scratch in Node and Express, starting with a simple cookie-based session counter and progressively adding file-based persistence, user accounts, logout, and password authentication using bcrypt. I also cover input sanitisation, secure cookies, and enforcing HTTPS behind an NGINX proxy. The tutorial is aimed at readers with basic familiarity with Node and Express...

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...

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...

Displaying markdown as HTML

This is a write-up of a small weekend project of mine: a Node.js/Express server that serves markdown files from a directory as HTML, using middleware to intercept requests for .md files and convert them with the Showdown library. I added a simple string-replacement template so the output is well-formed HTML with a proper page title. The code is on GitHub, with hardening and a possible Docker containerization left as future work...

Solved DNS Issues - Proxmox, LXC, Ubuntu, Tailscale

While setting up an Ubuntu 20.04 LXC on Proxmox to run a TP-Link Omada controller, I found DNS resolution failing because Proxmox copies the host’s Tailscale-based resolv.conf into containers, and the container has no route to the Tailscale DNS address. In this post, I cover the diagnosis of why Debian templates behaved differently, plus two fixes: specifying DNS explicitly in the Proxmox GUI, or creating a .pve-ignore.resolv.conf file so Proxmox stops overwriting the container’s settings...

Ansible with Secrets

This is a follow-up to my earlier Ansible playbook post, covering how to handle different sudo passwords across servers. I show how per-host inventory variables work, explain why storing ssh passwords in plaintext is risky, and walk through moving credentials into an external vars file before encrypting it with Ansible Vault, including the create, edit, and ask-vault-pass commands...

nginx in Front of a node.js app

I configure NGINX on a VPS to serve static files while forwarding /api routes to a Node.js app running on localhost. I cover a server block example with proxy_pass and the Host header, the conf.d include convention, and restarting or validating the config. This post is part of a series on my weather API, but it is self-contained...

HDD Swap on A1278 MacBook Pro

A short walkthrough of replacing my dead hard drive with a 2.5-inch SSD in my 2012 Intel MacBook Pro that had sat in a drawer for three years. I cover removing the bottom cover, disconnecting the SATA plug, transferring the mounting lugs with a torx driver, and reassembling, along with a few practical notes like the angled case screws and a packing-tape fix for the pull tab...

HP EliteDesk 800 G2 Memory Upgrade

I walk through upgrading the RAM in my HP EliteDesk 800 G2 Desktop Mini from 16GB to 32GB. I cover the tool-free disassembly, including the captive screw, sliding case lid, and tilting the fan aside to reach the SODIMMs, along with the RAM specs required (1.2V DDR4-2133MHz, PC4-17000) and the SK Hynix modules I used...

Problems mounting network share at boot

I move Jellyfin from a Proxmox LXC container to a VM because Tailscale wouldn’t run in the container, but the fstab entry mounting a NAS media share over CIFS no longer mounts at boot, even though mount -a works. The cause is the network not being up when the mount is attempted, and the fix is adding noauto, x-systemd.automount, and _netdev to the mount options so systemd waits for the network and mounts on demand...

Accessing a Synology NAS from Linux

I set up a secondhand Synology DS216j NAS as homelab storage with RAID 1 and a Samba share, intended for VM backups and eventually a media library. I then mount the share on a privileged Debian LXC container in Proxmox via a cifs entry in /etc/fstab, with a breakdown of the mount options and a note that the setup persists across reboots...

External USB Drives in Linux

A walkthrough of how I manually mount and unmount USB drives on Linux systems that don’t auto-mount them, such as servers. Using a Proxmox host as an example, I show how to identify a plugged-in drive with lsblk, mount a partition to a directory under /media, and safely unmount it with umount before removal...

ssh key login on VPS

I walk through moving a server from password-based SSH logins to key-based authentication as a defence against brute force attacks. I cover generating an SSH key pair on a Mac, installing the public key on the target machine with ssh-copy-id, and disabling password authentication on Ubuntu 22 by adding PasswordAuthentication no to a config file in sshd_config.d and reloading the ssh daemon. I also note console login still works if something goes wrong...

Functions in JavaScript

I give a tutorial-style explanation of functions in JavaScript, covering scope and pass-by-value arguments, nested functions, and my preference for passing values in explicitly rather than reaching for outer-scope variables. I then cover functions as first-class citizens, including anonymous and arrow function syntax, passing and returning functions, and using map to apply a function to array elements...

HTML 001

I give a beginner-level introduction to HTML covering what markup tags are, paired versus unpaired tags, and why HTML is semantic rather than about display. I explain the basic document structure with html, head, and body tags, how tags nest, and how to add an image with the img tag, building up a simple example page along the way...

Sharing is caring

Continuing my SwiftUI demo project, I use ShareLink to let users share a view rendered as an image via the standard share sheet, with SharePreview supplying the thumbnail and title. I also cover adding the Photo Library Additions usage description so a Save Image option appears, along with the permission prompt users see on first use...

ImageRenderer()

A short SwiftUI example showing how ImageRenderer converts a view into a UIImage or CGImage. I build a simple behaviour ticket view, use a Save button to render it to an image, and display the saved result behind a toggle...