Python apps
Deploy a Streamlit, Gradio or FastAPI app only your team can open
Updated
Run jiayang deploy in the app's folder. It detects Streamlit, Gradio, FastAPI, Flask or Django, builds a container and puts it behind sign-in, so only the people you share it with can open it, with no login code in your app. Container apps need the Team plan or above.
What you need
- The jiayang CLI, signed in with
jiayang login. - Docker running on your computer. The image is built there, for linux/amd64.
- A workspace on Team, which runs one container app, or Business, which runs four. See pricing.
Deploy it
In the app's folder:
jiayang deploy acme/report . --create
The first lines say what it found and how the app will start. Shortened, it looks like this:
a container (Streamlit)
because it has requirements.txt, runs as a python container
start: streamlit run app.py --server.port $PORT --server.address 0.0.0.0 --server.headless true
python: 3.13
Building . for linux/amd64 ...
Image uploaded. Waiting for it to go live ...
It listens on port 8080 and runs at size basic.
Version 1 (7c3a91e0d5f2) is live.
https://report--acme.jiayang-apps.cloud
With no Dockerfile of yours, the deploy writes one: Python 3.13 unless .python-version or runtime.txt asks for 3.11 or 3.12, installed with uv, Poetry or pip from your lock file, requirements.txt or pyproject.toml. jiayang dockerfile prints it. The start command comes from what the project depends on:
| Depends on | Starts with |
|---|---|
streamlit | streamlit run app.py, or main.py or streamlit_app.py |
gradio | python app.py |
fastapi | uvicorn main:app |
django | gunicorn <project>.wsgi:application |
flask | gunicorn app:app |
Set your own in jiayang.json as container.start. See Containers and How deploy decides what to build. To start from nothing, jiayang init fastapi writes a FastAPI app that already checks who is calling.
Share it
jiayang share acme/report sam@example.com
jiayang share acme/report @example.com
The first lets one person in, and the second everyone at your company's domain. People sign in with a code sent to their address, or with Google or Microsoft, and don't need an account first. Remove someone and they lose access within 60 seconds, open connections included.
Know who's using it
Every request reaches your app with a signed token saying who is calling. The jiayang package checks it, with an extra for each framework:
pip install "jiayang[streamlit]" # or [gradio], [fastapi], [flask], [django]
Streamlit
import streamlit as st
from jiayang.streamlit import get_user
user = get_user()
if user is None:
st.error("sign in to use this")
st.stop()
st.write(f"hello {user.email}")
The caller is checked once per session. On Jiayang Cloud the edge closes the connection within a minute of someone's access being removed, and the session ends with it.
Gradio
import os
import gradio as gr
from jiayang.gradio import require_user
def answer(question, request: gr.Request):
user = require_user(request)
return f"{user.email} asked: {question}"
gr.Interface(answer, "textbox", "textbox").launch(
server_name="0.0.0.0", server_port=int(os.environ.get("PORT", "7860"))
)
The app has to listen on 0.0.0.0 at the port in PORT. Gradio's defaults can't be reached here.
FastAPI
from jiayang.fastapi import CurrentUser
@app.get("/")
async def index(user: CurrentUser):
return {"hello": user.email}
A request without a valid caller gets a 401 and never reaches your handler. requires("editor") asks for a role too. Flask and Django have the same in jiayang.flask and jiayang.django. See Verify the caller in a Python app.
jiayang dev runs any of these on your computer behind a copy of our sign-in, with real tokens, so you can try each role before you deploy.
Why not a share link or a password?
Gradio's share=True link is public: anyone who has it can use the app, and it lasts a week (Gradio's guide). A shared password gets passed around, and tells the app nothing about who is using it. Here each person signs in as themselves, you can take one person's access away without changing anything for the rest, and every visit is in the audit log with their name.
Streamlit Community Cloud allows one private app at a time, with viewers invited by email who sign in with Google or a single-use emailed link (Streamlit's docs). If that covers you, it's a fine choice. Jiayang Cloud is for more than one private app, sharing with a whole domain, and an audit log.
Questions
Is there a free option?
Not for Python servers. Free runs JavaScript, TypeScript and static apps only, and a container deploy on Free stops before it builds anything, with a message that says so.
What does the first request get while the container starts?
A 503 that says the app is starting. A browser gets a page with the same message, which reloads itself until the app answers. After an app's first deploy this can take several minutes. A container stops after 10 minutes with no requests and starts again on the next one.
Where does my data go between restarts?
Not on the container's disk, which goes back to what the image holds each time the container starts. Keep it in the app's database or files, or a Postgres database you connect.
How do I keep an API key out of the code?
Store it as a secret for the API's host with jiayang secret set. Our outbound proxy adds it to the app's requests to that host, so the code calls the API without it. See Secrets.
Sources
- Sharing your app, Gradio's guide. Checked 16 October 2026.
- Share your app, Streamlit's docs. Checked 16 October 2026.
Next
- Containers, in the docs
- The Python SDK
- Pricing