OneBitDeploy
Deploying

Deploy a Flask app (Python)

Deploy a Flask or FastAPI app on OneBit Deploy from a GitHub repository — requirements.txt, gunicorn on port 8000, no Dockerfile. Python is in early access.

Python is in early access on OneBit Deploy: add a GitHub repository as an app, we recognise it as A Python app, and we build a python:3.12-slim image, install requirements.txt and start gunicorn on port 8000. No Dockerfile, no platform config file.

This page uses the exact files we run as our own canary — onebit-client/deploy-e2e-flask — so you can fork it or copy the two files below.

The two files

app.py must expose a WSGI callable named app:

app.py
import os

from flask import Flask, jsonify

app = Flask(__name__)


@app.get("/")
def index():
    return jsonify(app="deploy-e2e-flask", framework="flask", runtime="python", port=os.environ.get("PORT", "8000"), ok=True)


@app.get("/health")
def health():
    return "ok", 200


if __name__ == "__main__":  # local dev only; on Deploy gunicorn serves app:app
    app.run(host="0.0.0.0", port=int(os.environ.get("PORT", "8000")))

requirements.txt lists Flask and gunicorn (we install gunicorn if it is missing, but pin your own version so local and production match):

requirements.txt
flask==3.1.*
gunicorn==23.*

That is the whole repository. Push it to GitHub.

Deploy it

  1. In the console, on Apps, press Add app and open GitHub account (press Connect GitHub the first time).
  2. Pick the repository, choose the Branch and press Use this project. We look for requirements.txt (or an installable pyproject.toml) in the root directory, and the console says "A Python app." If it does not, open More settings and set Framework to Python; Port is 8000.
  3. Check the Name and press Deploy. The size is set for you from your plan.
  4. The Python version is 3.12 (3.11 and 3.13 are available) and the start command is empty. Leave the start command empty unless you need a custom entry point; both are in the app's Settings → Build settings → Change how it is built (Runtime version, Start command) and apply on the next update.

The build takes about 30 seconds for a repo this size. When it finishes your app answers on https://<name>-<id>.app.onebit.ng/ with automatic HTTPS.

What runs in the container

Without a start command the image runs:

gunicorn -b 0.0.0.0:8000 --workers 2 --threads 4 --timeout 60 --access-logfile - app:app

The module is picked at build time from the files in your root directory:

FoundModule
manage.py + <project>/wsgi.py<project>.wsgi:application (Django — see Deploy a Django app)
app.pyapp:app
main.pymain:app
wsgi.pywsgi:app

The build log prints the line it chose as Start command: …. PORT is set to the container port (8000 by default) and the process runs as a non-root user.

FastAPI

FastAPI is ASGI, so it needs an ASGI worker. If fastapi is in requirements.txt we install uvicorn[standard] and run gunicorn with -k uvicorn.workers.UvicornWorker against the detected module (main:app for the usual main.py). To be explicit, set the start command instead:

uvicorn main:app --host 0.0.0.0 --port 8000

Custom start command

The Start command field (app Settings → Build settings) becomes the container's CMD verbatim (run through sh -c, as the same non-root user). Use it for a different callable, extra gunicorn flags, or a migration step before the server starts:

gunicorn myapp.server:application -b 0.0.0.0:8000 --workers 4

Environment variables

Add secrets and settings on the app's Environment tab (Add variable); they are injected into the container at runtime (and written to a .env in the build context). Read them with os.environ. PORT is always present. See Environment variables.

Good to know

  • Free plan apps sleep when idle and wake on the next request (the first visit after a quiet period waits for the container to start). Starter and above stay always-on — see Scaling & idle.
  • Dependencies come from requirements.txt first; without one we run pip install . when the repo has a pyproject.toml.
  • Extra build steps (for example pip install -r requirements-prod.txt) go in the Build command under the app's Settings → Build settings → Change how it is built (commands separated by commas); they run after the install as RUN lines.
  • Your own Dockerfile wins. If the repository carries a Dockerfile we build from it and none of the above applies.

Next steps

On this page