BRYME TECH
SEPTEMBER 2026 · THE TOOL DESKPractical technology. No theatre.
THE BRYME

First-hand · verified against the real thing

How to deploy a Python app (the first-hand Render version)

In one line: A minimal Flask app, the three files that matter, secrets as environment variables, and the deploy pattern this very site uses in production — written by the person who runs it.

This site runs on Render, deployed by pushing a repository — and the same push-to-deploy pattern carries a Python app with almost no ceremony. This is the first-hand version: the three files that matter, the mistakes that cost time, and the config that works. (The static-site sibling of this story — how this very publication deploys — is in the Render static deploy walkthrough.)

The smallest honest app

A Python web app needs a framework entry point. Flask is the conventional first one — small, readable, installable in one line (pip install flask):

from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return {"status": "ok", "service": "bryme-demo"}

Run it locally with flask --app app run and visit the printed address — you should see the JSON. That round-trip is the whole loop you are automating: code, run, check.

The three files that matter

One: requirements.txt — every import your app needs, pinned to the versions you actually tested with (flask==3.1.3 at the time of writing; pin what you tested, because unpinned dependencies are how a working app breaks months later without a single edit). Two: the code itself, in a repo. Three: the two commands your host will run — on Render, a build command (pip install -r requirements.txt) and a start command. The production-grade start command uses a real WSGI server rather than Flask's development server:

gunicorn -w 2 -b 0.0.0.0:$PORT app:app

app:app means "the variable app inside app.py"; -b 0.0.0.0:$PORT binds to every interface on the port the platform assigns — binding to 127.0.0.1 is the classic reason a deploy "succeeds" and serves nothing. Flask's own server prints a warning for a reason: it is single-purpose and unsuitable for production traffic; gunicorn (install with pip install gunicorn) is the boring, correct answer.

Secrets are environment variables

API keys never go in code or in the repo — the host's environment-variable settings inject them at runtime, and your code reads them as the first thing it does:

import os

API_KEY = os.environ["API_KEY"]

Using os.environ["KEY"] (rather than .get) is deliberate: the app should refuse to start without its secrets rather than run broken and discover it at 3am. The full discipline — scoping, rotation, never committing tokens — is in the token-hygiene guide.

The first-hand warnings

Trust the deploy log, not the homepage — a host can serve the last good build while today's build quietly failed, which is the number-one "why is my change not live" mystery (our deployment-failure field notes). Expect free-tier web services to sleep between requests and wake slowly on the first hit (static hosting doesn't sleep; app services do — different products, despite the same dashboard). And after the first deploy, verify with curl, not vibes: hit the URL, check the status code, confirm the JSON — the same rule as every piece on this desk.

Sources

Next

Related on this desk.