Comparison

Cloudflare Tunnel vs ngrok vs Ujex Ingress: Which Should Your Agent Use?

Akshay Sarode 7 min read
Direct answer

ngrok, Cloudflare Tunnel, and Ujex Ingress all hand your agent's local server a public URL, but they're solving different problems. ngrok gets you a URL in one command, at the cost of a rotating address on the free tier. Cloudflare Tunnel gives you a free, stable subdomain if you already own a domain in Cloudflare, but it's a pure network pipe with no idea what "agent" means. Ujex Ingress (a daemon called packd) wraps the same bore/cloudflared tunneling under the hood, then ties the traffic to your agent's actual identity and writes it into the same audit log as everything else the agent does. Pick based on whether you need speed, a stable address, or a record of who came through.

You've got an agent running locally that needs to receive something: a Stripe webhook, a GitHub event, a callback from some other service that only knows how to POST to a public URL. Your laptop or dev box doesn't have one of those. So you reach for a tunnel, and within a minute or two you've got `https://something.ngrok-free.app` or `https://something.trycloudflare.com` forwarding traffic to `localhost:3000`. It works. All three tools in this post will get you to that same first result. The differences only show up later, when the URL rotates out from under you, or when you need to answer "which agent request actually hit this endpoint" and the tunnel itself has no idea.

What "tunnel" actually means here

Strip away the branding and all three tools do the same basic thing. A small client process runs next to your local server and opens an outbound connection to a relay somewhere on the public internet. That relay hands you a public hostname. When a request comes in on that hostname, the relay forwards it down the same outbound connection back to your local process. Because the connection is outbound-initiated, you never have to open an inbound port on your firewall or router, which is exactly why this pattern is the default for dev boxes, laptops behind NAT, and agents that don't live on a server with a real public IP.

ngrok's client, Cloudflare's `cloudflared`, and `bore` (one of the tools Ujex Ingress supervises) are all doing this same outbound-relay trick. What differs is everything built on top of that plumbing: how stable the resulting address is, what it costs, and whether the tool knows or cares who's actually sending traffic through it.

ngrok: the one-command tunnel

ngrok is the fastest way to get from "nothing" to "a public URL pointed at my laptop." Install the binary, run ngrok http 3000, and you have an address within seconds. For a quick test, that's hard to beat, and it's why ngrok is usually the first thing anyone tries when they need to hand a webhook provider a URL that isn't localhost.

The catch is the free tier. Every time you restart the tunnel, you get a new random subdomain, so anything that depends on a fixed address (a webhook you registered somewhere, a callback URL baked into a config file) breaks on restart unless you go back and update it. There are also rate limits and connection caps on the free plan that you'll notice the moment you push more than light, occasional traffic through it. None of that is a knock on ngrok. It's optimized for "let me try this right now," and for that job it's genuinely the least friction of the three.

Cloudflare Tunnel: free, stable, but doesn't know who's inside it

Cloudflare Tunnel solves the rotating-URL problem. If you already have a domain sitting in Cloudflare, you can point a subdomain at `cloudflared` running next to your local server, and that subdomain stays the same across restarts, deploys, and reboots. It's free, and once it's set up you stop thinking about it, which is the whole appeal.

The setup cost is real though: you need a domain, you need it on Cloudflare's nameservers, and you need to configure `cloudflared` with a tunnel token, which is more steps than `ngrok http 3000`. Once that's done, what you have is a pure network-layer tunnel. It moves bytes from a public hostname to your local port and terminates TLS along the way. It has no concept of "agent," no log of which request came from where, and no way to distinguish traffic meant for one agent's endpoint from another's beyond whatever routing you build yourself. If all you need is a stable address for a single service, that's not a limitation, it's just scope. If you've got multiple agents or multiple credentials calling into the same box and you need to know which one a given request belongs to, Cloudflare Tunnel by itself won't tell you.

ngrok fastest to try Cloudflare Tunnel free stable subdomain Ujex Ingress agent identity + audit
Same underlying trick (outbound tunnel to a relay, relay hands you a public URL), three different things optimized for: speed to first URL, a free stable address, or an entry point wired into agent identity and the audit log.

Ujex Ingress: packd, plus identity and the audit trail

Ujex's ingress subsystem is a small Go daemon called packd. It doesn't reinvent tunneling: it supervises `bore` and `cloudflared`, the same tools underneath the other two options in this post, and manages which one is running and how. So on the connectivity side, you get the same basic outcome as Cloudflare Tunnel: your agent's local server ends up with a stable public URL it can receive webhooks or requests on.

