Deploy a Python app on Renode
How a RE:NODE Python server runs your code: the entry file on the Startup tab, requirements.txt, binding 0.0.0.0 on SERVER_PORT with a production server, and the errors first deploys hit.
All docs pages
A Python server runs one file from /home/container: the entry file named on the server's Startup tab. Before it starts, it installs requirements.txt when your repository has one.
How it starts#
- The branch is fetched into
/home/container(on every start, or only the first - see the overview). requirements.txtat the repository root is installed withpip. Packages go into the user's site directory, so nothing needs root.- The entry file named on the Startup tab (often the
PY_FILEvariable) is run.
When you link a repository, the panel points the Startup tab at the first of main.py, bot.py, app.py, run.py or __main__.py that exists. Any other name works too - set it on the Startup tab.
Two things that are not read:
- `pyproject.toml` dependencies. Only
requirements.txtis installed. Export them:pip freeze > requirements.txt, oruv pip compile pyproject.toml -o requirements.txt. - A `Procfile`. The server runs the entry file, not the command in a
Procfile.
A minimal web app#
Start a production server from the entry file itself, bound to 0.0.0.0 and the port in SERVER_PORT. With FastAPI:
fastapi==0.115.0uvicorn==0.30.6import osimport uvicornfrom fastapi import FastAPIapp = FastAPI()@app.get("/")def home(): return {"hello": "renode"}if __name__ == "__main__": # The server's port. Never hardcode one, never bind 127.0.0.1. uvicorn.run(app, host="0.0.0.0", port=int(os.environ["SERVER_PORT"]))With Flask, use a production WSGI server rather than app.run(), which is Flask's development server:
flask==3.0.3waitress==3.0.0import osfrom flask import Flaskfrom waitress import serveapp = Flask(__name__)@app.get("/")def home(): return "Hello from Renode"if __name__ == "__main__": serve(app, host="0.0.0.0", port=int(os.environ["SERVER_PORT"]))Django works the same way: start waitress (or another production server) from the entry file, bound to 0.0.0.0 and SERVER_PORT, and add the domain you serve it on to ALLOWED_HOSTS.
Configuration and .env#
Read configuration from the environment. Upload .env through the Files tab into /home/container, or set variables on the Startup tab, and load the file with python-dotenv:
import osfrom dotenv import load_dotenvload_dotenv()TOKEN = os.environ.get("API_TOKEN")if not TOKEN: raise SystemExit("API_TOKEN is not set - upload .env through the Files tab")Logging#
The console shows what your process writes to stdout. Python buffers it when it is not attached to a terminal, so output can arrive late or not at all before a crash. Print with flush=True, or set PYTHONUNBUFFERED=1.
System packages#
apt and other system package managers are not available in the container. Choose packages that ship wheels for Linux rather than ones that compile against system libraries at install time.
Checklist#
- The entry file on the Startup tab exists in the repository.
requirements.txtis at the repository root, with pinned versions.- The app binds
0.0.0.0andint(os.environ["SERVER_PORT"]), with a production server. - No password, token or key is committed;
.envis in.gitignore. - Output is unbuffered so the console shows it.
- Files the app writes at run time go under
/home/container.
Common errors#
`ModuleNotFoundError` on start. The package is not in requirements.txt, or it is declared only in pyproject.toml.
`can't open file 'main.py'`. The Startup tab names a file the branch does not have. Set the entry file to the script that starts your app.
Running, but the address does not answer. The app is bound to 127.0.0.1, or to a hardcoded port such as 5000 or 8000.
Nothing appears in the console. Output is buffered - see Logging above.
More in Troubleshooting.
Questions this does not answer? Open a ticket from the panel, or start from the overview. These pages are also available as plain text for AI assistants at /llms.txt.