How to run a Gradio demo in a sandbox instead of a share link
Build Gradio and your app into an image whose start command runs it on port 7860, create a sandbox from it, and share 7860 as a preview.
On Runtime the demo's address lasts as long as you keep the sandbox, and it can be private. A Gradio share link expires after a week and is open to anyone who has it. A Runtime preview is refused without its token unless you make it public, stays up for as long as you keep the sandbox, and on a paid account can sit at your own domain. Gradio 6.28.0 was current on 25 September 2026.
Gradio's share link or a Runtime preview
| Question | share=True |
Runtime preview |
|---|---|---|
| Where the app runs | Your machine, or wherever launch() runs |
A Firecracker microVM of its own |
| How long the link lasts | Expires after 1 week | For as long as the sandbox and its preview exist |
| Who can open it | Anyone with the link | Token holders by default, or anyone when public |
| Traffic goes through | Gradio's share servers, as a proxy | Runtime's preview edge |
Build the demo into an image
An image can say what a sandbox runs when it starts and when it counts as
ready. With readyPort: 7860, the create call answers once Gradio is
listening, so the link works the moment you have it:
dockerfileFROM runtimeENV GRADIO_ANALYTICS_ENABLED=FalseRUN pip install --no-cache-dir gradio==6.28.0COPY app.py /workspace/app.pyRUN chown 1000:1000 /workspace/app.pyTypeScriptimport { readFile } from "node:fs/promises";import { Runtime } from "withruntime";const runtime = new Runtime();await runtime.images.build( { name: "shout-demo", dockerfile: await readFile("demo/Dockerfile", "utf8"), contextDir: "demo", start: { command: "python3 /workspace/app.py", readyPort: 7860, readyTimeoutSeconds: 120 }, }, { onLog: (line) => console.log(line.text) },);const sbx = await runtime.sandboxes.create({ image: "shout-demo", timeoutSeconds: 3600 });console.log(sbx.info.start); // { state: "ready", ... } once Gradio listensconst preview = await sbx.previews.create(7860);console.log(preview.urlWithToken);Pythonfrom withruntime import Runtimeruntime = Runtime()with open("demo/Dockerfile") as handle: runtime.images.build( name="shout-demo", dockerfile=handle.read(), context_dir="demo", start={"command": "python3 /workspace/app.py", "ready_port": 7860, "ready_timeout_seconds": 120}, )sbx = runtime.sandboxes.create(image="shout-demo", timeout_seconds=3600)print(sbx.info["start"])print(sbx.previews.create(7860)["urlWithToken"])The app itself is ordinary Gradio, with launch() and no share:
Pythonimport gradio as grdef shout(text: str) -> str: return text.upper()demo = gr.Interface(fn=shout, inputs="text", outputs="text")demo.launch()Gradio listens on 127.0.0.1:7860 by default, which a preview reaches from
inside the sandbox. GRADIO_SERVER_PORT moves the port, and
GRADIO_ANALYTICS_ENABLED=False in the image stops Gradio's analytics.
Call the demo from code
Every Gradio app has an HTTP API as well as its page. A private preview takes
the token as a header, so a test or an agent can call the demo directly. The
endpoint is named after the function: shout here.
TypeScriptimport { Sandbox } from "withruntime";const sbx = await Sandbox.connect(process.env.SANDBOX_ID ?? "");const preview = await sbx.previews.create(7860);const headers = { "x-runtime-preview-token": preview.token!, "content-type": "application/json" };const queued = await fetch(new URL("gradio_api/call/shout", preview.url), { method: "POST", headers, body: JSON.stringify({ data: ["hello"] }),});const { event_id } = (await queued.json()) as { event_id: string };const result = await fetch(new URL(`gradio_api/call/shout/${event_id}`, preview.url), { headers });console.log(await result.text()); // event: complete / data: ["HELLO"]The first call queues the request and answers with an event_id; the second
streams the result as server-sent events. In a test on 25 September 2026,
Gradio 6.28.0 answered data: ["HI"] for the input hi.
Make it public, or keep it to reviewers
| Want | Do |
|---|---|
| Only you | Open urlWithToken once in your browser |
| A reviewer | Send them a fresh urlWithToken |
| Anyone | previews.create(7860, { visibility: "public" }) |
| Cut off every link handed out so far | previews.rotate(7860) |
| A login inside the app as well | demo.launch(auth=("admin", password)) |
| Your own hostname | A custom domain on a paid account (networking) |
Models and memory
A demo that loads a model at start needs the memory for it and the disk for
its files. Ask for them at create, memoryMiB and diskMiB, and give the
image's ready check time with readyTimeoutSeconds, which goes up to 300. For
Hugging Face models on CPU, see Transformers in a sandbox.
A sandbox's lease runs for its timeoutSeconds, at most an hour ahead, and
then pauses it with the app still in memory. extend moves the lease, and a
visit to the preview wakes a paused sandbox with a short "Waking up" page.
Related
- Host a Streamlit app from a sandbox
- Hugging Face Transformers on CPU
- Preview apps an AI agent builds
- Custom images
Sources
Checked 25 September 2026.
- Sharing your app: share
links expire after 1 week, the share servers act as a proxy, and
auth= - Gradio environment variables:
GRADIO_SERVER_PORT(7860),GRADIO_SERVER_NAME(127.0.0.1),GRADIO_ANALYTICS_ENABLED - gradio on PyPI: version 6.28.0, Python 3.10 or later
- A local test of Gradio 6.28.0 on 25 September 2026:
POST /gradio_api/call/shoutreturned anevent_id, andGET /gradio_api/call/shout/<event_id>returnedevent: completewithdata: ["HI"]
Facts on this page were checked on 25 September 2026.