Minimal template for deploying Streamlit apps to Databricks Apps. This example demonstrates a Delta table editor with inline editing capabilities.
- Queries Unity Catalog Delta tables via SQL Warehouse
- Displays data in an editable Streamlit table
- Writes updates back to Delta tables
- Runs as a Databricks App with service principal authentication
- Databricks workspace with Apps enabled
- SQL Warehouse (note the warehouse ID)
- Unity Catalog table with data
- Databricks CLI installed and authenticated
Create a .env.local file with your Databricks configuration:
# App name (choose a unique name, lowercase, no spaces)
# This app will be CREATED by ./deploy.sh --create
DATABRICKS_APP_NAME=your-app-name
# Workspace path where source code will be uploaded
# Pattern: /Workspace/Users/<your-email>/<folder-name>
# This folder will be created automatically during deployment
DBA_SOURCE_CODE_PATH=/Workspace/Users/your.email@company.com/your-app-name
# Authentication method (use databricks-cli for profile-based auth)
DATABRICKS_AUTH_TYPE=databricks-cli
# Your Databricks CLI profile name (from ~/.databrickscfg)
# Run `databricks auth login` to create a profile if needed
DATABRICKS_CONFIG_PROFILE=your-profile-nameWhat gets created automatically:
- β
The Databricks App (when you run
./deploy.sh --create) - β
The workspace folder at
DBA_SOURCE_CODE_PATH - β You do NOT need to create anything manually in Databricks first
Edit streamlit_app.py to point to your data:
CATALOG = "your_catalog"
SCHEMA = "your_schema"
TABLE = "your_table"
WAREHOUSE_ID = "your_warehouse_id"Edit requirements.txt to add new packages:
# Python dependencies for Databricks Apps
# Add new packages here and run ./deploy.sh
streamlit>=1.30.0
pandas>=2.0.0
databricks-sdk>=0.18.0
# Add your packages below:
# plotly>=5.0.0
# scikit-learn>=1.0.0First-time deployment (creates the app):
./deploy.sh --createSubsequent deployments (updates existing app):
./deploy.shThe --create flag will:
- Check if the app exists
- Create it if it doesn't (takes several minutes)
- Deploy your code to the app
After deployment, grant your app's service principal:
SQL Warehouse:
- Navigate to SQL Warehouses β Select your warehouse β Permissions
- Add service principal (named
app-XXXXX <your-app-name>) with Can Use permission
Unity Catalog:
- Grant at catalog level OR specific schema/table level:
-- Option 1: Catalog-level (easiest)
GRANT USAGE ON CATALOG your_catalog TO `app-XXXXX your-app-name`;
GRANT USAGE ON SCHEMA your_catalog.your_schema TO `app-XXXXX your-app-name`;
GRANT SELECT ON TABLE your_catalog.your_schema.your_table TO `app-XXXXX your-app-name`;
GRANT MODIFY ON TABLE your_catalog.your_schema.your_table TO `app-XXXXX your-app-name`;
-- Option 2: Grant ALL PRIVILEGES at catalog level
GRANT ALL PRIVILEGES ON CATALOG your_catalog TO `app-XXXXX your-app-name`;SQL Warehouse Resource (via UI):
- Go to your Databricks App settings β Resources
- Add SQL Warehouse with "Can use" permission
Run locally for testing before deployment:
streamlit run streamlit_app.py --server.port=8501Why port 8501? This is Streamlit's default development port. Use any available port locally - it doesn't matter for local testing.
Local authentication: Uses your Databricks CLI profile from ~/.databrickscfg. Make sure to use WorkspaceClient(profile="your-profile-name") in local mode.
When deployed to Databricks Apps, key differences:
- Port: MUST use
${DATABRICKS_APP_PORT}environment variable (typically 8000)- This is set in
app.yaml:--server.port=${DATABRICKS_APP_PORT:-8000} - Databricks Apps routes traffic to this port
- This is set in
- Authentication: Uses app service principal automatically, not user credentials
- WorkspaceClient: Use
WorkspaceClient()with NO profile parameter
Port Mismatch:
- Symptom: "App Not Available" error
- Fix: App must listen on
${DATABRICKS_APP_PORT}(set by Databricks Apps) - Correct:
--server.port=${DATABRICKS_APP_PORT:-8000} - Wrong:
--server.port=8080
Permission Errors:
- Symptom: Empty tables or "no data" errors
- Fix: Ensure service principal has SQL Warehouse + Unity Catalog permissions
- Check app SP name:
databricks apps get <app-name> --output json | grep service_principal_name
Authentication:
- Local:
WorkspaceClient(profile="your-profile")works - Deployed:
WorkspaceClient()required (no profile parameter)
Add a logo to the Streamlit sidebar:
import streamlit as st
from PIL import Image
# At the top of your app
st.set_page_config(
page_title="Your Company - Loan Exceptions",
page_icon="π’", # Or use your logo emoji
layout="wide"
)
# Add logo in sidebar
with st.sidebar:
# Option 1: Load from file (upload logo.png to your workspace)
logo = Image.open("logo.png")
st.image(logo, width=200)
# Option 2: Load from URL
st.image("https://your-company.com/logo.png", width=200)
st.title("Loan Exceptions")Create a .streamlit/config.toml file in your app directory:
[theme]
primaryColor = "#FF6B6B" # Your brand color (buttons, links)
backgroundColor = "#FFFFFF" # Main background
secondaryBackgroundColor = "#F0F2F6" # Sidebar, inputs
textColor = "#262730" # Main text
font = "sans serif" # Options: "sans serif", "serif", "monospace"
# Example: Corporate blue theme
# primaryColor = "#1E40AF"
# backgroundColor = "#FFFFFF"
# secondaryBackgroundColor = "#EFF6FF"
# textColor = "#1F2937"Upload the config file to your workspace:
# Add to .streamlit/config.toml, then redeploy
./deploy.shdef format_currency(value):
"""Format number as USD currency"""
return f"${value:,.2f}"
# Usage in dataframe
df['loan_amount_display'] = df['loan_amount'].apply(format_currency)
st.dataframe(df)def get_status_color(status):
"""Return color for status badge"""
colors = {
"Open": "π΄",
"In Progress": "π‘",
"Cleared": "π’",
"Closed": "β«"
}
return colors.get(status, "βͺ")
# Usage
df['status_display'] = df['status'].apply(lambda x: f"{get_status_color(x)} {x}")import pandas as pd
from io import BytesIO
def export_to_excel(df):
"""Export dataframe to Excel file"""
output = BytesIO()
with pd.ExcelWriter(output, engine='openpyxl') as writer:
df.to_excel(writer, index=False, sheet_name='Exceptions')
return output.getvalue()
# Usage in your app
if st.button("π₯ Export to Excel"):
excel_data = export_to_excel(df)
st.download_button(
label="Download Excel",
data=excel_data,
file_name="loan_exceptions.xlsx",
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
)def add_filter_sidebar(df):
"""Add filter controls to sidebar"""
with st.sidebar:
st.header("Filters")
# Status filter
statuses = ["All"] + list(df['status'].unique())
selected_status = st.selectbox("Status", statuses)
# Severity filter
severities = ["All"] + list(df['severity'].unique())
selected_severity = st.selectbox("Severity", severities)
# Date range
date_range = st.date_input(
"Date Range",
value=(df['created_date'].min(), df['created_date'].max())
)
# Apply filters
filtered_df = df.copy()
if selected_status != "All":
filtered_df = filtered_df[filtered_df['status'] == selected_status]
if selected_severity != "All":
filtered_df = filtered_df[filtered_df['severity'] == selected_severity]
return filtered_dfdef show_metrics(df):
"""Display key metrics in columns"""
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric(
label="Total Exceptions",
value=len(df),
delta=f"+{len(df[df['status']=='Open'])} Open"
)
with col2:
avg_resolution_days = (df['updated_date'] - df['created_date']).dt.days.mean()
st.metric(
label="Avg Resolution Time",
value=f"{avg_resolution_days:.1f} days"
)
with col3:
high_severity = len(df[df['severity'] == 'High'])
st.metric(
label="High Severity",
value=high_severity,
delta=f"{(high_severity/len(df)*100):.1f}%"
)
with col4:
cleared_rate = len(df[df['status'] == 'Cleared']) / len(df) * 100
st.metric(
label="Clearance Rate",
value=f"{cleared_rate:.1f}%"
)To add new columns to your Delta table:
# Run this once to add a new column
from databricks.sdk import WorkspaceClient
w = WorkspaceClient(profile="your-profile")
query = """
ALTER TABLE your_catalog.your_schema.your_table
ADD COLUMN new_column_name STRING
"""
response = w.statement_execution.execute_statement(
warehouse_id="your_warehouse_id",
statement=query,
catalog="your_catalog",
schema="your_schema"
)Then update your load_data() query and editable columns list in streamlit_app.py:
# Add to SELECT statement
query = f"""
SELECT
exception_id,
loan_id,
...,
new_column_name -- Add your new column
FROM {CATALOG}.{SCHEMA}.{TABLE}
"""
# Add to editable columns list
editable_columns = [
'loan_id',
'exception_type',
'severity',
'status',
'owner',
'description',
'additional_comments',
'new_column_name' -- Add your new column
]- Edit code: Modify
streamlit_app.pyor add dependencies torequirements.txt - Test locally:
streamlit run streamlit_app.py --server.port=8501 - Deploy:
./deploy.shto push changes to Databricks Apps - Monitor: Visit
https://<your-app-url>/logzto check deployment logs
βββ streamlit_app.py # Your Streamlit application
βββ app.yaml # Databricks Apps configuration
βββ requirements.txt # Python dependencies (edit directly)
βββ deploy.sh # Deployment script
βββ .env.local # Local environment config (not committed)
βββ dba_logz.py # Helper: View deployment logs
βββ dba_client.py # Helper: Test authenticated requests
Key files to customize:
streamlit_app.py- Your app logic, UI, and queriesrequirements.txt- Add Python packages here.streamlit/config.toml- Theme and styling (create if needed)app.yaml- App startup command and port config
# Deploy app
./deploy.sh
# Check app status
databricks apps get <app-name>
# View app service principal
databricks apps get <app-name> --output json | grep service_principal_name
# Test deployed app endpoint
curl https://<app-url>Use the included helper scripts:
# View deployment logs (requires OAuth in browser)
uv run python dba_logz.py --app_url https://<your-app-url> --duration 60
# Test authenticated requests
uv run python dba_client.py https://<your-app-url> /healthSee LICENSE.md