---
title: Python web apps (FastAPI, Flask, Django)
description: Deploy FastAPI, Flask and Django apps on ZevCloud. The start command your app needs, the port it should listen on, and the environment variables to carry over.
---

A FastAPI, Flask or Django project is not a server on its own. Your code defines an app object, and an **app server** runs it and listens for requests: [uvicorn](https://www.uvicorn.org) for FastAPI, [gunicorn](https://gunicorn.org) for Flask and Django.

If a Python service has no start command, the build falls back to running your main file, for example `python main.py`. That imports your app, defines it, and exits, because nothing started a server. The deploy succeeds, then the app is stopped for crashing repeatedly with nothing in its logs. The start command is the fix.

## Settings at a glance

| Framework | Start command | Port |
| --- | --- | --- |
| FastAPI (`main.py`) | `uvicorn main:app --host 0.0.0.0 --port 8000` | `8000` |
| FastAPI (`app/main.py`) | `uvicorn app.main:app --host 0.0.0.0 --port 8000` | `8000` |
| Flask (`app.py`) | `gunicorn --bind 0.0.0.0:8000 app:app` | `8000` |
| Django (project `mysite`) | `gunicorn mysite.wsgi --bind 0.0.0.0:8000` | `8000` |

`main:app` means "the `app` object in `main.py`". If your app object or file has another name, change that part, for example `uvicorn api.server:application --host 0.0.0.0 --port 8000`.

Make sure the server is in your dependencies: add `uvicorn` (FastAPI) or `gunicorn` (Flask, Django) to `requirements.txt` or `pyproject.toml`.

## Start command

When ZevCloud detects FastAPI, Flask or Django and you leave the start command empty, it fills one in for you:

- **On create**, the recommended command is shown in the **Start Command** field, already filled in. It reads your repository to pick `main.py` or `app/main.py` for FastAPI, `app.py` or `wsgi.py` for Flask, and your project folder for Django. You can edit it before you create the service.
- **On deploy**, a Python service with no start command gets the same command before it builds. It's saved on the service, so you'll see it in **Settings → Build & Deploy**, and the **Setup check** on the Overview tab says it was set for you.

ZevCloud never replaces a start command you set. Clear it and save to get the recommended one back on the next deploy.

The start command can't use `$PORT` or other shell variables, so write the port number itself. If you change the service's port, change the number in the start command too. A command ZevCloud filled in follows the port for you.

## Port

ZevCloud routes requests to the port in **Settings → Build & Deploy → Port** (`8000` for Python apps by default), and sets the `PORT` environment variable to the same number.

Your app has to listen on that port and on all interfaces (`0.0.0.0`). A server bound to `127.0.0.1` or `localhost` only accepts connections from inside its own container, so requests never reach it. The **Setup check** warns you when the port in your start command doesn't match the service's port.

## Environment variables

Settings that differ between your laptop and production live in environment variables: database URLs, API keys, and allowed origins such as `CORS_ORIGINS`. Set them on the **Variables** tab, then redeploy. Variables reach your app on the next deploy.

A FastAPI app often reads its allowed origins like this:

```python
import os
from fastapi.middleware.cors import CORSMiddleware

origins = os.getenv("CORS_ORIGINS", "").split(",")
app.add_middleware(CORSMiddleware, allow_origins=origins, allow_methods=["*"], allow_headers=["*"])
```

If `CORS_ORIGINS` isn't set, the list is empty and every browser request from your frontend is refused. Set it to your frontend's address, for example `https://myapp.zevcloud.app,https://www.example.com`.

ZevCloud reads your code for `os.getenv("...")`, `os.environ["..."]`, `os.environ.get("...")`, pydantic `BaseSettings` fields and the keys in `.env.example`, and lists the ones that aren't set:

- on the create page, so you can fill them in before the first deploy;
- in the **Setup check** on the service's Overview tab, each with an **Add** button.

Variables your code reads with a default (`os.getenv("DEBUG", "false")`) are listed as optional. `PORT` is set for you.

## Using an AI coding assistant?

> **Tip: Copy this prompt into your AI assistant**
>
> If an AI tool (Cursor, Claude, ChatGPT, Lovable, Bolt and similar) wrote your backend, it may run the app with `python main.py` or bind it to localhost. Give it this prompt:
>
> ```text
> This Python web app deploys to ZevCloud. Make sure it runs under an app server, not `python main.py`: for FastAPI use `uvicorn main:app --host 0.0.0.0 --port 8000`, for Flask `gunicorn --bind 0.0.0.0:8000 app:app`, for Django `gunicorn <project>.wsgi --bind 0.0.0.0:8000` (adjust the module path to where the app object actually lives). Add uvicorn or gunicorn to requirements.txt. Read every setting that differs between environments (database URL, secrets, CORS_ORIGINS and other allowed origins) from environment variables, list them all in a .env.example file with placeholder values, and tell me the exact start command to paste into the service settings.
> ```

## Related

- [Troubleshooting: app loads and exits (Python)](/guide/troubleshooting#app-loads-and-exits-python)
- [Troubleshooting: moving an app to another account or team](/guide/troubleshooting#moving-an-app-to-another-account-or-team-copy-start-command-and-variables)
- [Environment Variables](/guide/env-vars)
- [Web Applications: build configuration](/guide/web-apps#build-configuration)
