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)