Deploy Python Applications¶
Framework-specific guide for deploying Python apps to Airbase
This guide covers Python-specific deployment patterns, best practices, and common issues.
Quick Start¶
Complete example available: Python Streamlit Example
Basic workflow:
# 1. Create Dockerfile
# 2. Build container
airbase container build
# 3. Deploy
airbase container deploy --yes staging
Time: 5 minutes
Dockerfile Pattern for Python¶
Standard Pattern¶
Pattern:
FROM gdssingapore/airbase:python-3.13
# Set environment variables
ENV PYTHONUNBUFFERED=TRUE
# Set working directory
WORKDIR /app
# Copy requirements and install dependencies
COPY --chown=app:app requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY --chown=app:app . ./
# Switch to non-root user
USER app
# Expose port
EXPOSE 3000
# Start application
CMD ["python", "app.py"]
Key points: - ✅ PYTHONUNBUFFERED=TRUE - See logs immediately - ✅ --no-cache-dir - Smaller image size - ✅ Install dependencies before copying code (layer caching) - ✅ USER app for security
Required Configuration¶
1. Bind to 0.0.0.0¶
Flask:
import os
if __name__ == '__main__':
app.run(
host='0.0.0.0',
port=int(os.environ.get('PORT', 3000))
)
FastAPI (with uvicorn):
import os
import uvicorn
if __name__ == '__main__':
uvicorn.run(
app,
host='0.0.0.0',
port=int(os.environ.get('PORT', 3000))
)
Streamlit:
# In Dockerfile CMD
CMD ["sh", "-c", "streamlit run app.py --server.port=${PORT:-8501} --server.address=0.0.0.0 --server.headless=true"]
2. Read PORT from Environment¶
3. Health Check Endpoint¶
Flask:
FastAPI:
Common Frameworks¶
Flask¶
requirements.txt:
app.py:
from flask import Flask
import os
app = Flask(__name__)
PORT = int(os.environ.get('PORT', 3000))
@app.route('/health')
def health():
return {'status': 'ok'}
@app.route('/')
def index():
return {'message': 'Hello from Flask on Airbase'}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=PORT)
Dockerfile CMD:
FastAPI¶
requirements.txt:
app.py:
from fastapi import FastAPI
import os
import uvicorn
app = FastAPI()
PORT = int(os.environ.get('PORT', 3000))
@app.get('/health')
def health():
return {'status': 'ok'}
@app.get('/')
def index():
return {'message': 'Hello from FastAPI on Airbase'}
if __name__ == '__main__':
uvicorn.run(app, host='0.0.0.0', port=PORT)
Dockerfile CMD:
Or using uvicorn directly:
Streamlit¶
See: Python Streamlit Example for complete working code
requirements.txt:
Dockerfile CMD:
CMD ["sh", "-c", "streamlit run app.py --server.port=${PORT:-8501} --server.address=0.0.0.0 --server.headless=true"]
Instance type: b.small recommended (Streamlit is memory-intensive)
Common Issues¶
Issue: "ModuleNotFoundError"¶
Cause: Package not in requirements.txt or not installed
Solution: - Add missing package to requirements.txt - Rebuild container: airbase container build - Verify requirements.txt syntax (no typos)
Issue: "Address already in use"¶
Cause: Not binding to 0.0.0.0
Solution:
Issue: "Permission denied"¶
Cause: Files owned by root
Solution: - Use --chown=app:app in COPY commands - Ensure USER app before CMD
Issue: No logs appearing¶
Cause: Python buffering output
Solution:
Issue: Slow startup¶
Cause: Large dependencies or Streamlit cold start
Solution: - Upgrade to b.small instance type - Use --no-cache-dir in pip install - Consider adding warmup endpoint
Best Practices¶
1. Pin Dependency Versions¶
Bad:
Good:
Best (with hashes):
2. Use Virtual Environments Locally¶
# Create virtual environment
python -m venv venv
# Activate
source venv/bin/activate # macOS/Linux
venv\Scripts\activate # Windows
# Install dependencies
pip install -r requirements.txt
# Generate requirements
pip freeze > requirements.txt
3. Minimize Docker Image Size¶
Use --no-cache-dir:
Install only what you need:
# ❌ Don't install entire scipy if you only need numpy
scipy==1.12.0
# ✅ Install just what you need
numpy==1.26.4
4. Security Scanning¶
Check for vulnerabilities:
5. Environment Variables¶
import os
# ❌ Bad - hardcoded
DATABASE_URL = "postgres://localhost/mydb"
# ✅ Good - from environment
DATABASE_URL = os.environ.get('DATABASE_URL')
# ✅ Better - with validation
DATABASE_URL = os.environ.get('DATABASE_URL')
if not DATABASE_URL:
raise ValueError("DATABASE_URL environment variable is required")
Production WSGI Server¶
For Flask/FastAPI APIs¶
Use Gunicorn for production:
requirements.txt:
Dockerfile CMD:
Why Gunicorn? - ✅ Production-ready - ✅ Multi-worker support - ✅ Better performance than Flask dev server
Configuration:
# gunicorn.conf.py
import os
bind = f"0.0.0.0:{os.environ.get('PORT', 3000)}"
workers = 2
worker_class = "sync"
timeout = 30
Dockerfile CMD with config:
CSP Considerations¶
Flask/FastAPI APIs¶
Good news: APIs serving JSON are CSP-compliant by default
If serving HTML:
from flask import Flask, render_template
@app.route('/page')
def page():
# ✅ Use templates with external JS
return render_template('page.html')
page.html:
<!DOCTYPE html>
<html>
<head>
<title>My Page</title>
</head>
<body>
<div id="app"></div>
<!-- ✅ External script (CSP-compliant) -->
<script src="{{ url_for('static', filename='app.js') }}"></script>
</body>
</html>
Streamlit CSP¶
See: Python Streamlit Example for CSP-compliant charting with Plotly
Use CSP-compliant libraries: - ✅ Plotly (CSP-compliant) - ❌ Matplotlib inline display (not CSP-compliant) - ❌ Some Streamlit components (check before using)
Performance Optimization¶
1. Use Appropriate Instance Type¶
For APIs: nano usually sufficient
For Streamlit: b.small recommended
2. Caching in Streamlit¶
import streamlit as st
@st.cache_data
def load_data():
# This result is cached
return expensive_operation()
3. Database Connection Pooling¶
from sqlalchemy import create_engine
from sqlalchemy.pool import QueuePool
engine = create_engine(
DATABASE_URL,
poolclass=QueuePool,
pool_size=5,
max_overflow=10
)
Checklist¶
Before deploying:
- requirements.txt with pinned versions
- PYTHONUNBUFFERED=TRUE in Dockerfile
- App binds to 0.0.0.0
- PORT read from environment variable
- Health check endpoint implemented
- Using --no-cache-dir for pip install
- All files owned by app:app
- USER app before CMD
- No hardcoded secrets in code
- Using Gunicorn for production (APIs)
See Also¶
- Example: Python Streamlit - Complete Streamlit example
- How-To: Set Environment Variables - Environment variable configuration
- Reference: Base Images - Python base images
- Reference: Dockerfile Requirements - Container requirements