Skip to content
Docs

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

VersionNotes
Python 3.14Supported
Python 3.13What new apps get when the code doesn't ask
Python 3.12Supported
Python 3.11Supported
Python 3.10No 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 hasWe run
uv.lockuv sync --frozen --no-dev
poetry.lockpoetry install --only main
PipfilePipenv's requirements, installed with pip
requirements.txtpip install -r requirements.txt
Only pyproject.toml or setup.pypip 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

FrameworkStart command
Django, with gunicorn in your packagesgunicorn yourproject.wsgi:application --bind 0.0.0.0:${PORT}
Django, without itpython manage.py runserver 0.0.0.0:${PORT}
FastAPIuvicorn main:app --host 0.0.0.0 --port ${PORT}
Flask, with gunicorngunicorn main:app --bind 0.0.0.0:${PORT}
Flask, without itflask --app main:app run --host 0.0.0.0 --port ${PORT}
Anything elsepython 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.

Add gunicorn for Django and Flask

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:

Terminal
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.

Django's ALLOWED_HOSTS and the health check

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:

Terminal
python manage.py migrate --noinput

If 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.