EthioDeploy/Docs
HomePricing
Overview

Overview

  • Getting started
  • How deploys work

Frameworks

  • Framework guides

Configuration

  • Web services & background workers
  • Environment variables
  • Addons
  • Database & Redis tools
  • Branch deployments
  • Custom domains
  • Analytics

Domains

  • Buying a domain

Agent mode

  • Agent mode
  • Agent plans & credit

Account

  • Team access
  • Billing

Help

  • Troubleshooting
  • Still stuck?
Deploy guides
  • Next.js
  • Node.js
  • Python
  • SvelteKit
Compare
  • AI coding agent
  • Hosting in Ethiopia
  • vs Vercel
  • vs Render
  • vs Railway
Deploy guidesPython

Deploy a Python app

FastAPI, Flask and Django deploy from requirements.txt or pyproject.toml. Set a start command that binds 0.0.0.0:$PORT, and for Django, add the domain to your settings.

Last updated September 29, 2026

Detected by
requirements.txt, pyproject.toml, Pipfile or main.py
Install
pip install -r requirements.txt (or poetry / uv / pdm)
Build
none by default
Start
Django: migrate + gunicorn. Others: python main.py

These are the defaults. Override any of them in Settings → Build.

Set a start command that binds 0.0.0.0:$PORT

Only Django gets a start command automatically. For FastAPI and Flask, set the Start command in Settings → Build so the server binds 0.0.0.0 on $PORT. A server on 127.0.0.1 or a fixed port fails the health check.

Prepare your app

List your dependencies in requirements.txt or pyproject.toml (Poetry, uv and PDM work too). We install them into an isolated environment. To pin the Python version, add a .python-version file or set the env var NIXPACKS_PYTHON_VERSION.

FastAPI

uvicorn app.main:app --host 0.0.0.0 --port $PORT --proxy-headers

Flask

gunicorn -b 0.0.0.0:$PORT app:app

Django

Django is detected via manage.py and starts with python manage.py migrate && gunicorn <project>.wsgi automatically. Add gunicorn to your requirements. Then update settings.py for production:

import os

SECRET_KEY = os.environ["SECRET_KEY"]
DEBUG = False
ALLOWED_HOSTS = ["<project-slug>.ethiodeploy.com", "yourdomain.com"]
CSRF_TRUSTED_ORIGINS = ["https://<project-slug>.ethiodeploy.com"]
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
STATIC_ROOT = BASE_DIR / "staticfiles"

CSRF_TRUSTED_ORIGINS entries must include the https:// scheme. Nothing runs collectstatic for you: set the Build command to python manage.py collectstatic --noinput and serve files with WhiteNoise (add it to MIDDLEWARE right after SecurityMiddleware).

Environment variables

Add variables in the project's settings. They're available both while the app builds and while it runs, so build-time values (like framework-public keys) work without extra setup. See Environment variables. Read them with os.environ["NAME"]. Set SECRET_KEY for Django.

Add a database

Add a Postgres addon and DATABASE_URL is injected automatically.

# Django (pip install dj-database-url)
import dj_database_url
DATABASES = {"default": dj_database_url.config(conn_max_age=600)}

# SQLAlchemy
engine = create_engine(os.environ["DATABASE_URL"])

Django runs migrate on every start, which is fine for a single instance. For Alembic, run alembic upgrade head before your server command, for example alembic upgrade head && uvicorn ….

Deploy

  1. Sign in with GitHub and install the EthioDeploy app on the repo.
  2. Click “New Project” and pick the repo.
  3. Watch the build in the dashboard. When the health check passes, your app is live at <project-slug>.ethiodeploy.com. Every push to the default branch redeploys, and the old version keeps serving until the new one is healthy.

Troubleshooting

App exits immediately or the health check fails

Cause: No start command, or the server binds 127.0.0.1 / a fixed port.

Fix: Set the Start command to bind 0.0.0.0:$PORT.

Django: DisallowedHost (400)

Cause: Your domain isn't in ALLOWED_HOSTS.

Fix: Add both the ethiodeploy.com subdomain and any custom domain.

Django: CSRF verification failed (403)

Cause: CSRF_TRUSTED_ORIGINS is missing the https origin, or the proxy header isn't trusted.

Fix: Add the full https://… origin and set SECURE_PROXY_SSL_HEADER.

Django admin has no styling

Cause: collectstatic never ran and nothing serves the files.

Fix: Run collectstatic in the Build command and add WhiteNoise.

Build fails installing a package (psycopg, Pillow…)

Cause: The package needs system libraries or a pinned Python version.

Fix: Use psycopg[binary], or set NIXPACKS_APT_PKGS for the libs you need.

Wrong Python version

Cause: The default Python doesn't match your app.

Fix: Add .python-version or set NIXPACKS_PYTHON_VERSION.

Still stuck? Check the deployment's build log in the dashboard, or see Troubleshooting.

Ready to deploy? Sign in with GitHub and pick your repo. No Dockerfile, no YAML.

Other framework guides

Next.jsNode.jsSvelteKit

See the full docs for custom domains, env vars, addons, and billing.

On this page

  • Prepare your app
  • Environment variables
  • Add a database
  • Deploy
  • Troubleshooting

Product

  • Pricing
  • Documentation
  • AI coding agent
  • Agent mode docs
  • Domains
  • Hosting in Ethiopia

Deploy guides

  • Next.js
  • Node.js
  • Python
  • SvelteKit

Compare

  • EthioDeploy vs Vercel
  • EthioDeploy vs Render
  • EthioDeploy vs Railway

Company

  • About
  • Support
  • Contact
  • Terms
  • Privacy

EthioDeploy: agentic first development and deployment, started in Ethiopia, built for developers everywhere.