Home

java-ngrok is a Java wrapper for ngrok that provides programmatic tunnels for ingress, webhooks, demos, and APIs.
It downloads and manages the ngrok agent for you, making it available through a Java API. It runs anywhere the
ngrok agent does. The library is mature and stable, and it tracks new ngrok releases.
ngrok is a reverse proxy that opens secure tunnels from public URLs to localhost. It's perfect
for rapid development (test webhooks, demo local websites, enable SSH access), establishing ingress to external
networks and devices, building production APIs (traffic policies, OAuth, load balancing), and more. And
it's made even more powerful with native Java integration through the java-ngrok client.
Installation
java-ngrok is available
on Maven Central.
If you want ngrok to be available from the command
line, pyngrok
can be installed using pip to manage that for you.
Open a Tunnel
To open a tunnel, use the NgrokClient's connect()
method, which returns a Tunnel,
and this returned object has a reference to the public URL generated by ngrok, which can be retrieved with
getPublicUrl().
final NgrokClient ngrokClient = new NgrokClient.Builder().build();
// Open a HTTP tunnel on the default port 80
// <Tunnel: "https://<public_sub>.ngrok.io" -> "http://localhost:80">
final Tunnel httpTunnel = ngrokClient.connect();
// Open a SSH tunnel
// <Tunnel: "tcp://0.tcp.ngrok.io:12345" -> "localhost:22">
final CreateTunnel sshCreateTunnel = new CreateTunnel.Builder()
.withProto(Proto.TCP)
.withAddr(22)
.build();
final Tunnel sshTunnel = ngrokClient.connect(sshCreateTunnel);
// Open a named tunnel from the config file
final CreateTunnel createNamedTunnel = new CreateTunnel.Builder()
.withName("my-config-file-tunnel")
.build();
final Tunnel namedTunnel = ngrokClient.connect(createNamedTunnel);
// Open an Internal Endpoint that's load balanced
// <Tunnel: "https://some-endpoint.internal" -> "http://localhost:9000">
final CreateTunnel createInternalEndpoint = new CreateTunnel.Builder()
.withAddr("9000")
.withUrl("https://some-endpoint.internal")
.withPoolingEnabled(true)
.build();
final Tunnel internalEndpoint = ngrokClient.connect(createInternalEndpoint);
The connect()
method can also take
a CreateTunnel
(which can be built through its Builder)
that allows you to pass additional properties that
are supported by ngrok (or withName()
to use a tunnel defined in ngrok's config
file), as documented here.
Note:
java-ngrokunifiesngrok's "tunnel" (v2) and "endpoint" (v3) concepts behind a single API:connect()returns aTunneland handles the differences for you through theJavaNgrokConfig.configVersionyou set. Allngrokfeatures are available to you through this. For v3-specific Endpoints, see Using v3 Endpoints.
Get Active Tunnels
It can be useful to ask the ngrok client what tunnels are currently open. This can be accomplished with the
getTunnels() method, which returns a list of Tunnel objects.
// [<Tunnel: "https://<public_sub>.ngrok.io" -> "http://localhost:80">]
final List<Tunnel> tunnels = ngrokClient.getTunnels();
Close a Tunnel
All open tunnels will automatically be closed when the Java process terminates, but we can also close them manually
with disconnect(String).
// The Tunnel returned from methods like connect(), getTunnels(), etc. contains the public URL
ngrokClient.disconnect(publicUrl);
Expose Other Services
Using ngrok you can expose any number of non-HTTP services, for instances databases, game servers, etc. This can be
accomplished by using java-ngrok to open a TCP tunnel to the desired service.
// Open a tunnel to MySQL with a Reserved TCP Address
// <NgrokTunnel: "tcp://1.tcp.ngrok.io:12345" -> "localhost:3306">
final CreateTunnel mysqlCreateTunnel = new CreateTunnel.Builder()
.withProto(Proto.TCP)
.withAddr(3306)
.withUrl("tcp://1.tcp.ngrok.io:12345")
.build();
final Tunnel mysqlTunnel = ngrokClient.connect(mysqlCreateTunnel);
You can also serve up local directories via ngrok's built-in fileserver.
// Open a tunnel to a local file server
// <NgrokTunnel: "https://<public_sub>.ngrok.io" -> "file:///">
final CreateTunnel fileserverCreateTunnel = new CreateTunnel.Builder()
.withAddr("file:///")
.build();
final Tunnel fileserverTunnel = ngrokClient.connect(fileserverCreateTunnel);
Tunnel Configuration
It is possible to configure the tunnel when it is created, for instance adding authentication, a domain, or other
additional endpoint configurations that are supported by ngrok.
This is accomplished by using CreateTunnel.Builder
to set what properties will be used when the tunnel is created. Here is an example that opens a tunnel with the
domain some-domain.ngrok.dev, requires basic authentication for requests, and defines a circuit breaker.
final CreateTunnel createTunnel = new CreateTunnel.Builder()
.withUrl("https://some-domain.ngrok.dev")
.withTrafficPolicy(Map.of("on_http_request", List.of(Map.of("actions", List.of(
Map.of("type", "basic-auth", "config", Map.of("credentials", List.of("username:password"))),
Map.of("type", "circuit-breaker", "config", Map.of("error_threshold", 0.5)))))))
.build();
final Tunnel tunnel = ngrokClient.connect(createTunnel);
If you already have a tunnel defined in ngrok's config file, you can start it by its name (note that "-api"
will be appended to its name when started).
final CreateTunnel createTunnel = new CreateTunnel.Builder()
.withName("my-config-file-tunnel")
.build();
final Tunnel tunnel = ngrokClient.connect(createTunnel);
Using v3 Endpoints
java-ngrok defaults to ngrok's v3 config (its endpoints block is
read alongside the tunnels block). v2 withAddr() / withProto() arguments are translated into the equivalent
upstream block automatically. Set JavaNgrokConfig.configVersion to ConfigVersion.V2 to use the
v2 config instead.
version: "3"
endpoints:
- name: my-config-file-tunnel
upstream:
url: http://localhost:8000
protocol: http1
pooling_enabled: true
- name: java-ngrok-default
upstream:
url: http://localhost:80
You can also open a v3 endpoint without defining it in a config file:
// Open a v3 endpoint
final CreateTunnel createEndpoint = new CreateTunnel.Builder()
.withUpstream(new Upstream.Builder().withUrl("http://localhost:8000").build())
.withBindings(List.of("public"))
.build();
final Tunnel endpoint = ngrokClient.connect(createEndpoint);
ngrok's API
The api() method allows you to use the local
ngrok agent to make requests against the ngrok API, if you
have set an API key.
For example, here's how you would reserve a ngrok domain, then create a Cloud Endpoint with an associated traffic
policy:
final String domain = "some-domain.ngrok.dev";
final ApiResponse domainResponse = ngrokClient.api(
List.of("reserved-domains", "create",
"--domain", domain));
final ApiResponse endpointResponse = ngrokClient.api(
List.of("endpoints", "create",
"--bindings", "public",
"--url", String.format("https://%s", domain),
"--traffic-policy-file", "policy.yml"));
Note:
api("endpoints", ...)here invokesngrok's agent CLI to manage Cloud Endpoints, which are dashboard-managed and persist independently of any local agent. This is distinct from the local agent Endpoints managed byconnect()(see Using v3 Endpoints).
Event Logs
When ngrok emits logs, java-ngrok can surface them to a callback function. To register this callback, use JavaNgrokConfig.Builder.withLogEventCallback(Function<NgrokLog, Void>), as show here:
final Function<NgrokLog, Void> logEventCallback = ngrokLog -> {
System.out.println(ngrokLog.getLine());
return null;
};
final JavaNgrokConfig javaNgrokConfig = new JavaNgrokConfig.Builder()
.withLogEventCallback(logEventCallback)
.build();
final NgrokClient ngrokClient = new NgrokClient.Builder()
.withJavaNgrokConfig(javaNgrokConfig)
.build();
If these events aren't necessary for your use case, some resources can be freed up by turning them off. JavaNgrokConfig.Builder.withoutMonitoring()
will disable logging, or you can call NgrokProcess.ProcessMonitor.stop()
to stop monitoring on a running process.
java-ngrok logs through SLF4J, and ngrok's own output is logged by the
com.github.alexdlaird.ngrok.process.NgrokProcess logger. To quiet it, raise that logger's level in your logging
backend. For example, with Logback:
<!-- Quiet ngrok's own output -->
<logger name="com.github.alexdlaird.ngrok.process.NgrokProcess" level="WARN"/>
<!-- Or quiet all java-ngrok output -->
<logger name="com.github.alexdlaird" level="WARN"/>
Configuration
JavaNgrokConfig
java-ngroks interactions with the ngrok binary can be configured using JavaNgrokConfig,
which can then be passed to NgrokClient.
final JavaNgrokConfig javaNgrokConfig = new JavaNgrokConfig.Builder()
.withMaxLogs(10)
.build();
final NgrokClient ngrokClient = new NgrokClient.Builder()
.withJavaNgrokConfig(javaNgrokConfig)
.build();
Note: If
ngrokis not already installed atJavaNgrokConfig.getNgrokPath, it will be installed the first time most methods inNgrokClientare called.If you need to customize the installation of
ngrok, perhaps specifying a timeout, proxy, use a custom mirror for the download, etc., you can do so by leveraging theNgrokInstaller.
Setting the authtoken or api_key
Running ngrok with an auth token and API key enables access to more features available on your account (for instance,
multiple concurrent tunnels, custom domains, use of Internal Endpoints,
etc). You can obtain your auth token and generate API keys from the ngrok dashboard,
then install it to ngrok's config file.
final JavaNgrokConfig javaNgrokConfig = new JavaNgrokConfig.Builder()
.withAuthToken("<NGROK_AUTHTOKEN>")
.withApiKey("<NGROK_API_KEY>")
.build();
final NgrokClient ngrokClient = new NgrokClient.Builder()
.withJavaNgrokConfig(javaNgrokConfig)
.build();
You could instead define NGROK_AUTHTOKEN or NGROK_API_KEY as environment variables, if you don't want to define
them in code.
Config File
By default, ngrok will look for its config file in the default location.
You can override this behavior with JavaNgrokConfig.Builder.withConfigPath(Path).
final JavaNgrokConfig javaNgrokConfig = new JavaNgrokConfig.Builder()
.withConfigPath(Path.of("opt", "ngrok", "config.yml"))
.build();
final NgrokClient ngrokClient = new NgrokClient.Builder()
.withJavaNgrokConfig(javaNgrokConfig)
.build();
Setting the Region
By default, ngrok chooses the region with the lowest latency for you. To pick a region instead, set
connect_url in ngrok's config file to that region's address. The
region is set when the ngrok agent starts, so it applies to every tunnel that agent opens.
final Path configPath = Path.of("opt", "ngrok", "au.yml");
new NgrokInstaller().installDefaultConfig(configPath,
Map.of("agent", Map.of("connect_url", "connect.au.ngrok-agent.com:443")));
final JavaNgrokConfig javaNgrokConfig = new JavaNgrokConfig.Builder()
.withConfigPath(configPath)
.build();
final NgrokClient ngrokClient = new NgrokClient.Builder()
.withJavaNgrokConfig(javaNgrokConfig)
.build();
To use more than one region at the same time, start a separate agent for each by giving each NgrokClient a
JavaNgrokConfig with its own config file.
Binary Path
The java-ngrok package manages its own ngrok binary. You can use your ngrok binary if you want by setting it with JavaNgrokConfig.Builder.withNgrokPath(Path)
and passing that config to NgrokClient.
final JavaNgrokConfig javaNgrokConfig = new JavaNgrokConfig.Builder()
.withNgrokPath(Path.of("usr", "local", "bin", "ngrok"))
.build();
final NgrokClient ngrokClient = new NgrokClient.Builder()
.withJavaNgrokConfig(javaNgrokConfig)
.build();
Command Line Usage
Assuming you have also installed pyngrok, all features of
ngrok are available
on the command line.
ngrok http 80
For details on how to fully leverage ngrok from the command line,
see ngrok's official documentation.
Java 8
A Java 8-compatible build was previously maintained in the 1.4.x
branch. While it is no longer supported, it is available through the java8-ngrok artifact instead on Maven Central.
For more details on what differs in the java8-ngrok dependency,
see the "Java 8" section of the docs.