I've been shipping local development demos through ngrok for years. I don't hate it — I actually admire it. It invented the category, the developer experience is excellent, the free tier kept me unblocked more times than I can count. If you clicked through here expecting me to tear into them, that isn't the post.
Over the last month I built 21tunnel: an open-source, self-hostable, multi-tenant tunnel service in Rust. The build log covers how. This post covers why — which means a fair comparison against the product that was in my shell history every day for five years.
Disclosure: I'm a maintainer of 21tunnel. Every claim below is either factual (sourced from ngrok's public pricing page) or my own opinion labeled as such. Comparison facts are frozen at April 2026; check the live sources if you're reading this later.
What ngrok gets right
Three things, and they're not small:
- The DX is the gold standard.
ngrok http 3000is the shortest possible distance between a laptop and a public HTTPS URL. Every other tool in this space — Cloudflare Tunnel, Tailscale Funnel, localtunnel — is measured against this. The inspector, the replay button, the config file format; they got almost all of it right on the first pass. - Reliability at their scale. The service basically never goes down. As a solo developer running a weekend tunnel service, I can tell you that “basically never” is an astonishing thing to ship.
- Agent polish. The Go agent is production-hardened, cross-compiles to every architecture that matters, and the SDKs for Node / Go / Python are well-maintained. This is years of compounded work.
Any comparison needs to start there. ngrok deserves the market position it has. Now the honest part.
Where it pinches
The free-tier cap
ngrok's free plan has an explicit rate cap on tunnel connections per minute. For “paste a URL in Slack so my designer can see the button” traffic, you will never hit it. For webhook-heavy workflows (Stripe test mode firing a hundred events in a burst, GitHub pushing through sample payloads, Shopify's checkout flow) you can hit it by lunchtime. The cap is there to make Personal plan at $10/mo worth buying — and it works; I've paid.
21tunnel's free tier has no rate cap. You're bounded by bandwidth (50 GB / month on Hobby) and nothing else. If you blow through 50 GB, you're likely a paying customer anyway; capping requests punishes the person doing legitimate development.
The custom-domain tax
A reserved custom domain is behind the Personal plan on ngrok.
Which is fine — pricing is their call. But random-8-chars.ngrok-free.app breaks
every webhook you're trying to test, because the URL
rotates every time you restart. Paying $10/mo to stop losing
your receive endpoint is a reasonable decision, and also a
decision you shouldn't have to make.
21tunnel gives every free-tier user one reserved subdomain on signup and lets you bring a custom domain via CNAME, free forever. Our cost to do this is roughly zero (it's a Postgres row); gating it behind payment would be a pricing decision, not a cost-recovery one. So we don't.
Single-tenant by design
The thing I bounced off the most: ngrok's control plane is single-tenant from your perspective. You have an account. If you're a SaaS company wanting to let your customers each have their own tunnels under your brand — the pattern where you want tunnels as a feature of your own product — you're stitching it together outside their dashboard.
21tunnel ships multi-tenant from day one: organizations, role-based access control (owner / admin / member / viewer), a hybrid self-serve signup with a superadmin-gated approval queue, a JWT + refresh-token auth stack with theft detection, optional TOTP MFA. If you want to white-label tunnels for your users, the dashboard is already set up to carve rooms for them. On our hosted tier you pay per seat; on self-host you pay nothing.
What we built instead
Same developer-facing CLI. Different everything behind it.
the whole thing, top to bottomAgent Rust binary (cross-compiled to macOS, Linux, Windows, ARM)
Transport TLS 1.3 + yamux over TCP (replaced QUIC for NAT traversal)
Server Rust (axum + tokio), single binary, static link
Auth capability-token (HMAC-signed, scoped, revocable)
Dashboard API JWT access (15min) + rotating refresh cookie (30d, theft-detect)
Dashboard UI Next.js 14 static export, one static asset tree
Data Postgres 15 with 5 migrations, monthly-partitioned audit log
License MIT + Apache-2.0, the whole stack
Deployment one VM, one binary, one docker-compose file
The differentiating piece: every layer of that is open. ngrok's agent is MIT; their server and control plane are proprietary and SaaS-only. We published the server too. The reasoning is simple — if you're not willing to hand your users' traffic to someone else's service, we want your business as a self-host customer, not a lost sale.
When to stay on ngrok
I genuinely mean this. Stay on ngrok when:
- You're a solo developer on the free tier and never hit the rate cap. The DX is great, it works, why complicate your life.
- You depend on one of their enterprise-tier features we haven't matched yet — their Kubernetes Operator is well-worn, their deeper traffic-policy rules are battle-tested, and some of their SaaS connectors are load-bearing in specific workflows. If you're wiring signed webhooks from ten providers today, we're not at feature parity.
- You value “it's been around since 2012” over “it's been around since January 2026.” This is a legitimate choice for production-critical dependencies.
When to try 21tunnel
Try us when:
- Your free-tier usage is getting bitten by the rate cap and you'd rather not pay $10/mo purely to un-throttle.
- You want a custom domain on your very first tunnel — not after you decide the tool is worth paying for.
- You're building a product where your users need tunnels. Multi-tenant out of the box, role-based access, audit log, invitations all ship. The dashboard mechanics are already there.
- You want to self-host. Compliance scope, data residency, or just “I don't want to route my customers' traffic through a third party” — the whole stack is on GitHub, and the runbook walks you through the four-command bootstrap.
- You want to run the same binary on your laptop, your CI runner, and your customer's VPC. The agent and server are one repo, one language, one build system.
Migrating in an afternoon
The quickest way to find out whether a tunnel service works for you is to point one tunnel at it and keep everything else the same. We've made that deliberately easy.
# before
ngrok http 3000 --domain=api.company.ngrok.app
# after
tunnel21 http 3000 --domain=api.21tunnel.app
# or, if you have a config file you like
tunnel21 --ngrok-compat
# reads your existing ~/.ngrok/ngrok.yml verbatim
Your CI scripts keep working. Your tests keep passing. If the
answer is no, ngrok is still the right call for us,
you've spent ten minutes finding out. If the answer is
yes, your bill has dropped and your .ngrok.yml
still works.
The pitch in one paragraph. ngrok is a genuinely great SaaS, and we don't expect it to lose its lead on developer brand or reliability any time soon. Our bet is that the people whose free-tier limits pinch, whose pricing sensitivity is real, or whose compliance wants the whole stack on their own VMs, don't need marketing — they need the source code, honest pricing, and a CLI that doesn't make them relearn anything. So that's what we built.
See the side-by-side feature + price table,
read the build log,
or just git clone and try it.