Skip to main content

Deploy a Python Stack (Flask + PostgreSQL)

A minimal Flask app that counts page visits in PostgreSQL, deployed as a Stack: the platform injects the database connection string into the web app and deploys both in order — no copy-pasting credentials.

Result: visiting the app shows Hello from Kuploy Stacks! Visits: N, incrementing on each refresh.

1. The app

The app code is the same whichever build method you pick:

app.py
app.py
import os
import psycopg
from flask import Flask

app = Flask(__name__)
DATABASE_URL = os.environ["DATABASE_URL"] # injected by the stack connection

def init_db():
with psycopg.connect(DATABASE_URL) as conn:
conn.execute("CREATE TABLE IF NOT EXISTS visits (count int)")
if conn.execute("SELECT count(*) FROM visits").fetchone()[0] == 0:
conn.execute("INSERT INTO visits VALUES (0)")

@app.route("/")
def home():
with psycopg.connect(DATABASE_URL) as conn:
conn.execute("UPDATE visits SET count = count + 1")
n = conn.execute("SELECT count FROM visits").fetchone()[0]
return f"Hello from Kuploy Stacks! Visits: {n}"

init_db()
requirements.txt
requirements.txt
flask
psycopg[binary]
gunicorn

Grab this from kuploy/examples (subdir flask-postgres), or create the files yourself and push them to a Git repo. First complete the tutorial Prerequisites — a connected Git provider and a build registry — then connect the repo and set the build path as in Deploy Your First App.

2. Choose a build method

Kuploy can build the same code several ways. Pick one — the choice syncs across these tutorials. See Build Methods for the full list.

Full control. The repo includes a Dockerfile:

Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["gunicorn", "-b", "0.0.0.0:8000", "app:app"]

When you create the app, set Build Type → Dockerfile and enter Dockerfile in the Docker File field (it's required, and is relative to the build path). Docker Context Path can stay empty.

3. Create the services

In your project's environment:

  1. Create Service → Application named web. The Set up your application wizard opens — walk the steps in order (or close it and use the service's tabs manually):
    • Source — point it at your Git repo (set Build Path flask-postgres if using kuploy/examples).
    • Build — pick the Build Type you chose above.
    • Network — set the port to 8000; add a domain if you want it public.
    • Registry — select the registry from the prerequisites; without it the build has nowhere to push and the deploy fails with Registry required. (Skipped automatically for Docker-image sources or single-server deploys.)
    • Deploy — stays disabled until source and registry are set. Don't deploy it yet — it needs the database first.
  2. Create Service → Database → PostgreSQL named db (defaults are fine). Its internal connection string — shown on the db's General tab — is postgresql://<user>:<password>@<db-app-name>:5432/<database> (the host is the service's app name; see Databases). The stack injects exactly this string as DATABASE_URL.

4. Group them into a stack

  1. Environment toolbar → Stacks → Create stack, name it visits, open it.
  2. Add component → add web, then add db.

5. Connect

Drag from the db node to the web node (or click Connect):

  • Provider: db
  • Source field: connectionString
  • Consumer: web
  • Env var name: DATABASE_URL

This injects postgresql://user:password@db-appname:5432/dbname into web at deploy. psycopg reads it directly.

6. Deploy

Click Deploy stack. The platform deploys db first, then web (because web depends on db). Open the app's domain — refresh and the counter climbs.

Verify the injection

On the web service's Environment tab you'll see a managed block with the resolved DATABASE_URL. That's the stack connection at work.

What you learned

  • A stack component is a normal service — web and db keep all their own settings.
  • One connection replaces manual credential wiring.
  • Dependency order is automatic from the connection.
  • The same app deploys via Dockerfile or Nixpacks — only the build config differs.

Next: the same app in Node.js or PHP.