gradio · difficulty ◆◆
gradio launch() — the call that turns your Blocks into a running web app
Interface built the UI; launch() opens the doors — server, queue, auth, tunnel and MCP in a single call.
Every 'Running on local URL' line you have ever seen was printed by this one call — and its signature has 46 public parameters (48 in all, counting two private ones).
AI-generated draft · reviewed and editedThis text was generated with AI as a first draft, then reviewed and edited by the author, who holds editorial responsibility for it.$ demo.launch()- 46
- Public parameters
- 3.13.1
- Share links born
- 5.28.0
- MCP switch added
- 22
- Routes on a fresh app
public parameters in the gr.Blocks.launch signature on 6.27.0 (48 in total; _app and _frontend are private)
frp-based public links; on PyPI 2022-12-15
'Let Gradio apps also be MCP Servers' (PR #10984); on PyPI 2025-04-29
counted from demo.app.routes on a fresh 6.27.0 Interface
What it does
demo.launch() is where your gr.Interface or gr.Blocks stops being a Python object and becomes a web service. On gradio 6.27.0 it starts a uvicorn server (default 127.0.0.1:7860, stepping to 7861, 7862… when the port is busy), wires the event queue, prints the * Running on local URL banner — and returns a (app, local_url, share_url) tuple. That tuple is the giveaway: launch() is not fire-and-forget, it hands you a live FastAPI app (gradio.routes.App) you can inspect, mount or test. In Gradio 6 the app-wide settings moved here too: theme, css, js and head belong to launch(), not the constructor. mcp_server, enable_monitoring, pwa and i18n are launch-time switches as well.
Why it matters
launch() is the only call in the library that touches the network, so every deployment-shaped decision lives on it. share=True tunnels your localhost to a public *.gradio.live URL — one parameter that let a GPU-locked lab machine serve a demo to a reviewer. auth=('admin','s3cret') puts a login page in front of the app. enable_monitoring=True exposes per-request analytics. mcp_server=True turns the same demo into a tool server an LLM can call. Learning launch() properly means never redeploying anything just to flip one of these switches.
Example
$ import gradio as gr, json, urllib.request
def answer(question):
return "42, obviously"
demo = gr.Interface(
answer,
inputs=gr.Textbox(label="question", placeholder="Ask anything"),
outputs=gr.Textbox(label="answer"),
title="Deep Thought",
examples=[["What is the answer to everything?"]],
)
app, local_url, share_url = demo.launch(server_port=7861, prevent_thread_lock=True)
print("app class:", type(app).__module__ + "." + type(app).__name__)
print("local_url:", local_url, "| share_url:", share_url)
info = json.load(urllib.request.urlopen("http://127.0.0.1:7861/gradio_api/info"))
print("named endpoints:", list(info["named_endpoints"].keys()))* Running on local URL: http://127.0.0.1:7861
* To create a public link, set `share=True` in `launch()`.
app class: gradio.routes.App
local_url: http://127.0.0.1:7861/ | share_url: None
named endpoints: ['/answer']Verified on gradio 6.27.0 (Python 3.12). The unpacked return: app is a live FastAPI (gradio.routes.App), share_url is None without share=True. The browser shows the Deep Thought title, a Textbox 'question' (placeholder 'Ask anything'), Submit, a Textbox 'answer', the example row and Clear/Flag buttons. /gradio_api/info is the machine-readable API index — the endpoint is named after the function, /answer, not /predict.
$ import gradio as gr
def caption(img, conf):
return f"a {['dim','moody','bright'][min(int(conf*3),2)]} image"
demo = gr.Interface(
caption,
inputs=[gr.Image(label="image"), gr.Slider(0, 1, value=0.5, step=0.1, label="confidence")],
outputs=gr.Textbox(label="caption", lines=2),
)
app, local, public = demo.launch(prevent_thread_lock=True, share=True)
print("CAPTURED local:", local)
print("CAPTURED share:", public)
demo.close()* Running on local URL: http://127.0.0.1:7860
* Running on public URL: https://25f4dae8d27c9b8019.gradio.live
This share link is temporary and will last for up to 1 week (best effort). For free permanent hosting and GPU upgrades, run `gradio deploy` from the terminal in the working directory to deploy to Hugging Face Spaces (https://huggingface.co/spaces)
CAPTURED local: http://127.0.0.1:7860/
CAPTURED share: https://25f4dae8d27c9b8019.gradio.live
Closing server running on port: 7860
Killing tunnel 127.0.0.1:7860 <> https://25f4dae8d27c9b8019.gradio.liveRan for real on 6.27.0: share=True downloads an frpc binary, tunnels localhost to a random *.gradio.live subdomain and prints the one-week caveat. demo.close() tears the tunnel down. Rendered UI: Image input 'image' plus Slider 'confidence' on the left, Textbox 'caption' (2 lines) on the right. Your public label is the 16-hex chunk before .gradio.live — anyone holding the URL reaches your machine until the process dies.
$ import gradio as gr, urllib.request, urllib.parse, http.cookiejar
def answer(question):
return "42, obviously"
demo = gr.Interface(answer, gr.Textbox(label="question"), gr.Textbox(label="answer"))
demo.launch(server_port=7861, prevent_thread_lock=True, quiet=True, auth=("admin", "s3cret"))
cj = http.cookiejar.CookieJar()
op = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
try:
op.open("http://127.0.0.1:7861/gradio_api/info")
except Exception as e:
print("pre-login /gradio_api/info:", e)
r = op.open("http://127.0.0.1:7861/login",
data=urllib.parse.urlencode({"username": "admin", "password": "s3cret"}).encode())
print("POST /login:", r.status)
r2 = op.open("http://127.0.0.1:7861/gradio_api/info")
print("post-login /gradio_api/info:", r2.status)pre-login /gradio_api/info: HTTP Error 401: Unauthorized
POST /login: 200
post-login /gradio_api/info: 200Verified on 6.27.0. auth= fronts the app with a login page: API routes 401 until you POST the credentials to /login, which sets an HttpOnly session cookie. Basic-auth headers are rejected — script it by POSTing /login and reusing the cookie jar (curl -c/-b cookies.txt). A wrong password returns HTTP 400, not a retry loop. quiet=True silenced the banner; the UI is the same Textbox 'question' → Textbox 'answer' demo behind the wall.
$ import gradio as gr
def reverse(text):
return text[::-1]
with gr.Blocks() as demo:
t_in = gr.Textbox(label="prompt")
btn = gr.Button("Reverse")
t_out = gr.Textbox(label="reversed")
btn.click(reverse, t_in, t_out)
demo.launch(server_name="0.0.0.0", server_port=7861, prevent_thread_lock=True)* Running on local URL: http://0.0.0.0:7861
* To create a public link, set `share=True` in `launch()`.Blocks app, verbatim run on 6.27.0: Textbox 'prompt', Button 'Reverse', Textbox 'reversed' in one column. server_name='0.0.0.0' binds every interface so colleagues on the LAN reach http://<your-ip>:7861 — the printed 0.0.0.0 is not the address you hand them. Omit server_port and launch() starts at 7860, stepping forward when occupied: in my probe a second demo auto-landed on 7861 while the first held 7860.
$ import gradio as gr
a = gr.Interface(lambda x: x, gr.Textbox(), gr.Textbox())
a.launch(server_port=7860, prevent_thread_lock=True, quiet=True)
b = gr.Interface(lambda t: t.upper(), gr.Textbox(label="text"), gr.Textbox(label="shout"))
app_b, url_b, _ = b.launch(prevent_thread_lock=True)
print("SECOND DEMO URL:", url_b)
c = gr.Interface(lambda t: t[::-1], gr.Textbox(label="text"), gr.Textbox(label="reversed"))
print("BEGIN-PRINT")
print(c.launch(prevent_thread_lock=True, quiet=True))
print("END-PRINT")
c.close(); a.close(); b.close()* Running on local URL: http://127.0.0.1:7861
* To create a public link, set `share=True` in `launch()`.
SECOND DEMO URL: http://127.0.0.1:7861/
BEGIN-PRINT
END-PRINT
Closing server running on port: 7862
Closing server running on port: 7860
Closing server running on port: 7861Three launches, one output, verbatim on 6.27.0. a quietly holds 7860; b (no server_port) auto-steps to 7861 and unpacked url_b confirms it; c then lands on 7862 — visible in the closing order (c=7862, a=7860, b=7861). And print(c.launch(...)) emits an empty line: the return tuple's class, TupleNoPrint, prints nothing by design to keep notebooks clean. Two demos ran side by side on different ports with zero configuration.
Common flags
- share=True
- Tunnel localhost to a temporary public *.gradio.live URL — 'up to 1 week (best effort)' per launch's own banner. Needs outbound internet.
- server_name / server_port
- Bind address and port. Default 127.0.0.1:7860; '0.0.0.0' exposes the app to the LAN; the port auto-steps forward when occupied.
- prevent_thread_lock=True
- Return control to your script instead of blocking until shutdown — the test/REPL switch. The server dies with the process unless something keeps it alive.
- auth / auth_message
- Tuple or dict of username→password puts a login page in front of the app; auth_message customizes the login text. Cookie sessions, not Basic auth.
- mcp_server=True
- Expose the app's event listeners as MCP tools (since 5.28.0) — your demo becomes callable by LLM agents.
- enable_monitoring=True
- Prints a /monitoring/<key> URL with per-request analytics for the running app.
- quiet=True
- Suppress the startup banner — pairs with prevent_thread_lock=True in scripts that parse the return tuple.
- footer_links
- Which links the app footer shows — each entry must be "api", "gradio", "settings" or "runs". None (the default) shows all four, an empty list shows none. This is the Gradio 6 replacement for show_api=False.
History
2019–2022: from print() to a proper public link
launch() existed from the first PyPI release (gradio 0.1.0, uploaded 2019-02-19) — for its early years it just started a server and printed a URL. The share feature as we know it arrived with 3.13.1 (on PyPI 2022-12-15), whose release notes describe replacing ssh port-forwarding tunnels with frp — the birth of the *.gradio.live link.
2023: Colab makes it famous
gradio 3.46.0 shipped PR #5767 — 'Set share=True for all Gradio apps in Colab by default'. From then on every notebook demo carried a public link, a big reason 'Running on public URL: https://….gradio.live' became one of the most-screenshot console lines in machine learning.
2025–2026: launch() becomes the settings table
Gradio 6.0 (2025-11-21) moved app-wide settings (theme, css, js, head) from the Blocks/Interface constructor into launch(), while earlier 5.x releases had already piled server features onto it: mcp_server since 5.28.0, then enable_monitoring, pwa and i18n. On 6.27.0 the signature counts 48 parameters, 46 of them public.
Under the hood
Under the hood: what launch() actually builds
launch() constructs the FastAPI application (type gradio.routes.App — my unpacked return says so) and mounts /config (the JSON blueprint the Svelte frontend renders from), /login and /logout when auth is set, /gradio_api/info (the machine-readable API index), /monitoring when enabled, plus static asset routes — 22 routes total on a fresh 6.27.0 Interface, counted from demo.app.routes. Event calls flow through the queue over SSE; uvicorn (gradio/http_server.py subclasses uvicorn.Server) serves it all. prevent_thread_lock=True just runs uvicorn in a background thread and hands back the tuple instead of blocking on it.
Fun facts
Pros & cons
pros
- + One call covers dev to demo: local server, LAN binding, public tunnel, login page — no nginx, no ngrok account
- + Returns (app, local_url, share_url) so scripts and tests can grab the URL and the FastAPI app programmatically
- + 46 public parameters mean the deploy-time switches (MCP, monitoring, PWA, theme, i18n) never require re-plumbing your app
cons
- − Param surface is huge and easy to get wrong: prevent_thread_lock's server dies with your process, share links expire in about a week
- − share=True leans on Gradio's tunnel infrastructure and a binary download — great for demos, not a production deployment story
- − Auth is one shared credential set with cookie sessions — fine to keep a demo private, not real user management
Takeaways
- 1Unpack the return: app, local_url, share_url = demo.launch() — you will want all three eventually.
- 2prevent_thread_lock=True for scripts and tests; plain launch() for anything that must stay up.
- 3share=True is the 10-second reviewer demo; auth=('user','pass') is the 10-second privacy switch.
- 4server_name='0.0.0.0' for LAN access — the printed 0.0.0.0 URL is not the address your colleagues type.
- 5Gradio 6 rule: theme, css, js and head go on launch(), not the Blocks constructor.
