modal.forward

@contextmanager
def forward(port: int, *, unencrypted: bool = False, client: Optional[_Client] = None) -> Iterator[Tunnel]:

Expose a port publicly from inside a running Modal container, with TLS.

If unencrypted is set, this also exposes the TCP socket without encryption on a random port number. This can be used to SSH into a container. Note that it is on the public Internet, so make sure you are using a secure protocol over TCP.

Important: This is an experimental API which may change in the future.

Usage:

from flask import Flask
from modal import Image, App, forward

app = App(image=Image.debian_slim().pip_install("Flask"))  # Note: "app" was called "stub" up until April 2024
app = Flask(__name__)


@app.route("/")
def hello_world():
    return "Hello, World!"


@app.function()
def run_app():
    # Start a web server inside the container at port 8000. `modal.forward(8000)` lets us
    # expose that port to the world at a random HTTPS URL.
    with forward(8000) as tunnel:
        print("Server listening at", tunnel.url)
        app.run("0.0.0.0", 8000)

    # When the context manager exits, the port is no longer exposed.

Raw TCP usage:

import socket
import threading
from modal import App, forward


def run_echo_server(port: int):
    """Run a TCP echo server listening on the given port."""
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(("0.0.0.0", port))
    sock.listen(1)

    while True:
        conn, addr = sock.accept()
        print("Connection from:", addr)

        # Start a new thread to handle the connection
        def handle(conn):
            with conn:
                while True:
                    data = conn.recv(1024)
                    if not data:
                        break
                    conn.sendall(data)

        threading.Thread(target=handle, args=(conn,)).start()


app = App()  # Note: "app" was called "stub" up until April 2024


@app.function()
def tcp_tunnel():
    # This exposes port 8000 to public Internet traffic over TCP.
    with forward(8000, unencrypted=True) as tunnel:
        # You can connect to this TCP socket from outside the container, for example, using `nc`:
        #  nc <HOST> <PORT>
        print("TCP tunnel listening at:", tunnel.tcp_socket)
        run_echo_server(8000)