pyngrok Documentation

repository·develop·Indexed 19 days ago

https://github.com/alexdlaird/pyngrok

A Python wrapper for ngrok that provides a programmatic API and command-line access to ngrok's reverse proxy capabilities. It manages its own binary and unifies ngrok's tunnel (v2) and endpoint (v3) concepts via the ngrok.connect() method. The library includes modules for process management, configuration, agent interaction, and automatic binary installation, with integration guides for Flask, Django, FastAPI, Docker, and Google Colab.

Tokens
9.9K
Snippets
31
Records
51
Agent score
61%

What's inside pyngrok

  1. How tunnels and endpoints are unified in pyngrok

    develop
    Historically, ngrok distinguished between "tunnels" (v2) and "endpoints" (v3). pyngrok abstracts these differences into a single connect() method. The returned NgrokTunnel object handles the underlying implementation details based on your configured config_version. This allows you to use all ngrok features (including v3-specific endpoints) through a consistent Python interface.
  2. Use pyngrok via Docker

    develop

    You can use pre-built images from alexdlaird/pyngrok on Docker Hub.

    • Interactive Python shell: docker run -e NGROK_AUTHTOKEN=$NGROK_AUTHTOKEN -it alexdlaird/pyngrok
    • Interactive Bash shell: docker run -e NGROK_AUTHTOKEN=$NGROK_AUTHTOKEN -it alexdlaird/pyngrok /bin/bash
    • Custom Config: Mount your local ngrok.yml to /root/.config/ngrok/ngrok.yml using a volume.
    • Web Inspector: To access the ngrok web inspector, expose port 4040 and ensure your config sets web_addr: 0.0.0.0:4040.
    # Run interactive bash
    docker run -e NGROK_AUTHTOKEN=$NGROK_AUTHTOKEN -it alexdlaird/pyngrok /bin/bash
    
    # Run with custom config
    docker run -v ./ngrok.yml:/root/.config/ngrok/ngrok.yml -it alexdlaird/pyngrok
    
    # Expose Web Inspector
    docker run --env-file .env -p 4040:4040 -it alexdlaird/pyngrok
  3. Integrate pyngrok with Django

    develop

    For Django, add a USE_NGROK flag to your settings.py that checks os.environ.get("RUN_MAIN", None) != "true". To trigger the tunnel creation when the development server starts, extend AppConfig.ready() in one of your apps.py files. Inside ready(), use ngrok.connect(port) to open the tunnel, then update settings.BASE_URL with the resulting public_url so that your application's internal logic (like webhooks) uses the public endpoint.

    # In settings.py
    USE_NGROK = os.environ.get("USE_NGROK", "False") == "True" and os.environ.get("RUN_MAIN", None) != "true"
    
    # In apps.py
    class CommonConfig(AppConfig):
        def ready(self):
            if settings.USE_NGROK:
                from pyngrok import ngrok
                # Logic to determine port from sys.argv...
                public_url = ngrok.connect(port).public_url
                settings.BASE_URL = public_url
  4. Expose a local AWS Lambda shim via Flask

    develop

    To develop AWS Lambda functions locally, use a Flask application as a shim. Create a Flask Blueprint that captures incoming requests and transforms them into the event dictionary format expected by your Lambda handler. You can then use pyngrok to expose this Flask shim, allowing you to test your Lambda logic via public HTTP requests.

    from flask import Blueprint, request
    import json
    from lambdas.foo_GET import lambda_function as foo_GET
    
    bp = Blueprint("lambda_routes", __name__)
    
    @bp.route("/foo")
    def route_foo():
        # Transform Flask request into Lambda event
        event = {
            "someQueryParam": request.args.get("someQueryParam")
        }
        return json.dumps(foo_GET.lambda_handler(event, {}))
  5. Use pyngrok in Google Colab

    develop

    To use pyngrok in a Google Colab notebook, first install it as a dependency using !pip install pyngrok.

    SSH Example

    To expose an SSH server running in Colab, use ngrok.connect("22", "tcp"). You will need to provide your ngrok authtoken via conf.get_default().auth_token.

    HTTP Example

    To expose a web server (like Flask) from Colab, use ngrok.connect(port).public_url. It is recommended to update your application's base URL or webhook configurations with this public_url so that external services can reach your local server.

    import getpass
    from pyngrok import ngrok, conf
    
    # Set authtoken
    print("Enter your authtoken...")
    conf.get_default().auth_token = getpass.getpass()
    
    # Open a TCP ngrok tunnel to the SSH server
    connection_string = ngrok.connect("22", "tcp").public_url
    
    ssh_url, port = connection_string.strip("tcp://").split(":")
    print(f" * ngrok tunnel available, access with `ssh root@{ssh_url} -p{port}`")
  6. Enable DEBUG logging to the console

    develop

    To debug common issues, configure the standard Python logging module to stream DEBUG level logs to the console. This will surface the root cause of failures during pyngrok method calls.

    import logging
    from pyngrok import ngrok
    
    # Setup a logger
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    handler = logging.StreamHandler()
    handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
    logger.addHandler(handler)
    
    # Call the pyngrok method throwing the error
    ngrok.connect()
  7. Implement end-to-end testing with pyngrok

    develop

    You can automate end-to-end testing by extending unittest.TestCase and creating fixtures that start a development server and open a pyngrok tunnel. This allows your tests to validate workflows that require a publicly accessible URL (e.g., testing webhooks).

    Key steps in the pattern:

    1. Use setUpClass to set environment variables (like USE_NGROK=True), start the server, and capture the app.config["BASE_URL"] from the tunnel.
    2. Use tearDownClass to shut down the server and call ngrok.kill() to close all tunnels.
    3. For Flask/Werkzeug servers, implement a /shutdown route to allow the test suite to stop the server gracefully.
    import os
    import unittest
    import threading
    from pyngrok import ngrok
    from server import create_app
    
    class PyngrokTestCase(unittest.TestCase):
        @classmethod
        def setUpClass(cls):
            os.environ["USE_NGROK"] = True
            app = cls.start_dev_server()
            cls.base_url = app.config["BASE_URL"]
    
        @classmethod
        def tearDownClass(cls):
            cls.stop_dev_server()
            ngrok.kill()
  8. Integrate pyngrok with FastAPI

    develop

    In FastAPI, use a BaseSettings class (e.g., via Pydantic) to manage USE_NGROK and BASE_URL. When the application initializes, if USE_NGROK is enabled, import ngrok, connect to the local port (defaulting to 8000 or parsed from --port), and update your settings object with the public_url returned by the tunnel.

    from fastapi import FastAPI
    from pydantic import BaseSettings
    from pyngrok import ngrok
    
    class Settings(BaseSettings):
        BASE_URL = "http://localhost:8000"
        USE_NGROK = os.environ.get("USE_NGROK", "False") == "True"
    
    settings = Settings()
    app = FastAPI()
    
    if settings.USE_NGROK:
        port = sys.argv[sys.argv.index("--port") + 1] if "--port" in sys.argv else "8000"
        public_url = ngrok.connect(port).public_url
        settings.BASE_URL = public_url
  9. Install pyngrok

    develop

    You can install pyngrok using pip or conda. Once installed, pyngrok is available as a Python package, and the ngrok binary is added to your command line path.

    # Using pip
    pip install pyngrok
    
    # Using conda
    conda install -c conda-forge pyngrok
  10. Verify pyngrok ngrok installation via Command Line

    develop

    Since pyngrok is a wrapper, errors are often caused by the underlying ngrok binary. To ensure pyngrok is managing the correct version of ngrok, run ngrok in your terminal. The output must end with PYNGROK VERSION: |pyngrok_version|.

    If you see a different version or no PYNGROK VERSION string, another ngrok installation (e.g., via Homebrew or npm) is likely higher in your $PATH. You should reorder your $PATH to prioritize the pyngrok managed binary.

    To further isolate if the issue is with ngrok or your Python code, try running these commands:

    1. Headless mode: ngrok start --none --log stdout
    2. Simple HTTP tunnel: ngrok http 80 --log stdout

    If these commands work in the terminal but your Python code fails, the issue is likely in your application's configuration.

    ngrok
  11. Integrate pyngrok with Flask

    develop

    To use pyngrok with Flask, initialize a tunnel within your application factory (create_app). Use an environment variable like USE_NGROK to toggle the tunnel and ensure you check WERKZEUG_RUN_MAIN != 'true' to prevent the tunnel from opening twice during Flask's reloader process. Once the tunnel is open via ngrok.connect(port), capture the public_url and update your application's BASE_URL or webhook configurations so they use the public ngrok address instead of localhost.

    import os
    import sys
    from flask import Flask
    from pyngrok import ngrok
    
    def create_app():
        app = Flask(__name__)
        app.config.from_mapping(
            BASE_URL="http://localhost:5000",
            USE_NGROK=os.environ.get("USE_NGROK", "False") == "True" and os.environ.get("WERKZEUG_RUN_MAIN") != "true"
        )
    
        if app.config["USE_NGROK"]:
            port = sys.argv[sys.argv.index("--port") + 1] if "--port" in sys.argv else "5000"
            public_url = ngrok.connect(port).public_url
            app.config["BASE_URL"] = public_url
        return app