Hacks, 2025-09-06
Sometimes I write hacks. Here’s a place for them, along with commentary. This is the September 6, 2025 edition.
- Real working Quadlets I use in production
- Stop a headless laptop-server from sleeping with the lid closed (systemd)
- Getting DuckDNS to do only IPv6
- A Gentoo env file that got 32-bit x86 FFmpeg building with glibc/Clang/ThinLTO
- Manual full → tv levels mapping (in case FFmpeg doesn’t like you)
Why?
I feel like I write a lot of useful but difficult-to-place snippets, and I don’t feel very good about padding out my blog with single snippets. It just doesn’t feel right to me. I know some people make “experimental” repos, but it feels, in some way, Too Formal to put it in Git, rather than just dumping them to my blog.
Real working Quadlets I use in production
There are so few in-the-wild Quadlets out there on the Web, and what few are available are poorly-documented. Let me provide two worked examples.
bgutil Proof-of-Origin Token server (for yt-dlp reasons)
Full details are at yt-dlp’s wiki, but the short of it is that YouTube now sometimes wants PO tokens to make your traffic look less bot-like.
[Unit]
Description=yt-dlp POT provider
[Container]
# Fully-qualified to allow AutoUpdate=registry to work.
Image=docker.io/brainicism/bgutil-ytdlp-pot-provider
AutoUpdate=registry
RunInit=true
# This might not be common knowledge;
# podman-run(1) has the details,
# but you can restrict the IP the container binds to.
# This one means
# "bind only to localhost on port 4416 inside and outside the container".
PublishPort=127.0.0.1:4416:4416
[Install]
WantedBy=default.target
Raspberry Flavoured Minecraft server
While this one is for Raspberry Flavoured, I expect it to be generalizable. Refer to the docs for more env vars.
[Unit]
Description=Raspberry Flavoured Minecraft server
# There's a good chance the service will fail on boot without this;
# Java servers don't seem to be able to cope with "oops, no network!".
After=network-online.service
[Container]
# Fully-qualified to allow AutoUpdate=registry to work.
Image=docker.io/itzg/minecraft-server:latest
AutoUpdate=registry
Environment=EULA=TRUE
# I'm told this is a reasonable starting point.
Environment=MEMORY=4096M
Environment=SIMULATION_DISTANCE=16
Environment=VIEW_DISTANCE=16
# You *must* match these to the modpack in use,
# otherwise it'll default to vanilla, latest.
Environment=TYPE=FORGE
Environment=VERSION=1.19.2
Environment=FORGE_VERSION=43.4.12
# CurseForge requires you to have an API key,
# which is annoying.
# I believe PackWiz and Modrinith are easiest to work with.
# See https://docker-minecraft-server.readthedocs.io/en/latest/mods-and-plugins/
# for details.
Environment=PACKWIZ_URL=https://asphodel.cc/packwiz/Ports/Curse/Raspberry-Server/pack.toml
# I admit no knowledge about the quality of these flags...
Environment=USE_MEOWICE_FLAGS=true
# ...but I can reasonably vouch for lz4.
# zlib (the default) is obsolete.
# It was made for systems with
# *far* less memory than the systems of today,
# and is thus limited by that constraint
# in other, more currently relevant aspects,
# such as compression ratio and compression/decompression speed.
Environment=REGION_FILE_COMPRESSION=lz4
# Change this if you want, my setup is exposed over public IPv6,
# so the application itself needs to be secured in some way.
# An auth mod would do you fine here, but this one's built-in.
# Make sure to write an ops.json and whitelist.json,
# or you'll be locked out of your own server!
Environment=ENABLE_WHITELIST=true
# You'll need this to actually make the server accessible.
# Change these if you run on a different port.
PublishPort=25565:25565
# This is more-or-less arbitrary, right, but
# it's preferable to keep it consistent.
# My layout is as follows:
# /srv/minecraft/<packname> is where a specific server's data folder is,
# /var/minecraft-backups is for backups.
#
# You might need to add :z or :Z to the end of this for SELinux systems.
# I didn't do my testing with an SELinux-enabled system.
Volume=/srv/minecraft/raspberry:/data
[Service]
# I'm setting OnFailure here so that a /stop command
# issued from within the server doesn't cause the service to restart.
# I think that's how it works, anyway.
Restart=OnFailure
[Install]
WantedBy=default.target
One possible backup service
I don’t suggest you do this.
I don’t know whether this correctly syncs the state of the server
before it does a backup.
I’ve placed a sleep 30 in there to be reasonably sure,
but any kind of fixed delay for disk sync is sketchy business.
This is a lesser problem, but I’ve been told systemd maintainers would be very displeased with my work. Ah, well, is it good art if it doesn’t make people feel?
[Unit]
Description=Back up Raspberry Flavoured MC server
# Conflicts= is used here so that raspberry-flavoured.service
# is brought down when this service starts back up.
Conflicts=raspberry-flavoured.service
[Service]
# chdir here so that the tar command there
# produces an archive with paths starting in
# raspberry/
# instead of
# /srv/minecraft/raspberry/
# I know you can do -C on... most tar impls, but this came to mind first.
WorkingDirectory=/srv/minecraft
# Extremely sketchy fixed delay to let the Minecraft server wind down.
# Strongly consider replacing.
ExecStartPre=sleep 30
ExecStartPre=sync
# ! choice of compressor !
#
# It kinda doesn't matter, given that most of
# a data folder's size is incompressible,
# but I think it's worth giving it a light tap;
# 75% of original size isn't *much* but it's *something*.
# I like zstd here because it produces decent ratios while
# also being reasonably fast to decompress.
#
# ! date !
#
# That date format is just date -Iminutes with the
# timezone replaced with the letter "Z" to mean UTC.
# This is ISO 8601 compliant, but GNU date prints "+00:00" instead.
#
# ! templating thoughts !
# I suspect it's possible to template this with a bit more finagling.
ExecStart=/usr/bin/bash -c 'tar -I "zstd -3 -T0" -cf /var/minecraft-backups/raspberry-"$(date --utc +%Y-%m-%dT%H:%MZ)".tar.zst raspberry; sync'
# I'd like the server to be brought back up once the backup is done.
# This is sketchy, but it does work.
# I'd like to see a future Interrupts= key that encodes both
# "stop this service" and "start it back up on exit".
ExecStopPost=systemctl start raspberry-flavoured.service
I don’t use a timer service because I’m the only player on my server, so I trigger backups manually. A timer service should be trivial to get up and running.
Stop a headless laptop-server from sleeping with the lid closed (systemd)
There are some weird answers to this problem floating around.
-
Masking the sleep, suspend, hibernate, and hybrid-sleep services entirely. (makes logind hog a whole CPU core and produce huge amounts of log spam)
-
Disabling ACPI entirely in kernel cmdline by passing
acpi=off. (ACPI does other things too!!) -
Unbinding the lid switch entirely via sysfs. (Huh?!)
So, here, the correct solution.
[login]
# (...)
HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
HandleLidSwitchDocked=ignore
Getting DuckDNS to do only IPv6
I didn’t want to set up port-forwarding, and UPnP on my router is flaky.
First, clear any existing records. You don’t want a useless A (IPv4) record to confuse your users.
#!/usr/bin/env sh
token=FILL_THIS_IN
domain=FILL_THIS_IN
echo url="https://www.duckdns.org/update?domains=${domain}&token=${token}&clear=true" | curl -K -
Then, we need two parts. First is the script that does a single update. This part is general to any init system.
#!/usr/bin/env sh
# Any number of these v6 IP detection websites is fine.
# I could probably hack together an ip invocation, but I couldn't be assed.
ipv6addr=$(curl -6 https://ifconfig.co)
token=FILL_THIS_IN
domain=FILL_THIS_IN
echo url="https://www.duckdns.org/update?domains=${domain}&token=${token}&ipv6=${ipv6addr}" | curl -K -
And then the systemd service and timer units.
[Unit]
Description=Update DuckDNS (v6 only)
[Service]
Type=oneshot
ExecStart=/usr/local/bin/duckdns-update-v6.sh
[Unit]
Description=Update DuckDNS entries periodically
After=network-online.service
[Timer]
OnBootSec=15s
# Adjust this to taste.
# My v6 addresses expire every 5 minutes,
# so I set the refresh interval to 4 minutes.
OnUnitActiveSec=240s
AccuracySec=15s
[Install]
WantedBy=timers.target
As usual, systemctl daemon-reload after installing these files.
A Gentoo env file that got 32-bit x86 FFmpeg building with glibc/Clang/ThinLTO
For reference, this was done on a 64-bit x86_64 Gentoo system that typically
uses glibc and GCC.
I just wanted FFmpeg built with LTO for funsies, but I also
enabled ABI_X86="64 32" system-wide for ease of admin.
This meant that I had to build x86 FFmpeg as well.
Credit goes to GitHub project InBetweenNames/gentooLTO issue #354, Build ffmpeg with -ffat-lto-objects
for -ffat-lto-objects.
I should probably take this up with upstream,
but I’m not sure how to approach the question.
The rest of this is ripped from the Gentoo Wiki article for LTO.
WARNING_FLAGS="-Werror=odr -Werror=strict-aliasing"
# -ffat-lto-objects seems to be the winner here.
COMMON_FLAGS="${COMMON_FLAGS} -flto=thin -ffat-lto-objects ${WARNING_FLAGS}"
CFLAGS="${COMMON_FLAGS}"
CXXFLAGS="${COMMON_FLAGS}"
FCFLAGS="${COMMON_FLAGS}"
FFLAGS="${COMMON_FLAGS}"
LDFLAGS="${COMMON_FLAGS} ${LDFLAGS}"
CC="clang"
CXX="clang++"
CPP="clang-cpp"
AR="llvm-ar"
NM="llvm-nm"
RANLIB="llvm-ranlib"
USE="lto"
Manual full → tv levels mapping (in case FFmpeg doesn’t like you)
yuvj420p (and other “j” pixel formats) are full-level pixel formats.
Normally FFmpeg can convert them just fine,
but in the case of yuvj420p → yuv420p10le conversion
(as you might do for AV1 encoding),
it simply makes no attempt to do level conversions;
it assumes that the video is already using tv levels.
Here’s a filtergraph statement that’ll simply force the matter, though I’d imagine there’s probably some precision loss or something.
# to convert [16, 235] to [0, 255]:
# subtract by 16 to get [0, 219]
# multiply by 255/219 to get [0, 255]
# clip values between [0, 255] for good measure.
geq=lum='clip((p(X, Y) - 16) * 1.164383562, 0, 255)':cb='clip((p(X, Y) - 16) * 1.164383562, 0, 255)':cr='clip((p(X, Y) - 16) * 1.164383562, 0, 255)'
You may also want to detect the pixel format before doing this,
as it clearly wouldn’t make sense for tv-level content to
be subjected to this.
pix_fmt="$(ffprobe -v error -of default=noprint_wrappers=1:nokey=1 -show_entries 'stream=pix_fmt' -select_streams V:0 "${input}")"