# 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: ```dockerfile no-run FROM runtime ENV GRADIO_ANALYTICS_ENABLED=False RUN pip install --no-cache-dir gradio==6.28.0 COPY app.py /workspace/app.py RUN chown 1000:1000 /workspace/app.py ``` ```ts check import { 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 listens const preview = await sbx.previews.create(7860); console.log(preview.urlWithToken); ``` ```python check from withruntime import Runtime runtime = 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`: ```python no-run import gradio as gr def 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. ```ts check import { 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](/docs/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](/integrations/huggingface-transformers). 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](/integrations/streamlit) - [Hugging Face Transformers on CPU](/integrations/huggingface-transformers) - [Preview apps an AI agent builds](/use-cases/preview-agent-built-apps) - [Custom images](/docs/images) ## Sources Checked 25 September 2026. - [Sharing your app](https://www.gradio.app/guides/sharing-your-app): share links expire after 1 week, the share servers act as a proxy, and `auth=` - [Gradio environment variables](https://www.gradio.app/guides/environment-variables): `GRADIO_SERVER_PORT` (7860), `GRADIO_SERVER_NAME` (`127.0.0.1`), `GRADIO_ANALYTICS_ENABLED` - [gradio on PyPI](https://pypi.org/project/gradio/): 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/shout` returned an `event_id`, and `GET /gradio_api/call/shout/` returned `event: complete` with `data: ["HI"]` Facts on this page were checked on 25 September 2026.