Holm on Windows: a small compile gap, an untested runtime
Holm ships for Linux and macOS, on amd64 and arm64. There has never been a Windows build, and the installer stops with "Unsupported OS" anywhere else. This post covers why, what we found when we tried to build Holm for Windows, and what has to happen before we call it supported.
Nothing described here has shipped. Windows support is planned work.
Why there was no Windows build
Holm started as a way to deploy static sites to your own VPS with no extra dependencies. The design target is still a small server: a cheap VPS with one virtual CPU and 1 GB of memory. A production node is a Linux machine where Holm installs itself as a systemd service, gets HTTPS certificates for your domains and upgrades itself in place.
Release builds have targeted Linux and macOS since the first release pipeline, and that list has not changed. Linux is where Holm runs in production. macOS lets you run the same binary on your own machine. Even there Holm trails Linux: it has no native service setup on macOS yet. Linux has always come first.
Windows also raised a product question we had not answered. Should Holm on Windows be a command-line tool that manages Holm servers elsewhere, or a complete local server? Those lead to different work. A server needs integration with the Windows service manager, a safe upgrade path and a clean way to stop and start. We chose to wait for that answer before building either, and to make no claim about Windows in the meantime. The public reference says it plainly: Windows is not an official target.
The last reason is evidence. A cross-compile proves that code builds. It tells you nothing about how the program behaves on that system. We want platform claims backed by tests that ran on the platform, and our continuous integration has only ever run on Linux. Without a Windows machine in the loop, a Windows build would have been a guess.
That open question has since narrowed. Whatever role Windows ends up with, the first step is the same: the Holm core has to build and run there. So we started with the build.
What a Windows compile turns up
A C toolchain is usually the hardest part of porting a program to Windows. Holm needs none. The binary is built without cgo, and its SQLite driver is written in pure Go. Go can cross-compile a program like that for Windows from a Linux machine with no extra tools.
When we cross-compiled the server for 64-bit Windows, the build failed in exactly four places. All four are Unix-only code:
- Free disk space, checked in three places. Holm refuses app storage writes when the disk is nearly full, checks for room before it snapshots the database ahead of a migration, and reports free space in its database stats. All three ask the operating system through the Unix
statfscall. - The single-instance lock. At startup Holm takes an exclusive lock on a file beside the database, so a second server can't open the same database. That lock uses the Unix
flockcall. - The hardened network listener. On Linux, Holm asks the kernel to hold a new connection until the client sends data (TCP_DEFER_ACCEPT). That blunts slow-connection attacks at the kernel level. It also turns on TCP Fast Open. The library that sets these options is Linux-only and doesn't compile for Windows. Holm already falls back to a plain listener on other systems at runtime, so only the build breaks.
We replaced those four with stand-ins in a throwaway copy, and the whole server compiled into a 64-bit Windows executable of about 65 MB. The compile gap is small.
The real fixes are small too. Windows has direct equivalents for free-space queries and file locks. The listener code will split by build target, so the Linux hardening stays on Linux and every other system gets the plain listener at compile time, as it already does at runtime. We expect the whole change to come to roughly 150 to 250 lines.
What we don't know yet
None of this has run on Windows. A program that compiles for a platform can still misbehave there, and server software touches the operating system in many places. These are the areas we expect to need work:
- Self-upgrade.
holm upgradereplaces the running binary with a new release. Linux allows that. Windows won't let a running .exe be replaced in place, so upgrades need a different approach there. - Running as a service. Holm installs a systemd service on Linux. Windows has its own service manager, and Holm has no code for it yet.
- Shutdown. On Unix, the service manager stops Holm with a signal and Holm shuts down cleanly. Windows delivers stop requests through other channels.
- File permissions and paths. Holm sets Unix file modes on its data and assumes Unix-style paths in places. Windows handles both differently.
- The installer. The installer is a shell script for Linux and macOS. Windows will need its own way to install.
Each of these could be a small fix or could need real design work. We will know once the server runs on an actual Windows machine.
The plan
The work comes in four steps:
- Proper Windows versions of the four spots. Native free-space and lock calls, and a listener split by build target.
- A Windows cross-compile check in CI. Every change gets built for Windows, so new Unix-only code can't break that build silently.
- The test suite on a Windows CI runner. The tests that guard the Linux build will run on Windows too.
- A smoke test on a disposable Windows VM. Start the server, deploy a static app, restart it, upgrade it and run it as a service. The VM is thrown away afterwards, so each run starts from a clean machine.
The first two steps are mechanical. The last two will show which of the runtime questions above are real problems.
Where that leaves you today
The release archives and the installer still cover Linux and macOS only, and the public reference still says Windows is not an official target. That stays true until the tests above pass. A successful compile is not enough for us to call a platform supported. When a Windows build ships, the changelog will say so.