Introducing ngrok-go: Ingress to Your Go Apps as a net.Listener
Today, we're excited to announce ngrok-go, our idiomatic Go package for embedding secure ingress directly into your Go applications. If you’ve used ngrok before, you can think of ngrok-go as the ngrok agent packaged as a Go library. ngrok-go is open source on GitHub, and API reference documentation is available on go.pkg.dev.
ngrok-go lets developers serve Go apps on the internet in a single line of code without setting up low-level network primitives like IPs, certificates, load balancers and even ports! Applications using ngrok-go listen on ngrok’s global ingress network but they receive the same interface any Go app would expect (net.Listener) as if it listened on a local port by calling net.Listen(). This makes it effortless to integrate ngrok-go into any application that uses Go's net or net/http packages.
Check out how easy it is to run the classic Go hello world web app with ngrok-go:
Add your ngrok authtoken to your environment as NGROK_AUTHTOKEN and run the above code. Your app is on the internet and ready to receive requests with a simple ngrok.Listen()!
It is that simple. There is no additional configuration or code required. In this example, we didn’t request a specific domain, so your app is assigned a URL (e.g. https://myapp.ngrok.io) which is available as l.Addr().
How ngrok-go works
The example app above doesn’t listen on any ports, how is that possible? When you call ngrok.Listen(), ngrok-go initiates a secure and persistent outbound TLS connection to ngrok’s ingress-as-a-service platform and transmits your configuration requirements — i.e. URL, authentication, webhook verification, and IP restrictions. The ngrok service sets up your configuration across all of our global points of presence in seconds and returns a URL for your application.
After your ingress is set up, ngrok receives HTTP requests at the closest region to the requester and enforces the middleware policies defined by your application. Unauthorized requests are blocked at the edge and only valid requests reach your ngrok-go app via the persistent TLS connection.
Why we built ngrok-go
Ingress should be a high-level abstraction
Creating ingress today is a frustrating exercise of wrangling a slew of disparate low level networking primitives. Developers must manage a number of technologies at different layers of the network stack like DNS, TLS Certificates, network-level CIDR policies, IP and subnet routing, load balancing, VPNs and NATs, just to name a few. In short, developers are being forced to work with the assembly language of networking.
We built ngrok-go to empower application developers to declare ingress policies at a high layer of abstraction without sacrificing security and control. As an example, here’s how ngrok-go allows developers to specify ingress with declarative options by removing the burden of working with low-level details:
A complete list of supported middleware configurations can be found in the ngrok-go API reference.
Ingress should be environment-independent
Traditionally, ingress is tightly coupled to the environment where your app is deployed. For example, the same app deployed to your own datacenter, an EC2 instance, or a Kubernetes cluster requires wildly different networking setups. Running your app in those three different environments means you need to manage ingress in three different ways.
ngrok-go decouples your app’s ingress from the environment where it runs.
When your application uses ngrok-go, you can run it anywhere and it will receive traffic the same way. From an ingress standpoint, your application becomes portable: it does not matter whether it runs on bare metal, VMs, AWS, Azure, in Kubernetes, serverless platforms like Heroku or Render, a racked data-center server, a Raspberry Pi, or on your laptop.
Ingress shouldn’t require sidecars
Developers often distribute the ngrok agent alongside their own applications to create ingress for their IoT devices, SaaS offerings, and CI/CD pipelines. It can be challenging to bundle and manage the ngrok agent as a sidecar process. ngrok-go eliminates the agent, simplifying distribution and management as well as enabling developers to easily deliver private label experiences.
How we designed ngrok-go
ngrok-go was designed with developer ergonomics top of mind and to seamlessly fit into the Go ecosystem:
- Compatible with any code that accepts a net.Listener: ngrok-go returns the standard library’s net.Listener interface. Developers can easily integrate ngrok-go into existing Go network code like http.Server without changes.
- Works in one line: ngrok-go can give you ingress with a simple ngrok.Listen(ctx, config.HTTPEndpoint()) call.
- Idiomatic functional options: ngrok-go enables developers to declare behaviors like authentication in a simple way, e.g. config.WithOAuth("google").
- Batteries included: ngrok-go includes logging adapters for the most common Go libraries like zap and logrus.
We validated ngrok-go’s design by dogfooding it in the ngrok agent and our soon-to-be-released Kubernetes ingress controller. We also worked with fellow gophers and customers like Airplane and Kubefirst for additional feedback on the design.
Will ngrok support other programming languages?
Get started with ngrok-go
Getting started is as easy as running go get golang.ngrok.com/ngrok in your Go module. Then check out the ngrok-go package docs, our getting started with ngrok-go guide, and the open source ngrok-go repo on GitHub.