Deploy a Streamlit app

Lizard runs a Streamlit app with the command you use locally, streamlit run app.py. Put it in a Procfile web: line with --server.address 0.0.0.0 and a fixed port such as 8501, then deploy with lizard up --port 8501. Without the Procfile, lizardpack starts python app.py, which prints a warning and exits instead of serving the app.

Test status: the sample below ran locally with Streamlit 1.65.0, and the database version with psycopg 3.3.5 against PostgreSQL 14. We checked its build plan with lizardpack, the build planner Lizard runs. We have not yet deployed it end to end. The browser keeps a WebSocket open to Streamlit, and we have not yet checked that connection through Lizard’s routing.

Before you start

  • Python 3 on your computer to test the app.
  • Lizard CLI, signed in with lizard login, on Pro or the 7-day Pro trial (card required). See pricing.

A minimal app

app.py:

import streamlit as st
 
st.title("Hello from Streamlit on Lizard")
name = st.text_input("Your name", "world")
st.write(f"Hello, {name}!")

requirements.txt:

streamlit==1.65.0

.python-version:

3.13

Procfile:

web: streamlit run app.py --server.port 8501 --server.address 0.0.0.0

Run it locally:

python -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
streamlit run app.py

Streamlit opens on http://localhost:8501. Add .venv/, .env and .streamlit/secrets.toml to .gitignore.

What lizardpack builds

StepWhat happens
Detectionrequirements.txt, pyproject.toml, Pipfile or any .py file
Python version.python-version, then .tool-versions, then requires-python in pyproject.toml; major and minor only, on python:<x.y>-slim. Default 3.13.
Installpip install -r requirements.txt; uv, Poetry or Pipenv when their lock or config file is present
StartThe Procfile web: line
RuntimePYTHONUNBUFFERED=1, so print output reaches the logs at once

Set the service port with --port to match --server.port in the Procfile. See runtime versions for the version rules and their edge cases.

Deploy

From the app’s folder:

lizard init --name streamlit-app
lizard add --service web
lizard up --service web --port 8501

lizard up uploads the folder, builds it on Lizard and prints the URL. Inside a git repository it skips what .gitignore lists; outside git it uploads .env files. For git push to deploy, connect a GitHub repository and set the port with lizard port 8501 --service web.

Secrets and settings

st.secrets reads .streamlit/secrets.toml, a file you should not upload. On Lizard, store secrets as service variables and read them from the environment:

lizard secrets set OPENAI_API_KEY=sk-... --service web
import os
 
api_key = os.environ["OPENAI_API_KEY"]

A variable change restarts the app without a rebuild. See variables and secrets.

Add Managed Postgres

lizard add postgres
lizard secrets set DATABASE_URL='${{postgres.DATABASE_PRIVATE_URL}}' --service web

The single quotes keep ${{…}} away from your shell; Lizard resolves the reference at deploy time. Add psycopg[binary]==3.3.5 to requirements.txt and open one connection per server process:

import os
 
import psycopg
import streamlit as st
 
 
@st.cache_resource
def get_conn():
    return psycopg.connect(os.environ["DATABASE_URL"], autocommit=True)
 
 
with get_conn().cursor() as cur:
    cur.execute("SELECT now()")
    st.write("Database time:", cur.fetchone()[0])

st.cache_resource keeps the connection across reruns and browser sessions. Keep data in Managed Postgres or Managed Object Storage: files the app writes inside the container are lost on the next deploy.

Check that it works

lizard logs --service web --tail 100
lizard domain --service web
curl https://<your-domain>/_stcore/health

The log shows You can now view your Streamlit app in your browser, and the health URL answers ok. Open the app’s URL and change the text box: the greeting should update.

Common problems

SymptomFix
Log says to view this Streamlit app on a browser, run it with the following commandThe Procfile is missing or has no web: line, so lizardpack ran python app.py.
Deploy never becomes healthyThe service port and --server.port differ. Run lizard port --service web. See service never healthy.
ModuleNotFoundErrorAdd the package to requirements.txt and deploy again.
KeyError for a secretThe variable is missing on the web service: lizard secrets list --service web.
The page shows a connecting message and never loadsThe browser cannot keep its WebSocket to /_stcore/stream. Look for that failed request in the browser console, and for errors in lizard logs.

Updated