The fastest entry point to every app on your home server.
GenHTTP Gateway is a lightweight, self-hosted reverse proxy built on ioxide, the engine behind the fastest HTTP/1.1 web server in the HTTP Arena benchmark. Deployed in front of a homelab, NAS or VPS, it routes domains and paths to Docker containers, devices, static files and redirects from a single YAML file, with Let’s Encrypt certificates, HTTP/2 and HTTP/3.
docker pull genhttp/gateway
engine: ioxide
hosts:
domain1.com:
default:
destination: http://10.0.0.2:8080
routes:
admin:
routes:
portainer:
destination: http://10.0.0.2:9000
pi-hole:
destination: http://10.0.0.3/admin/
files:
listing: /data/files/
security:
certificate:
pem: live/domain1.com/fullchain.pem
key: live/domain1.com/privkey.pem
GET https://domain1.com/admin/portainer/
Powered by the fastest engine available
The gateway runs on ioxide, an engine built on Linux’ io_uring that keeps every request on a single CPU core from start to finish. GenHTTP on ioxide is the fastest web server on HTTP/1.1 in the independent HTTP Arena benchmark, ahead of the leading servers written in Rust, Zig, Go and JavaScript; only experimental configurations tuned specifically for the benchmark score higher. ioxide is open source and available on GitHub.
Compared with established reverse proxies
HTTP Arena ranks infrastructure servers such as nginx, h2o and Caddy separately from web servers. The tests below are those both rankings share. Each ball represents the same number of requests, so a faster flow means higher throughput.
Static files served over HTTPS, a core workload for a gateway in front of a website.
This is an indirect comparison: in these tests, each server answers requests itself; nginx, h2o and Caddy do so through a small module written for the benchmark. The figures do not include forwarding to an upstream application. Traefik, Apache and HAProxy are not part of HTTP Arena. Values are averages across all connection counts, measured on a 64-core AMD Threadripper in November 2026.
Full results
| Requests per second | GenHTTP Gateway (ioxide) | nginx | h2o | Caddy |
|---|---|---|---|---|
| Static files over TLSHTTP/1.1 | 730k | 721k | no result | 159k |
| PlainHTTP/1.1 | 3.41M | 3.28M | 3.64M | 432k |
| Short-lived connectionsHTTP/1.1 | 2.61M | 2.63M | 2.65M | 238k |
| PipelinedHTTP/1.1 | 24.21M | 4.04M | 4.94M | 761k |
| JSON over TLSHTTP/1.1 | 1.03M | 1.31M | 1.23M | 322k |
| PlainHTTP/2 | 9.37M | 3.37M | 15.87M | 389k |
| Static filesHTTP/2 | 552k | 799k | 353k | 115k |
| PlainHTTP/3 | 1.94M | 4.85M | no result | 361k |
| Static filesHTTP/3 | 237k | 361k | no result | 22k |
Efficient CPU usage
Peak throughput is only part of the picture. HTTP Arena also runs tests at a fixed request rate and measures the resources required, which better reflects the load of a typical home server.
- ¼ core
- of one CPU core at a steady 10,000 requests per second, with a mean latency of 75 µs. ioxide does not busy-poll, so CPU usage follows the load. Latency-10K test
- 2.7 cores
- to echo 10 KB request bodies over TLS 50,000 times per second, roughly 8 Gbit/s of traffic. 8Gbit test
- 0.1 ms
- mean response time at one million requests per second. Latency-1M test
A focused feature set for home servers
Each host is a block in gateway.yaml and each path a route. Changes take effect after restarting the container.
- Virtual hosts
- Serve any number of domains from a single IP address, for example several websites on one VPS. A host named
anyhandles all remaining requests. - Nested routes
/admin/portainer/is a route within a route, so services can be grouped in a clear hierarchy.- Reverse proxy
destination:forwards requests to any HTTP service on your network, such as Nextcloud, Jellyfin or Home Assistant in a container, a NAS or a router’s web interface.- Static websites
content:serves a directory as a website, with an optional index file.- File listings
listing:turns a directory into a browsable download page, for example to share files from a NAS.- Redirects
location:redirects a path to another URL, on your server or elsewhere on the web.- HTTPS by default
- Once a host has a certificate, plain HTTP requests are redirected to HTTPS.
- Let’s Encrypt
- Reads certbot’s PEM files directly and serves the
.well-knownfolder for webroot validation. - HTTP/1.1, HTTP/2, HTTP/3
- Select the protocols for the secure port. HTTP/3 runs over QUIC on the same port via UDP.
- Multi-platform images
- Images for Linux x64, ARM64 and ARM32, including the Raspberry Pi, and for Windows x64.
Deliberately simpler than Traefik
Most reverse proxies are designed for clusters. The gateway is designed for a single machine: a home server, a NAS, a Raspberry Pi or a VPS hosting a few projects. The entire configuration can be read in a minute, and nothing changes unless you change it. Some features are therefore out of scope: container discovery via labels, middleware chains, load balancing and built-in certificate issuance. If you need them, Traefik, Caddy or nginx are a better fit.
Enable ioxide with a single line
The gateway runs on the engines of the GenHTTP web server, selected with engine: in gateway.yaml. Kestrel is the default because it is available in every image. On Linux x64 servers, switching to ioxide provides the fastest of the three, with 2.3 times Kestrel’s HTTP Arena score.
| Engine | HTTP Arena score | Built on | Protocols | Runs on |
|---|---|---|---|---|
ioxide fastest | 7,117 | io_uring, one reactor per CPU core | HTTP/1.1, 2, 3 | Linux x64, kernel 6.1+ |
kestrel default | 3,090 | ASP.NET Core Kestrel | HTTP/1.1, 2, 3 | Every image, including ARM and Windows |
internal | 4,538 | GenHTTP’s own engine | HTTP/1.1 | Every image |
Enabling ioxide
More about the engine is available on the ioxide website and in its GitHub repository. To use it with the gateway, run the linux-x64 image on a host with kernel 6.1 or newer. Docker’s default seccomp profile blocks the io_uring system calls, so pass the profile from the repository, which is Docker’s default profile with those calls allowed. For HTTP/3 on ioxide, provide certificates as PEM files.
# gateway.yaml
engine: ioxide
# docker run
docker run ... \
--security-opt seccomp=/data/gateway/seccomp-ioxide.json \
genhttp/gateway:linux-x64
From docker run to HTTPS in four steps
The image is available on Docker Hub as genhttp/gateway. On first start, it writes an example gateway.yaml to the configuration volume.
-
Start the container
docker run -d -p 80:80 -p 443:443 \ -v /data/gateway/config:/app/config \ -v /data/gateway/data:/app/data \ -v /etc/letsencrypt:/app/certs:ro \ --name gateway \ genhttp/gatewayservices: gateway: image: genhttp/gateway container_name: gateway restart: always volumes: - /data/gateway/config:/app/config - /data/gateway/data:/app/data - /etc/letsencrypt:/app/certs:ro ports: - 80:80 - 443:443 -
Describe your hosts
Edit
/data/gateway/config/gateway.yaml: one entry per domain, adefaultfor its root androutesfor the paths below it. Then restart the container withdocker restart gateway.hosts: home.example.com: default: destination: http://10.0.0.2:8080 routes: cloud: destination: http://10.0.0.3:8080 # Nextcloud media: destination: http://10.0.0.4:8096 # Jellyfin home-assistant.example.com: default: destination: http://10.0.0.5:8123 -
Get certificates
Certbot validates your domain through the running gateway, which serves
.well-knownfrom the data volume. Reference the PEM files belowlive/and let certbot restart the gateway after each renewal.certbot certonly --webroot \ -w /data/gateway/data/ -d home.example.com certbot renew \ --deploy-hook "docker restart gateway"home.example.com: security: certificate: pem: live/home.example.com/fullchain.pem key: live/home.example.com/privkey.pem -
Turn on HTTP/3 optional
Add
http3to the protocols and publish the secure port for UDP as well:-p 443:443/udp.protocols: - http1 - http2 - http3
Images on Docker Hub
| Tag | For |
|---|---|
latest | Selects the matching image for your platform: Linux x64, ARM32, ARM64 or Windows x64 |
linux-x64 | Linux x64; the only image that includes the ioxide engine |
linux-arm64 | ARMv8 Linux, such as the Raspberry Pi 4 and 5 |
linux-arm32 | ARMv7 Linux, such as the Raspberry Pi 2 and 3 |
windows-x64 | Windows hosts, on Nano Server |
Frequently asked questions
Is it an alternative to Traefik or nginx?
For home servers, yes. It routes domains and paths to your applications over HTTPS, configured in a single YAML file. It deliberately omits container discovery, middleware chains, load balancing and built-in certificate issuance. If you need those, Traefik, Caddy or nginx are a better fit.
Does the gateway request Let’s Encrypt certificates itself?
No. Certbot obtains them in webroot mode while the gateway is running: the gateway serves the .well-known folder from its data volume, including over plain HTTP when HTTPS is enforced. It then reads certbot’s PEM files directly, without conversion. Mount the entire /etc/letsencrypt directory, as the files in live/ are links into archive/.
What can I run behind the gateway?
Any service that speaks HTTP: self-hosted applications such as Nextcloud, Jellyfin, Home Assistant, Immich, Vaultwarden or Gitea, admin tools such as Portainer and Pi-hole, the web interface of a NAS or router, or your own websites. Typical setups are a homelab, a NAS or Raspberry Pi at home, a small VPS hosting several sites, or the development and staging servers of a small team.
Does it run on a Raspberry Pi?
Yes. genhttp/gateway is published for Linux ARM32 (Raspberry Pi 2 and later) and ARM64 (Raspberry Pi 4 and later), as well as Linux x64 and Windows x64.
Which engine should I choose?
On Linux x64 with kernel 6.1 or newer, ioxide: it is the fastest engine, built on io_uring, and supports HTTP/1.1, HTTP/2 and HTTP/3. Kestrel is the default because it runs in every image, including ARM and Windows. The internal GenHTTP engine is lightweight and supports HTTP/1.1 only.
How fast is it compared to nginx or Caddy?
Built on the ioxide engine, the gateway runs on the fastest HTTP/1.1 web server in the HTTP Arena benchmark. Compared with the reverse proxies HTTP Arena measures, it is on par with nginx and h2o for plain HTTP/1.1, short-lived connections and static files over TLS; several times faster for pipelined HTTP/1.1; faster than nginx for plain HTTP/2; behind nginx for JSON over TLS, static files over HTTP/2 and HTTP/3; and 3 to 32 times faster than Caddy in every test. These tests measure each server answering directly, not forwarding to an application. The comparison above covers every test.
How much CPU does it need?
In HTTP Arena's Latency-10K test, which holds the load at 10,000 requests per second, ioxide uses a quarter of one CPU core with a mean latency of 75 microseconds. It does not busy-poll, so CPU usage follows the load. In the 8Gbit test, it echoes 10 KB bodies over TLS 50,000 times per second using 2.7 cores.
Can I run it without Docker?
Yes. It is a .NET application: clone the repository and start it with dotnet run (requires the .NET SDK).
Questions or feedback? Join us on Discord.
The gateway is open source under the MIT license and part of the GenHTTP project. Questions, ideas and bug reports are welcome.