Skip to content
Dashboard
Quickstarts 5 min read

Python Quickstart

Deploy a Python HTTP application as a NexHost Web Service.

Python Quickstart#

Use a Web Service for a Python HTTP application. NexHost waits for a reachable TCP listener, so the application must start a persistent server bound to 0.0.0.0; the platform discovers the listener and assigns ingress automatically.

This guide uses Flask for the example and notes the matching FastAPI variant. The contract is the same for both: bind to 0.0.0.0 and keep the process alive. A fixed internal port is safe because containers are isolated.

When to use this vs a different service#

Use Web Service (this guide) when:

  • The deliverable is an HTTP server that should answer browser, mobile, or webhook callers over a public hostname.
  • You need readiness gating: callers should only reach the service once it has proved it can serve requests.

Do not use Web Service when:

  • The process should be reachable only from inside the workspace — choose Private Service.
  • The process is a persistent asynchronous consumer with no HTTP — choose Background Worker.
  • The process should run on a schedule and exit — choose Cron Job.

Before you begin#

  • Python 3.10+ locally so you can verify the server starts and opens a listener before deploying.
  • A fresh directory for the project, or an existing service directory inside a monorepo.
  • A workspace and permission to create a project.

Create a Flask application#

In an empty directory, create app.py:

python
import os
from flask import Flask

app = Flask(__name__)

@app.get("/")
def home():
    return "Hello from NexHost"

@app.get("/health")
def health():
    return {"ok": True}

if __name__ == "__main__":
    # NexHost supplies PORT=3000 as a compatibility default.
    app.run(host="0.0.0.0", port=int(os.environ["PORT"]))

Why this shape works on the platform:

  • os.environ["PORT"] reads NexHost's compatibility default. A fixed internal port is also discoverable.
  • host="0.0.0.0" binds to the container interface, not only to loopback. Binding only to 127.0.0.1 makes TCP readiness observe "connection refused" even when your direct localhost test passed.
  • /health is optional application monitoring; NexHost deployment readiness is based on TCP reachability.

Create requirements.txt in the same directory:

text
Flask
gunicorn

Flask is the framework; gunicorn is the production WSGI server you will actually run. TCP readiness verifies that the running gunicorn workers opened a listener, not just that the file imports correctly.

Tip

Test locally before you deploy:

```bash

python -m venv .venv && source .venv/bin/activate

pip install -r requirements.txt

PORT=3000 python app.py &

curl -i http://127.0.0.1:3000/health

# expect 200 with {"ok": true} quickly and without authentication

```

Create a FastAPI variant (optional)#

If you prefer FastAPI, the same readiness contract applies — only the runner changes:

python
# app.py
import os
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def home():
    return {"message": "Hello from NexHost"}

@app.get("/health")
def health():
    return {"ok": True}

requirements.txt:

text
fastapi
uvicorn

The platform-observed difference is only the start command: Flask uses gunicorn, FastAPI uses uvicorn.

Configure the service#

  1. Select Web Service in New Project — not Frontend App and not Private Service.
  2. Connect the repository or archive containing app.py and requirements.txt. Verify the branch and root directory when the dashboard shows them.
  3. Set Build command to pip install -r requirements.txt. If your project lives in a subdirectory of a monorepo, point the root directory there first so this path resolves.
  4. Set Start command to gunicorn app:app --bind 0.0.0.0:$PORT. This keeps workers alive and routes them at the injected port.
  5. Deploy. The deployment detail page will show source preparation, dependency install, build (which is this pip install), launch, and TCP readiness — in that order.

For FastAPI, use a comparable uvicorn start command that sets --host 0.0.0.0 and --port $PORT:

bash
uvicorn app:app --host 0.0.0.0 --port $PORT

The flags look slightly different (--bind vs --host/--port) but the meaning is identical: read the injected PORT, bind to the correct interface, stay alive.

What success looks like#

  • The deployment status becomes successful and the detail page shows a populated PORT-aware log line rather than a hard-coded startup announcement.
  • The dashboard shows a generated hostname for the service. Opening https://<hostname>/health returns {"ok": true} quickly. Opening https://<hostname>/ returns the greeting.
  • A fresh deployment after updating only an environment variable produces the updated /health response with the new value inlined to the configuration — no code edit was required.

Troubleshooting#

SymptomCheck
Startup readiness times outIs gunicorn/uvicorn the start command rather than python app.py without gunicorn? Does the server reach its listen call after initialization?
Connection refusedThe container bound only to 127.0.0.1. Bind to 0.0.0.0.
"Module not found: app"The service’s root directory does not contain app.py, or requirements.txt referenced the wrong package.
Build passes but the process exits immediatelyThe start command built but did not keep a worker pool alive — pip install … belongs in Build; the gunicorn line belongs in Start.

See Deployment Troubleshooting and Logs for broader diagnosis.