Skip to content

Home

java-ngrok - A Java wrapper for ngrok; programmatic tunnels for ingress, webhooks, demos, and APIs

Version Java Versions Coverage Build Code Quality Docs GitHub License

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-ngrok unifies ngrok's "tunnel" (v2) and "endpoint" (v3) concepts behind a single API: connect() returns a Tunnel and handles the differences for you through the JavaNgrokConfig.configVersion you set. All ngrok features 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 invokes ngrok'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 by connect() (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 ngrok is not already installed at JavaNgrokConfig.getNgrokPath, it will be installed the first time most methods in NgrokClient are 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 the NgrokInstaller.

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

Version Java Versions Coverage Build Docs GitHub License

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.