Newt and OpenBSD
The plan was simple: spin up an OpenBSD VM on Proxmox to host a small static site generated with an SSG. No giant stack, no Docker, no panel full of abstractions in the middle. Just a clean, predictable system with httpd serving static files, the way OpenBSD likes it.

On the public side I already had something running on a VPS: Pangolin, Traefik and Gerbil. Their job is to take traffic from the internet and send it to services inside my network, and inside the Pangolin world, the natural connector for that is Newt.
In this post I'll call the OpenBSD VM "web" and an older Debian machine on the same network "gateway". Names, addresses and the domain are changed, but the story is real. That Debian machine looked like a minor detail at first, and it turned out to be the key to everything.
Was OpenBSD a good idea? #
For Docker, no. For a static site, yes. People mix those two up and then blame the operating system. OpenBSD is a poor choice for piling up containers, ready-made stacks and a compose file for everything. For serving a static site with httpd, pf and a short config file, it's excellent.
I made a very modest VM: one CPU, a little memory, a simple disk and VirtIO networking. No X, no extras. The system came up, I left sshd on, created the right user, set up permissions with doas and moved on to what looked like the main step: installing Newt.
The wrong attempt: Newt on OpenBSD #
Pangolin has a nice, tempting one-liner to install Newt, the kind of command that solves everything on Linux in ten seconds:
curl -fsSL https://static.pangolin.net/get-newt.sh | bashBut I was on OpenBSD, and that's where it stopped. The script checks uname and only knows Linux, macOS and FreeBSD. Since the installer couldn't help, I tried to build Newt from source:
pkg_add curl git go gmake
git clone https://github.com/fosrl/newt.git
cd newt
go build -o newt .The build did start. Go downloaded the dependencies, filled the screen with modules and almost gave me hope. Then it stopped with a very clear error:
package github.com/fosrl/newt
imports github.com/fosrl/newt/clients/permissions: build constraints exclude all Go files in ~/newt/clients/permissionsNo missing package, no typo. In that part of the code, every Go file was excluded by build constraints on OpenBSD, so there was simply no implementation for the platform. Yelling at the terminal wasn't going to fix that.
The real mistake was in my head. I was stuck on the idea that the connector had to run inside the OpenBSD VM. Newt doesn't need to be on the same server as the service. It only needs to run somewhere that can reach the service.
That's when it clicked. I had connected internal services to Pangolin before, and I suspected it went through the Debian machine. So I checked:
hostname
uname -a
ip a
which newt
ps aux | grep -i newt | grep -v grep
systemctl status newt --no-pagerIt was all there. The gateway was running Newt as a systemd service, already connected to my Pangolin endpoint. The tunnel existed. I was trying to build a new bridge over a river that already had one.
The right architecture #
Everything got cleaner once I stopped trying to push Newt into OpenBSD. The design became:
- the internet reaches Pangolin, Traefik and Gerbil on the VPS
- Pangolin sends the traffic through the Newt tunnel to the gateway
- the gateway forwards it over the LAN to
http://192.168.1.20:80, the OpenBSD VM
The gateway was already the connector for that network and could reach the VM directly. So Pangolin only needed a new resource pointing at the VM's internal IP, using the gateway as the site. No need to turn OpenBSD into Linux.
On the OpenBSD side, things were easy. I created the site directory inside httpd's usual place:
mkdir -p /var/www/htdocs/siteI added a test index.html and a minimal /etc/httpd.conf. httpd runs chrooted in /var/www, which is why the root below starts at /htdocs:
server "default" {
listen on * port 80
root "/htdocs/site"
}Checked the config:
httpd -nAnd turned the service on:
rcctl enable httpd
rcctl start httpdFrom the VM itself, the server was up. From the gateway, the test passed too:
curl -I http://192.168.1.20It answered 200 OK with Server: OpenBSD httpd, so the path from the gateway to the VM was working.
The white page #
Then came the most annoying part. I opened the site in the browser, the page loaded with no error, the certificate was valid, and everything was white. This is the kind of bug that fools you, because it looks like DNS, like the proxy, like Pangolin, like anything except the obvious.
So I stopped staring at the browser and tested with curl:
curl -v http://192.168.1.20/
curl -v https://example.org/Both returned 200 OK. TLS was fine, the domain pointed at the VPS, and the traffic was reaching OpenBSD. Then one line gave it away:
Content-Length: 0It wasn't DNS, Pangolin, Traefik, Gerbil or Newt. OpenBSD was sending a perfectly valid response with nothing in it. The white page was, literally, an empty page.
The fix #
I recreated index.html properly in the directory httpd serves:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Hello from OpenBSD</title>
</head>
<body>
<h1>Alive on OpenBSD</h1>
<p>Served by OpenBSD httpd on a small VM.</p>
</body>
</html>I restarted httpd, ran the tests again, and the content showed up. The site now loaded HTML served by the OpenBSD VM, through the gateway and Pangolin.
What I took from it #
The obvious lesson: not everything has to run on the same machine. Newt didn't need to be on OpenBSD. It needed to be on a machine that could see OpenBSD, and the gateway was already doing that.
The more important one: when a tool doesn't really support a platform, insisting on it turns into technical fetishism. Building Newt on OpenBSD felt elegant, and it was unnecessary. The right move was to let OpenBSD do what it does well, which is serving a site simply and cleanly.
And then there's the lesson that keeps coming back to humble me: test layer by layer. DNS, TLS, proxy, connector, internal service, in that order. In the end the bug was in the dumbest possible place, a file with zero bytes of useful content.
The OpenBSD VM stayed minimal, serving a static site with httpd. The Debian gateway stayed the Newt connector for the network. Pangolin kept handling public exposure, with Traefik and the certificate working. OpenBSD didn't become the host of a stack that doesn't suit it, and Linux kept the connector that already worked.
What fixed it in the end was taking things away. I stopped installing software where it didn't need to be.
A note from late 2026 #
Pangolin has moved on a bit since I set this up. Newt's own README now says it's being phased out in favor of the Pangolin CLI, and the docs tell new installs to use the CLI (pangolin site up). Newt is still available as a standalone connector if you want a smaller binary or a container. Neither install script lists OpenBSD (the CLI one covers Linux, macOS, FreeBSD and Windows), so the same approach still applies: run the connector on a machine that's supported and point it at the OpenBSD box.