A Python app deploys from its repository with no Dockerfile. Nimbus Land installs your dependencies and starts your app with a production server, as long as it can tell which one.
Quick start#
Python apps need a production server. In the console, create an app from your repository with the deploy wizard. From the CLI, run this in the project directory:
nimbus deploy
Before you do, check three things:
- A dependency manifest (
requirements.txt,pyproject.toml,Pipfileorsetup.py) is committed. - A production server is a dependency:
gunicorn,uvicorn,hypercorn,waitress,daphneorgranian. Otherwise add aProcfilewith aweb:line. See what every app needs. - The server listens on
PORT. Gunicorn's--bind 0.0.0.0:$PORTdoes.
FastAPI works with no Procfile. Django needs a few settings; see Django.
Python version and uv#
The Python version comes from .python-version. A uv.lock is installed with uv sync --locked.
Start command#
If there's no Procfile web: line and none of the servers above is among the dependencies, the deploy is blocked before anything builds:
This app has no start command. Add gunicorn (or another server) to requirements.txt, or a Procfile with a web: line.
For a generic app, a Procfile like this works:
web: gunicorn app:app --bind 0.0.0.0:$PORT
See the deploy plan for how verdicts like this appear.
FastAPI#
With no Procfile, Nimbus Land writes the start command: fastapi run main.py when fastapi[standard] or fastapi-cli is a dependency, otherwise uvicorn main:app --host 0.0.0.0 --port $PORT, naming the root file that creates FastAPI(). A Procfile wins, for example web: gunicorn -k uvicorn.workers.UvicornWorker main:app.
Django#
A stock django-admin startproject needs two additions.
A dependency manifest. startproject writes no requirements.txt, and the deploy is refused until you add one:
Django==5.2.17
gunicorn
whitenoise
Production settings. Replace the hard-coded SECRET_KEY, DEBUG and ALLOWED_HOSTS in settings.py with values the platform fills:
import os
SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY", "django-insecure-change-me")
DEBUG = os.environ.get("DJANGO_DEBUG", "True") == "True"
ALLOWED_HOSTS = [h for h in os.environ.get("DJANGO_ALLOWED_HOSTS", "").split(",") if h]
# HTTPS ends at the platform's edge; trust its X-Forwarded-Proto header.
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
# Collected by the build, served by WhiteNoise.
STATIC_ROOT = BASE_DIR / "staticfiles"
Add "whitenoise.middleware.WhiteNoiseMiddleware" to MIDDLEWARE right after SecurityMiddleware, or the admin has no CSS.
What Nimbus Land fills in#
DJANGO_SECRET_KEYis generated when absent and stored on the app, so redeploys reuse it.DJANGO_ALLOWED_HOSTSlists every hostname that reaches the app: thenimbusapps.caaddress, and a verified custom domain with itswwwvariant. It follows your domains as they change, unless you edit it. See Custom domains.DJANGO_DEBUG=Falseis filled when absent.DJANGO_SETTINGS_MODULEcomes from yourmanage.py.- Start command:
gunicorn <project>.wsgiwhengunicornis a dependency and there's noProcfile. - Static files: the build runs
collectstatic. WithoutSTATIC_ROOTit is skipped (Nimbus: STATIC_ROOT is not set; skipping collectstatic), and nothing serves/static/.
Database and migrations#
Use a managed database. The stock SQLite file lives inside the app's image: it starts empty on every release, and one-off commands never see it. Add psycopg[binary] and dj-database-url, and set:
DATABASES = {"default": dj_database_url.config(conn_max_age=600)}
The injected DATABASE_URL starts with postgresql://, so code that checks for postgres:// should accept both.
The deploy wizard suggests python manage.py migrate as the release command for a Django app; you can edit or clear it under Customize. See release command. To run it once, use nimbus run python manage.py migrate. See commands, workers and one-off runs.
Database TLS#
Attaching a managed Postgres database sets DATABASE_URL and DATABASE_CA_CERT. By default, psycopg and other libpq-based drivers connect in libpq's prefer mode, which doesn't verify the database's certificate, and that works as is.
To verify it, write DATABASE_CA_CERT to a file at start-up and connect with sslmode=verify-full sslrootcert=<path>.
Troubleshooting#
For problems that aren't specific to Python, such as an app that never answers, see Apps troubleshooting.
This app has no start command#
The deploy was blocked because no server is a dependency and there's no Procfile. Add a server, or a Procfile with a web: line. See Start command.
Django admin has no CSS, or /static/ returns 404#
STATIC_ROOT isn't set, so the build skipped collectstatic, or WhiteNoise isn't in MIDDLEWARE. Set both as shown under Django and deploy again.
Django: DisallowedHost#
ALLOWED_HOSTS doesn't read DJANGO_ALLOWED_HOSTS. Use the settings shown under Django.
certificate verify failed#
A libpq-based driver set to verify the database's certificate doesn't know its authority. See Database TLS.
Also available as Markdown.