Python
Django, FastAPI and Flask, what Marina starts for each, and the binding mistake that hides an app.
Marina builds a Python app from a requirements.txt, pyproject.toml or Pipfile, or from a recognisable entry point at the root (main.py, app.py, server.py and a few others). pip, Poetry, PDM, uv and Pipenv all work; commit the lockfile.
marina vessels create api --repo acme/api The start command
Marina recognises three frameworks and starts each the way it expects:
| Framework | What runs |
|---|---|
| Django | manage.py migrate, then gunicorn against your WSGI app. |
| FastAPI | uvicorn on main:app, bound to 0.0.0.0 and PORT. |
| Flask | gunicorn on main:app, bound to 0.0.0.0 and PORT. |
Those defaults assume the conventional layout. If your ASGI app is not main:app, or you want workers or a different server, declare it in marina.toml:
[processes]
web = "uvicorn app.api:app --host 0.0.0.0 --port $PORT --workers 2" Anything else starts the entry-point file directly.
The port
Marina sets PORT and routes to it. Bind that, on 0.0.0.0.
uvicorn.run(app, host="0.0.0.0", port=int(os.environ["PORT"])) Binding 127.0.0.1 is the mistake that costs the most time here, because everything looks right: the build passes, the process starts, the logs are clean, and every request times out. 0.0.0.0 or nothing.
The Python version
From .python-version, .tool-versions, or runtime.txt. Without one you get a recent 3.x that will move under you eventually, so pin it.
3.12 Django, specifically
Two things are yours to handle.
Static files. Nothing serves your static/ directory for you. Run collectstatic as part of the build and serve them from the app; whitenoise is the usual answer and needs no extra service.
Migrations run at start. The default start command migrates and then serves, which is what you want on a first deploy and often not what you want later, when two versions are briefly serving at once. To take that back, declare a web without it and run migrations when you choose:
[processes]
web = "gunicorn myproject.wsgi:application --bind 0.0.0.0:$PORT" marina run -- python manage.py migrate ALLOWED_HOSTS has to include the address Marina gave you, and your own domain once you attach one.
