Python
Deploy Django, FastAPI or Flask. We pick Python 3.10 to 3.14 from your code, install with pip, uv, Poetry or Pipenv, and start your app on port 8080.
Updated 17 Oct 20263 min read
A Python app deploys from its requirements.txt, pyproject.toml or Pipfile. We choose Python, install your packages into the app's own virtual environment, and start Django, FastAPI or Flask for you. Your app listens on the port in the PORT variable, which is always 8080.
Versions
| Version | Notes |
|---|---|
| Python 3.14 | Supported |
| Python 3.13 | What new apps get when the code doesn't ask |
| Python 3.12 | Supported |
| Python 3.11 | Supported |
| Python 3.10 | No more security fixes from its makers: it still builds, with a warning |
We read the version from .python-version, then runtime.txt (python-3.12), then requires-python in pyproject.toml, and pick the newest supported version that meets it. requires-python = ">=3.11" builds with 3.14. To stay on one version, write it in .python-version (3.12), choose it in the app's Build and run settings, or set "version" in tanzahost.json.
How packages are installed
| Your code has | We run |
|---|---|
uv.lock | uv sync --frozen --no-dev |
poetry.lock | poetry install --only main |
Pipfile | Pipenv's requirements, installed with pip |
requirements.txt | pip install -r requirements.txt |
Only pyproject.toml or setup.py | pip install . |
Everything goes into a virtual environment that's already active for the build, the app, its workers and its scheduled tasks. PYTHONUNBUFFERED=1 is set, so what you print shows in your logs at once.
How your app starts
| Framework | Start command |
|---|---|
Django, with gunicorn in your packages | gunicorn yourproject.wsgi:application --bind 0.0.0.0:${PORT} |
| Django, without it | python manage.py runserver 0.0.0.0:${PORT} |
| FastAPI | uvicorn main:app --host 0.0.0.0 --port ${PORT} |
Flask, with gunicorn | gunicorn main:app --bind 0.0.0.0:${PORT} |
| Flask, without it | flask --app main:app run --host 0.0.0.0 --port ${PORT} |
| Anything else | python main.py, app.py or server.py |
For Django we read your project's name from manage.py. For FastAPI and Flask we look for the line that makes the app (app = FastAPI()) in main.py, app.py, server.py or api.py.
Django's and Flask's own servers are for development. Add gunicorn to your packages and we start it instead.
Example: a Django shop
Asha's friend moves his Django shop, project shop, to Tanzahost. His repository has manage.py, requirements.txt with Django and gunicorn, and .python-version with 3.12. We find Django on Python 3.12, run pip install -r requirements.txt, and start:
gunicorn shop.wsgi:application --bind 0.0.0.0:${PORT}Then he sets his variables, like SECRET_KEY, DEBUG=False and ALLOWED_HOSTS, under the project's Variables tab. Attaching a PostgreSQL database adds DATABASE_URL for him. See Connect an app to a database.
Before a new version gets visitors, our health check asks it for its health address at the app's private address, not at your domain. Django answers that with an error (400) when ALLOWED_HOSTS lists only your domains, so the new version never goes live. Until we change this, either set ALLOWED_HOSTS to *, or keep your list of addresses and give the health check a path of its own, as DisallowedHost shows.
To run Django's migrations before each new version gets visitors, he sets Before going live in Build and run to:
python manage.py migrate --noinputIf it fails, the deploy stops and the version before keeps serving. See Deploying.
Static files
Django doesn't serve its own static files with DEBUG=False. The simplest way is WhiteNoise in your packages, with collectstatic as your build command, like python manage.py collectstatic --noinput, set in Build and run.
Workers and tasks
Celery, RQ or any long-running script runs as a worker, like celery -A shop worker. Jobs on a timetable run as scheduled tasks, like python manage.py clearsessions every night.