Introducing Hubro
Hubro is a personal exploration into learning more Go, experimenting with HTMX and Alpine.js, and setting up a simple build pipeline with TailwindCSS and esbuild in a Go project. It also doubles as a replacement for a blog previously generated by Jekyll, retaining (mostly) compatible Markdown-based blog posts.
What Is Hubro?
Hubro sits in a middle ground between a static site generator and a traditional database-backed blog engine:
- It reads Markdown files from a
blog
directory and renders them using Go templates. - It also reads Markdown files from a
pages
directory for static pages, such as an About or Contact page. - All files are read into memory when Hubro starts, and any changes on disk are automatically updated.
- Thanks to Go’s simplicity and performance, Hubro is designed to be both minimal and efficient—perfect for personal blogs and small websites.
Why Hubro?
-
Learn Go Hubro serves as a personal learning experience, a hands-on approach to deepen Go skills, especially around building performant web applications and file handling.
-
Try Out HTMX & Alpine.js The project incorporates HTMX and Alpine.js to enhance interactivity and responsiveness in the browser, without having to resort to a full-blown JavaScript framework.
-
Simple Build Pipeline A main goal was to set up a minimal build pipeline with TailwindCSS and esbuild. By coupling these tools with Go, you can have a highly efficient workflow that keeps dependencies light and code organized.
-
Jekyll Compatibility Previous blog posts were created with the Jekyll static site generator. Hubro retains (mostly) the same Markdown structure, allowing old posts to be dropped in without heavy modification.
Check It Out
Hubro is open-source and available on GitHub. Feel free to clone the repository and try it out for yourself!
Disable snap refresh on metered connections
In the previous post, I mentioned that I kept an eye on the network traffic on my LTE connection on my laptop. I've been bitten hard (It updated IntelliJ IDEA, several hundred megabytes) by snap's autorefresh feature, which have seemed hard/impossible to disable. There's been config options for only allowing updates at certain times etc., but the feature I've been crying for, to be able to disable it when on a metered connection, has been missing in action. Until now!
$ snap set system refresh.metered=hold
Easy as that!
(Found it here - otherwise not much info about this around the web) UPDATE 2020-02-11: Link is dead, but this is in the official documentation here
Seems quite recent, introduced in snapd 2.33, which was released on 2018-06-25.
So how does it work? How do I confirm that it's actually working? No feedback when running the command, nothing in syslog.
Hmm.
Only one thing to do; use the source, Luke!
From the source, I can see that it reads the DBus property org.freedesktop.NetworkManager.Metered
for a value, and decides what to do based on that.
const (
// https://developer.gnome.org/NetworkManager/stable/nm-dbus-types.html#NMMetered
NetworkManagerMeteredUnknown = 0
NetworkManagerMeteredYes = 1
NetworkManagerMeteredNo = 2
NetworkManagerMeteredGuessYes = 3
NetworkManagerMeteredGuessNo = 4
)
If it's a NetworkManagerMeteredYes
or NetworkManagerMeteredGuessYes
, it will disable autorefresh.
I then read the DBus property with the tool D-Feet, and confirmed that the value returned was '3' when on LTE, and '4' on Wi-fi. Wow, this actually seems to work!
While I would prefer more information about this (regular users will NEVER find it, and it is default off), and more logging, I'll take this for now!
Network stats without ifconfig
Sometimes I get curious about how much data a network interface has transferred. Actually, I get curious quite frequently after I got an LTE modem for my laptop.
Usually, I just use ifconfig:
$ ifconfig wlp3s0
wlp3s0 Link encap:Ethernet HWaddr 00:15:00:7e:af:bd
inet addr:192.168.0.165 Bcast:192.168.0.255 Mask:255.255.255.0
inet6 addr: fe80::4c15:ef76:132e:768/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:20922893 errors:0 dropped:0 overruns:0 frame:0
TX packets:20916831 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:15370202859 (15.3 GB) TX bytes:13050802316 (13.0 GB)
With Ubuntu 18.04, however, there is no ifconfig. Yes, I know I can install it, but I wanted to learn how I should be doing it.
I came up with this, using the 'ip' command:
$ ip -s -h addr show wlp3s0
3: wlp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:15:00:7e:af:bd brd ff:ff:ff:ff:ff:ff
inet 192.168.0.165/24 brd 192.168.0.255 scope global dynamic wlp3s0
valid_lft 5439sec preferred_lft 5439sec
inet6 fe80::4c15:ef76:132e:768/64 scope link
valid_lft forever preferred_lft forever
RX: bytes packets errors dropped overrun mcast
15.4G 20.9M 0 0 0 0
TX: bytes packets errors dropped carrier collsns
13.1G 20.9M 0 0 0 0
Meh. This is gonna take some time to get used to.
LXD - Failed container creation - No root device could be found
After upgrading LXD from 2.0.x to 3.2, with everything at first looking hunky dory, I discovered after a few days that I couldn't create new containers any more!
$ lxc launch ubuntu:bionic testcontainer
Error: Failed container creation: No root device could be found
WTF!
On another host which received the same upgrade, however, everything still worked fine. After checking if there were any differences in the configuration, I found something missing in the default profile, in the devices section.
On the host where everything worked:
devices:
eth0:
name: eth0
nictype: bridged
parent: br0
type: nic
root:
path: /
pool: default
type: disk
On the one which DIDN'T work:
devices:
eth0:
name: eth0
nictype: bridged
parent: br0
type: nic
AHA! The entire 'root' section is gone! Why didn't it get added in the first place? I don't know.
Adding it was easy though, just did "lxc profile edit default" and pasted it in, and everything works fine.
Weird stuff.
Migrate LXD (2.0.x) ZFS storage pool to new ZFS pool
I've done this a few times now, and when you know how to do it, it's a breeze.
Posting the "recipe" here, for my self and anyone who might need it.
Should be pretty self explanatory for those with a little experience with ZFS.
Obviously, after starting LXD after migrating, you should test if everything is OK before destroying the old dataset.
(you will need the packages sqlite3 and mbuffer, mbuffer can be omitted at the cost of some speed)
zpool create -f -o ashift=12 newpool ata-Samsung_SSD_860_EVO_250GB_S3YJNB0K581614J
systemctl stop lxd.service lxd.socket
zfs snapshot -r oldpool/lxd@migrate
zfs send -R oldpool/lxd@migrate | mbuffer -q -s 128k -m 1G | zfs receive -vF newpool/lxd
zfs list | grep oldpool/lxd | awk '{print $1}' | while read line; do zfs set mountpoint=none $line; done
zfs list -r newpool/lxd -t snapshot | grep '@migrate' | awk '{print $1}' | while read line; do zfs destroy $line; done
sqlite3 /var/lib/lxd/lxd.db "UPDATE config SET value='newpool/lxd' WHERE value='oldpool/lxd';"
systemctl start lxd.socket lxd.service
zfs destroy -r oldpool/lxd
Ubuntu 18.04 ZFS on root NetworkManager gotcha
When installing Ubuntu with root on ZFS (following this great guide), I soon discovered that something was amiss with the networking. Wi-fi worked fine, but my mobile broadband failed to connect, and NetworkManager didn't respond when I plugged in an ethernet cable.
After literally HOURS of troubleshooting (beginning in the wrong end with the mobile broadband), I stumbled across this bug report. Sounds familiar, I installed the ZFS root with a chroot (debootstrap). Thankfully, the comments have the solution.
Create an empty file, and restart stuff.
$ sudo touch /etc/NetworkManager/conf.d/10-globally-managed-devices.conf
$Â sudo systemctl restart NetworkManager ModemManager
VoilĂ ! Problem solved.
Tomatoes!
After about seven months of waiting, I can soon enjoy tomatoes from the plant I planted from seed in February!
Tomatoes!
Simplifying port forwarding with LXD
LXD containers are brilliant, but lacks an easy way to forward ports from the containers to the host. One can use iptables manually, of course, but I really missed something easy like Docker. To try and remedy this, I have conjured up a little bash script. With this script, you can add, delete and list port forwarding rules.
It's a bit rough around the edges, but maybe I'll tidy it up a bit some day.
Read on for the script and examples.