The part that's different is identity. Ujex issues agents their own credentials, either device keys or inbox-scoped keys, deliberately separate from Firebase Auth, which is reserved for human users. When a request comes in through packd's tunnel, it's tied to that agent's actual identity rather than being anonymous traffic hitting a port. And because it's part of the Ujex platform, that request can flow into the same hash-chained audit log that records every other privileged action the agent takes. In practice that means you're not choosing between "connectivity" and "an audit trail." You get both from the same piece of infrastructure: a stable URL, and a record of which agent identity that traffic was associated with, sitting next to the rest of what the agent did.

That last part is the actual pitch. A tunnel that just moves bytes can't tell you, six weeks later, which agent's webhook handler processed a specific request. One that's wired into the platform's identity and audit system can, because the ingress traffic isn't a side channel, it's part of the same trail as everything else.

 ngrokCloudflare TunnelUjex Ingress
SetupOne commandDomain on Cloudflare + configPart of Ujex agent setup
URL stability (free tier)Rotates on restartFixed, yoursFixed, tied to agent
Underlying techngrok's own relaycloudflaredpackd supervising bore/cloudflared
Agent identity on requestsNoNoYes, device key or inbox-scoped key
Audit trailNoNoYes, hash-chained, same log as other agent actions
Cost shapeFree tier with limitsFreeFree tier, self-hostable, Apache-2.0

Which one should you use

The honest answer depends on what you're actually optimizing for right now, not on which tool sounds more sophisticated. Answer the question that's actually true for your setup and the widget below will give you a straight recommendation instead of an "it depends" essay.

Try it: what does your setup actually need?

Use ngrok. ngrok http 3000 gets you a public URL in under a minute, which is exactly what you need to hand a webhook provider or a teammate for a quick test today. Don't build anything long-running on top of the free tier's rotating URL, and don't overthink this one.

When plain Cloudflare Tunnel or ngrok is still the better call

I'd rather say this directly than pretend the platform option is always the right one. If you don't care about tying traffic to an agent's identity, and you don't need it showing up in an audit log, Ujex Ingress is more machinery than the job requires. Setting up device keys and thinking about an audit chain for a five-minute Stripe webhook test is overkill when `ngrok http 3000` does the same job with none of that setup. Same goes for a single personal project where you're the only one calling the endpoint and you already know exactly what hit it, because you're the one who sent it.

Where Ujex Ingress earns its keep is when the tunnel isn't the whole project, it's one part of an agent that's already doing privileged things through the platform: sending mail, writing to memory, taking actions that get logged. In that case you want the incoming traffic accounted for the same way as everything else the agent does, instead of being the one channel that's invisible to your own audit trail. If that's not your situation yet, plain Cloudflare Tunnel or ngrok is simpler, and simpler is correct until you actually need the rest.

FAQ

Does Ujex Ingress replace Cloudflare Tunnel entirely?

No. packd, the daemon behind Ujex Ingress, is built on top of the same underlying tunnel technologies, bore and cloudflared, that Cloudflare Tunnel itself uses. It supervises those tools rather than reimplementing tunneling from scratch, so it's complementary to that underlying tech, not a replacement for it.

What's the actual cost difference between ngrok, Cloudflare Tunnel, and Ujex Ingress?

We don't have exact current pricing figures for all three to hand you a table of numbers, and pricing changes, so check each provider's current pricing page before deciding on cost alone. The general shape: ngrok's free tier comes with a rotating URL and rate limits, Cloudflare Tunnel is free but requires you to own a domain on Cloudflare, and Ujex has a free tier and is fully self-hostable under Apache-2.0 if you'd rather not depend on a hosted plan at all.

Does traffic through Ujex Ingress show up in the same audit log as everything else the agent does?

Yes. Because ingress traffic is tied to the agent's own identity (its device key or inbox-scoped key), privileged actions that come in through the tunnel append to the same hash-chained audit log as every other action the agent takes on the platform. That's the actual point of the integration, not a side effect.

Can I self-host Ujex Ingress?

Yes. Ujex is Apache-2.0 and self-hostable, and packd runs as part of that same self-hosted stack, so you're not required to route traffic through a hosted relay you don't control.

Do I need to own a domain to use any of these?

For Cloudflare Tunnel, yes, you need a domain added to your Cloudflare account to get a stable subdomain out of it. ngrok gives you a subdomain on its own infrastructure without you owning anything (random on the free tier, custom on paid plans). Ujex Ingress can similarly give your agent a stable address without requiring you to own a domain yourself, worth checking current docs for the exact options.