pyngrok - a Python wrapper for ngrok¶

pyngrok
is a Python wrapper for ngrok
that manages its own binary, making ngrok
available via a convenient
Python API.
ngrok is a reverse proxy tool that opens secure tunnels from public URLs to localhost, perfect
for exposing local web servers, building webhook integrations, enabling SSH access, testing chatbots, demoing from
your own machine, and more, and its made even more powerful with native Python integration through pyngrok
.
Installation¶
pyngrok
is available on PyPI and can be installed
using pip
:
pip install pyngrok
or conda
:
conda install -c conda-forge pyngrok
That’s it! pyngrok
is now available as a package to our Python projects,
and ngrok
is now available from the command line.
Open a Tunnel¶
To open a tunnel, use the connect
method, which returns a NgrokTunnel
,
and this returned object has a reference to the public URL generated by ngrok
in its public_url
attribute.
from pyngrok import ngrok
# Open a HTTP tunnel on the default port 80
# <NgrokTunnel: "https://<public_sub>.ngrok.io" -> "http://localhost:80">
http_tunnel = ngrok.connect()
# Open a SSH tunnel
# <NgrokTunnel: "tcp://0.tcp.ngrok.io:12345" -> "localhost:22">
ssh_tunnel = ngrok.connect("22", "tcp")
# Open a named tunnel from the config file
named_tunnel = ngrok.connect(name="my_tunnel_name")
The connect
method takes kwargs
as well, which allows us to pass
additional properties that are supported by ngrok,
as shown below.
Note
ngrok
v2’s default behavior for http
when no additional properties are passed is to open two tunnels,
one http
and one https
. pyngrok
’s connect
method will return a reference to
the http
tunnel in this case. If only a single tunnel is needed, pass bind_tls=True
and a reference to
the https
tunnel will be returned.
ngrok
’s Cloud Edge¶
To use ngrok’s Cloud Edge with pyngrok
, first configure an Edge on ngrok’s dashboard
(with at least one Endpoint mapped to the Edge), and define a labeled tunnel in the ngrok config file
that points to the Edge.
tunnels:
some-edge-tunnel:
labels:
- edge=my_edge_id
addr: http://localhost:80
To start a labeled tunnel in pyngrok
, pass its name
to connect
.
from pyngrok import ngrok
# Open the Cloud Edge tunnel that is defind in the config file
named_tunnel = ngrok.connect(name="some-edge-tunnel")
Once a Cloud Edge tunnel is started, it can be managed through ngrok’s dashboard.
Get Active Tunnels¶
It can be useful to ask the ngrok
client what tunnels are currently open. This can be
accomplished with the get_tunnels
method, which returns a list of
NgrokTunnel
objects.
from pyngrok import ngrok
# [<NgrokTunnel: "https://<public_sub>.ngrok.io" -> "http://localhost:80">]
tunnels = ngrok.get_tunnels()
Close a Tunnel¶
All open tunnels will automatically be closed when the Python process terminates, but we can
also close them manually with disconnect
.
from pyngrok import ngrok
# The NgrokTunnel returned from methods like connect(),
# get_tunnels(), etc. contains the public URL
ngrok.disconnect(ngrok_tunnel.public_url)
The ngrok
Process¶
Opening a tunnel will start the ngrok
process. This process will remain alive, and the tunnels
open, until kill
is invoked, or until the Python process terminates.
If we are building a short-lived app, for instance a CLI, we may want to block on the ngrok
process so tunnels stay open until the user intervenes. We can do that by accessing the
NgrokProcess
.
from pyngrok import ngrok
ngrok_process = ngrok.get_ngrok_process()
try:
# Block until CTRL-C or some other terminating event
ngrok_process.proc.wait()
except KeyboardInterrupt:
print(" Shutting down server.")
ngrok.kill()
The NgrokProcess
contains an api_url
variable, usually initialized to
http://127.0.0.1:4040, from which we can access the ngrok client API.
Note
If some feature we need is not available in this package, the client API is accessible to us via the
api_request
method. Additionally, the NgrokTunnel
objects expose a
uri
variable, which contains the relative path used to manipulate that resource against the client API.
This package also gives us access to ngrok
from the command line, as shown below.
Event Logs¶
When ngrok
emits logs, pyngrok
can surface them to a callback function. To register this
callback, use PyngrokConfig
and pass the function as log_event_callback
. Each time a
log is processed, this function will be called, passing a NgrokLog
as its only parameter.
from pyngrok import conf, ngrok
def log_event_callback(log):
print(str(log))
conf.get_default().log_event_callback = log_event_callback
# <NgrokTunnel: "https://<public_sub>.ngrok.io" -> "http://localhost:80">
ngrok_tunnel = ngrok.connect()
If these events aren’t necessary for our use case, some resources can be freed up by turning them off. Set
monitor_thread
to False
in PyngrokConfig
.
from pyngrok import conf, ngrok
conf.get_default().monitor_thread = False
# <NgrokTunnel: "https://<public_sub>.ngrok.io" -> "http://localhost:80">
ngrok_tunnel = ngrok.connect()
Alternatively, stop_monitor_thread
can be used to stop monitoring on a
running process.
import time
from pyngrok import ngrok
# <NgrokTunnel: "https://<public_sub>.ngrok.io" -> "http://localhost:80">
ngrok_tunnel = ngrok.connect()
time.sleep(1)
ngrok.get_ngrok_process().stop_monitor_thread()
Expose Other Services¶
Using ngrok
we can expose any number of non-HTTP services, for instances databases, game servers, etc. This
can be accomplished by using pyngrok
to open a tcp
tunnel to the desired service.
from pyngrok import ngrok
# Open a tunnel to MySQL with a Reserved TCP Address
# <NgrokTunnel: "tcp://1.tcp.ngrok.io:12345" -> "localhost:3306">
ngrok.connect("3306", "tcp",
remote_addr="1.tcp.ngrok.io:12345")
We can also serve up local directories via ngrok’s built-in fileserver.
from pyngrok import ngrok
# Open a tunnel to a local file server
# <NgrokTunnel: "https://<public_sub>.ngrok.io" -> "file:///">
ngrok.connect("file:///")
Configuration¶
PyngrokConfig
¶
pyngrok
’s interactions with the ngrok
binary can be configured using PyngrokConfig
.
The default pyngrok_config
object can updated with our own object using set_default
.
from pyngrok import conf
pyngrok_config = conf.PyngrokConfig(log_event_callback=log_event_callback,
max_logs=10)
conf.set_default(pyngrok_config)
Most methods in the ngrok
module also accept a pyngrok_config
keyword arg, which can be used
to pass in the config rather than updating the default as shown above.
The pyngrok_config
argument is only used when the ngrok
process is first started, which will be
the first time most methods in the ngrok
module are called. We can check if a process is already or
still running by calling its healthy
method.
Note
If ngrok
is not already installed at the ngrok_path
in PyngrokConfig
, it
will be installed the first time most methods in the ngrok
module are called.
If we need to customize the installation of ngrok
, perhaps specifying a timeout, proxy, use a custom mirror
for the download, etc. we can do so by leveraging the installer
module. Keyword arguments in this
module are ultimately passed down to urllib.request.urlopen
, so as long as we use the
installer
module ourselves prior to invoking any ngrok
methods, we can can control
how ngrok
is installed and from where.
ngrok
Version Compatibility¶
pyngrok
is compatible with ngrok
v2 and v3, but by default it will install v3. To install v2 instead,
set ngrok_version
in PyngrokConfig
.
from pyngrok import conf, ngrok
conf.get_default().ngrok_version = "v2"
Setting the authtoken
¶
Running ngrok
with an auth token enables additional features available on our account (for
instance, the ability to open multiple tunnels concurrently, or use ngrok’s Cloud Edge).
We can obtain our auth token from the ngrok dashboard and install it to ngrok
’s
config file.
from pyngrok import ngrok
# Setting an auth token allows us to open multiple
# tunnels at the same time
ngrok.set_auth_token("<NGROK_AUTH_TOKEN>")
# <NgrokTunnel: "https://<public_sub1>.ngrok.io" -> "http://localhost:80">
ngrok_tunnel1 = ngrok.connect()
# <NgrokTunnel: "https://<public_sub2>.ngrok.io" -> "http://localhost:8000">
ngrok_tunnel2 = ngrok.connect("8000")
We can also override ngrok
’s installed auth token using PyngrokConfig
.
from pyngrok import conf, ngrok
conf.get_default().auth_token = "<NGROK_AUTH_TOKEN>"
# <NgrokTunnel: "https://<public_sub>.ngrok.io" -> "http://localhost:80">
ngrok_tunnel = ngrok.connect()
Setting the region
¶
By default, ngrok
will open a tunnel in the us
region. To override this, use
the region
parameter in PyngrokConfig
.
from pyngrok import conf, ngrok
conf.get_default().region = "au"
# <NgrokTunnel: "https://<public_sub>.au.ngrok.io" -> "http://localhost:80">
ngrok_tunnel = ngrok.connect()
Passing options
as kwargs
¶
It is possible to configure the tunnel when it is created, for instance adding authentication,
a subdomain, or other tunnel properties supported by ngrok.
This is accomplished by passing these options
as additional kwargs
to connect
,
then they will be used as properties for the tunnel when it is created.
Here is an example starting ngrok
in Australia, then opening a tunnel with subdomain
foo
that requires basic authentication for requests.
from pyngrok import conf, ngrok
conf.get_default().region = "au"
# <NgrokTunnel: "http://foo.au.ngrok.io" -> "http://localhost:80">
ngrok_tunnel = ngrok.connect(subdomain="foo",
auth="username:password")
Config File¶
By default, ngrok will look for its config file in the default location.
We can override this behavior by updating our default PyngrokConfig
.
from pyngrok import conf, ngrok
conf.get_default().config_path = "/opt/ngrok/config.yml"
# <NgrokTunnel: "https://<public_sub>.ngrok.io" -> "http://localhost:80">
ngrok_tunnel = ngrok.connect()
Binary Path¶
The pyngrok
package manages its own ngrok
binary. We can use our ngrok
binary if we want
by updating the default PyngrokConfig
.
from pyngrok import conf, ngrok
conf.get_default().ngrok_path = "/usr/local/bin/ngrok"
# <NgrokTunnel: "https://<public_sub>.ngrok.io" -> "http://localhost:80">
ngrok_tunnel = ngrok.connect()
Command Line Usage¶
This package puts the default ngrok
binary on our path, so all features of ngrok
are also
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.
Python 2.7¶
The last version of pyngrok
that supports Python 2.7 is 4.1.x, so we need to pin pyngrok>=4.1,<4.2
if we still
want to use pyngrok
with this version of Python. Its legacy documentation can be found here.
Dive Deeper¶
For more advanced usage, integration examples, and tips to troubleshoot common issues, dive deeper in to the rest of the documentation.
Contributing¶
If you find issues, report them on GitHub.
If you would like to contribute to the code, the process is pretty simple:
Familiarise yourself with this package, pyngrok’s APIs and other documentation, and ngrok’s documentation.
Fork the repository on GitHub and start implementing changes.
Write a test that plainly validates the changes made.
Build and test locally with
make local test
Submit a pull requests to get the changes merged.
Also be sure to review the Code of Conduct before submitting issues or pull requests.
Want to contribute financially? If you’ve found pyngrok
useful, sponsorship
would also be greatly appreciated!