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
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
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.
- Dockerfile
- Nixpacks
Full control. The repo includes a 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.
Zero-config — Nixpacks auto-detects Python and installs requirements.txt. No Dockerfile needed. Add a nixpacks.toml so it starts under gunicorn in production:
[start]
cmd = "gunicorn -b 0.0.0.0:8000 app:app"
When you create the app, set Build Type → Nixpacks.
3. Create the services
In your project's environment:
- 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-postgresif usingkuploy/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.
- Source — point it at your Git repo (set Build Path
- Create Service → Database → PostgreSQL named
db(defaults are fine). Its internal connection string — shown on the db's General tab — ispostgresql://<user>:<password>@<db-app-name>:5432/<database>(the host is the service's app name; see Databases). The stack injects exactly this string asDATABASE_URL.
4. Group them into a stack
- Environment toolbar → Stacks → Create stack, name it
visits, open it. - Add component → add
web, then adddb.
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.
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 —
webanddbkeep 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.