Python Web Development Setup

Complete Guide for LAMP/LEMP Stack on RHEL/OEL8/CentOS

Flask â€Ē Django â€Ē FastAPI â€Ē WSGI â€Ē Database Integration

⚡ Quick Start - Python Web Stack

# Install Python 3 with web development essentials sudo dnf install -y python3 python3-pip python3-devel python3-mod_wsgi python3-PyMySQL python3-virtualenv && pip3 install --user flask django fastapi uvicorn gunicorn pymysql sqlalchemy --break-system-packages

📋 System Information

Target Systems & Python Version
  • Oracle Enterprise Linux 8 (OEL8)
  • Red Hat Enterprise Linux 8 (RHEL8)
  • CentOS 8 / Rocky Linux 8 / AlmaLinux 8
  • Python Version: 3.6+ (default in RHEL8 is Python 3.6-3.9 depending on release)
  • Python 3.11+: Available via AppStream modules
📘 Web Server Options
  • Apache + mod_wsgi: Traditional, well-tested, good for mixed environments
  • Gunicorn + Nginx: Modern, scalable, recommended for production
  • uWSGI + Nginx: High performance, complex configuration
  • Uvicorn/Hypercorn: For ASGI apps (FastAPI, async frameworks)

ðŸ“Ķ System Packages (DNF/YUM)

Python Core

python3 Python 3 interpreter
python3-pip Python package installer (pip)
python3-devel Development headers (needed for compiled packages)
python3-virtualenv Virtual environment creation
python3-setuptools Python package installation tools
python3-wheel Wheel package format support

Database Drivers (System Packages)

python3-PyMySQL Pure Python MySQL/MariaDB driver
python3-psycopg2 PostgreSQL driver
python3-sqlalchemy SQL toolkit and ORM (if available in repos)

Web Server Integration

python3-mod_wsgi Apache WSGI module for Python 3
nginx High-performance web server (alternative to Apache)

Development Tools

gcc C compiler (for building Python packages)
gcc-c++ C++ compiler
make Build automation tool
git Version control

🔧 Installation Steps

Step 1: Install System Packages

Complete System Package Installation

sudo dnf install -y \ python3 \ python3-pip \ python3-devel \ python3-virtualenv \ python3-setuptools \ python3-wheel \ python3-mod_wsgi \ python3-PyMySQL \ gcc \ gcc-c++ \ make \ git

Step 2: Verify Python Installation

Version Check
# Check Python version python3 --version # Check pip version pip3 --version # Check which Python which python3 # Check installed packages pip3 list

ðŸ“Ķ Python Packages (via pip)

⚠ïļ RHEL8 pip Restriction
RHEL8 requires --break-system-packages flag when using pip outside virtual environments. For production, always use virtual environments!

Web Frameworks

Flask Micro web framework
Django Full-featured web framework
FastAPI Modern async API framework
Pyramid Flexible web framework
Tornado Async web framework

WSGI/ASGI Servers

gunicorn Python WSGI HTTP Server (recommended!)
uvicorn ASGI server for FastAPI
hypercorn ASGI server (alternative to uvicorn)
uwsgi High-performance application server

Database Libraries

PyMySQL Pure Python MySQL/MariaDB driver
mysql-connector-python Oracle's official MySQL driver
SQLAlchemy SQL toolkit and ORM
psycopg2-binary PostgreSQL driver (binary distribution)
redis Redis client

Essential Web Development Packages

requests HTTP library
Jinja2 Template engine (included with Flask)
python-dotenv Environment variable management
pydantic Data validation (required for FastAPI)
python-multipart Form data parsing (for FastAPI)

Install Python Packages

Install via pip (User Install)
# Install web frameworks pip3 install --user \ flask \ django \ fastapi \ --break-system-packages # Install WSGI/ASGI servers pip3 install --user \ gunicorn \ uvicorn[standard] \ --break-system-packages # Install database drivers pip3 install --user \ pymysql \ sqlalchemy \ --break-system-packages # Install utilities pip3 install --user \ requests \ python-dotenv \ pydantic \ python-multipart \ --break-system-packages

🔒 Virtual Environments (Best Practice!)

ðŸ’Ą Always Use Virtual Environments
Virtual environments isolate project dependencies, prevent conflicts, and make deployment reproducible. Never install packages system-wide in production!

Create and Use Virtual Environment

Virtual Environment Workflow
# Create project directory mkdir -p /var/www/myapp cd /var/www/myapp # Create virtual environment python3 -m venv venv # Activate virtual environment source venv/bin/activate # Upgrade pip in venv pip install --upgrade pip # Install packages (no --break-system-packages needed in venv!) pip install flask gunicorn pymysql sqlalchemy # Create requirements.txt pip freeze > requirements.txt # Deactivate when done deactivate # Later: Recreate environment from requirements.txt source venv/bin/activate pip install -r requirements.txt

ðŸŒķïļ Flask Application Setup

Flask - Micro Web Framework

Lightweight, flexible, perfect for small to medium applications and APIs. Easy to learn, great for beginners.

Flask Installation & Project Structure

Flask Project Setup
# Create project directory sudo mkdir -p /var/www/flask-app cd /var/www/flask-app # Create virtual environment python3 -m venv venv source venv/bin/activate # Install Flask with database support pip install flask gunicorn pymysql sqlalchemy flask-sqlalchemy # Create project structure mkdir -p app/{static,templates} touch app/__init__.py touch app/routes.py touch app/models.py touch config.py touch wsgi.py touch .env # Set ownership sudo chown -R apache:apache /var/www/flask-app
Flask Application - Basic Structure /var/www/flask-app/app/__init__.py
from flask import Flask from flask_sqlalchemy import SQLAlchemy import os db = SQLAlchemy() def create_app(): app = Flask(__name__) # Configuration app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'dev-secret-key-change-in-production') app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv( 'DATABASE_URL', 'mysql+pymysql://webapp_user:YourPassword@localhost/webapp_db' ) app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # Initialize extensions db.init_app(app) # Register blueprints from app import routes app.register_blueprint(routes.bp) return app
Flask Routes /var/www/flask-app/app/routes.py
from flask import Blueprint, render_template, jsonify from app import db import sys bp = Blueprint('main', __name__) @bp.route('/') def index(): return render_template('index.html', title='Flask App', python_version=sys.version) @bp.route('/api/health') def health(): # Check database connection try: db.session.execute('SELECT 1') db_status = 'connected' except Exception as e: db_status = f'error: {str(e)}' return jsonify({ 'status': 'ok', 'database': db_status, 'python_version': sys.version }) @bp.route('/api/users') def users(): # Example database query try: result = db.session.execute( 'SELECT * FROM users LIMIT 10' ) users = [dict(row) for row in result] return jsonify(users) except Exception as e: return jsonify({'error': str(e)}), 500
WSGI Entry Point /var/www/flask-app/wsgi.py
from app import create_app app = create_app() if __name__ == '__main__': app.run()

Run Flask Development Server

Flask Development
# Activate virtual environment source venv/bin/activate # Set environment variables export FLASK_APP=wsgi.py export FLASK_ENV=development # Run development server flask run --host=0.0.0.0 --port=5000 # Or use gunicorn (production-like) gunicorn --bind 0.0.0.0:5000 wsgi:app

ðŸŽļ Django Application Setup

Django - Full-Featured Web Framework

Batteries-included framework. Perfect for large applications. Built-in admin, ORM, authentication, and more.

Django Installation & Project Creation

Django Project Setup
# Create project directory sudo mkdir -p /var/www/django-app cd /var/www/django-app # Create virtual environment python3 -m venv venv source venv/bin/activate # Install Django with database support pip install django gunicorn pymysql mysqlclient # Create Django project django-admin startproject myproject . # Create an app python manage.py startapp myapp # Set ownership sudo chown -R apache:apache /var/www/django-app
Django Settings (Database Configuration) /var/www/django-app/myproject/settings.py
# Key settings for MySQL/MariaDB import os import pymysql # Use PyMySQL as MySQLdb pymysql.install_as_MySQLdb() # Security SECRET_KEY = os.getenv('SECRET_KEY', 'change-me-in-production') DEBUG = os.getenv('DEBUG', 'False') == 'True' ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'your-domain.com'] # Database - Works with MySQL or MariaDB! DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'webapp_db', 'USER': 'webapp_user', 'PASSWORD': 'YourPassword', 'HOST': 'localhost', 'PORT': '3306', 'OPTIONS': { 'charset': 'utf8mb4', 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", }, } } # Static files STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') # Add your app to INSTALLED_APPS INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'myapp', ]

Django Database Setup

Django Migrations & Admin
# Activate virtual environment source venv/bin/activate # Create database tables python manage.py migrate # Create superuser for admin python manage.py createsuperuser # Collect static files python manage.py collectstatic --noinput # Run development server python manage.py runserver 0.0.0.0:8000 # Or use gunicorn (production) gunicorn --bind 0.0.0.0:8000 myproject.wsgi:application

⚡ FastAPI Application Setup

FastAPI - Modern Async API Framework

High performance, automatic API documentation, async support, type hints. Perfect for APIs and microservices.

FastAPI Installation

FastAPI Project Setup
# Create project directory sudo mkdir -p /var/www/fastapi-app cd /var/www/fastapi-app # Create virtual environment python3 -m venv venv source venv/bin/activate # Install FastAPI with all extras pip install 'fastapi[all]' uvicorn[standard] pymysql sqlalchemy # Create project structure mkdir -p app touch app/__init__.py touch app/main.py touch app/database.py touch app/models.py # Set ownership sudo chown -R apache:apache /var/www/fastapi-app
FastAPI Application /var/www/fastapi-app/app/main.py
from fastapi import FastAPI, Depends from sqlalchemy.orm import Session from app.database import engine, get_db from pydantic import BaseModel import sys app = FastAPI( title="FastAPI Application", description="Python web app with database connectivity", version="1.0.0" ) @app.get("/") def read_root(): return { "message": "Welcome to FastAPI", "python_version": sys.version, "docs": "/docs" } @app.get("/health") def health_check(db: Session = Depends(get_db)): try: # Test database connection db.execute("SELECT 1") db_status = "connected" except Exception as e: db_status = f"error: {str(e)}" return { "status": "ok", "database": db_status } # Example Pydantic model class User(BaseModel): name: str email: str @app.post("/api/users") def create_user(user: User, db: Session = Depends(get_db)): # Insert user into database result = db.execute( "INSERT INTO users (name, email) VALUES (:name, :email)", {"name": user.name, "email": user.email} ) db.commit() return {"id": result.lastrowid, **user.dict()} @app.get("/api/users") def get_users(db: Session = Depends(get_db)): result = db.execute("SELECT * FROM users LIMIT 10") users = [dict(row) for row in result] return users
FastAPI Database Connection /var/www/fastapi-app/app/database.py
from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, declarative_base # Database URL - Works with MySQL or MariaDB! DATABASE_URL = "mysql+pymysql://webapp_user:YourPassword@localhost/webapp_db" engine = create_engine(DATABASE_URL, pool_pre_ping=True) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() # Dependency def get_db(): db = SessionLocal() try: yield db finally: db.close()

Run FastAPI Application

FastAPI Development & Production
# Activate virtual environment source venv/bin/activate # Development server (auto-reload) uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload # Production server (multiple workers) uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4 # Access automatic API documentation: # http://your-server:8000/docs (Swagger UI) # http://your-server:8000/redoc (ReDoc)

ðŸŠķ Apache + mod_wsgi Deployment

Apache Configuration for Flask/Django /etc/httpd/conf.d/python-app.conf
<VirtualHost *:80> ServerName example.com ServerAdmin webmaster@example.com # WSGI Configuration WSGIDaemonProcess myapp \ python-home=/var/www/flask-app/venv \ python-path=/var/www/flask-app \ processes=2 \ threads=15 \ display-name=%{GROUP} WSGIProcessGroup myapp WSGIScriptAlias / /var/www/flask-app/wsgi.py # Directory permissions <Directory /var/www/flask-app> WSGIProcessGroup myapp WSGIApplicationGroup %{GLOBAL} Require all granted </Directory> # Static files (for Django) Alias /static/ /var/www/flask-app/static/ <Directory /var/www/flask-app/static> Require all granted </Directory> # Logging ErrorLog /var/log/httpd/python-app-error.log CustomLog /var/log/httpd/python-app-access.log combined </VirtualHost>

Enable and Test Apache Configuration

Apache Deployment Steps
# Set proper permissions sudo chown -R apache:apache /var/www/flask-app # Set SELinux contexts sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/flask-app(/.*)?" sudo semanage fcontext -a -t httpd_sys_script_exec_t "/var/www/flask-app/wsgi.py" sudo restorecon -Rv /var/www/flask-app # Enable database connectivity sudo setsebool -P httpd_can_network_connect_db on # Test configuration sudo apachectl configtest # Restart Apache sudo systemctl restart httpd

ðŸĶ„ Gunicorn + Nginx Deployment (Recommended)

ðŸ’Ą Why Gunicorn + Nginx?
This combination is the modern standard for Python web apps:
  • Gunicorn handles Python application serving
  • Nginx handles static files, SSL, load balancing, and reverse proxy
  • Better performance than mod_wsgi for pure Python apps
  • Easier to scale horizontally

Install Nginx

Nginx Installation
sudo dnf install -y nginx sudo systemctl enable nginx sudo systemctl start nginx sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https sudo firewall-cmd --reload
Systemd Service for Gunicorn /etc/systemd/system/gunicorn-myapp.service
[Unit] Description=Gunicorn daemon for Flask/Django app After=network.target [Service] Type=notify User=apache Group=apache WorkingDirectory=/var/www/flask-app Environment="PATH=/var/www/flask-app/venv/bin" # For Flask: ExecStart=/var/www/flask-app/venv/bin/gunicorn \ --workers 3 \ --bind unix:/run/gunicorn-myapp.sock \ --access-logfile /var/log/gunicorn/access.log \ --error-logfile /var/log/gunicorn/error.log \ wsgi:app # For Django: # ExecStart=/var/www/django-app/venv/bin/gunicorn \ # --workers 3 \ # --bind unix:/run/gunicorn-myapp.sock \ # myproject.wsgi:application # For FastAPI (use uvicorn instead): # ExecStart=/var/www/fastapi-app/venv/bin/uvicorn \ # --workers 4 \ # --bind unix:/run/gunicorn-myapp.sock \ # app.main:app ExecReload=/bin/kill -s HUP $MAINPID KillMode=mixed TimeoutStopSec=5 PrivateTmp=true [Install] WantedBy=multi-user.target
Nginx Reverse Proxy Configuration /etc/nginx/conf.d/python-app.conf
server { listen 80; server_name example.com www.example.com; # Logging access_log /var/log/nginx/python-app-access.log; error_log /var/log/nginx/python-app-error.log; # Static files (for Django) location /static/ { alias /var/www/flask-app/staticfiles/; expires 30d; add_header Cache-Control "public, immutable"; } # Media files (uploads) location /media/ { alias /var/www/flask-app/media/; } # Proxy to Gunicorn location / { proxy_pass http://unix:/run/gunicorn-myapp.sock; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # Timeouts proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } # Security headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; }

Enable Gunicorn Service

Start Gunicorn + Nginx
# Create log directory sudo mkdir -p /var/log/gunicorn sudo chown apache:apache /var/log/gunicorn # Reload systemd sudo systemctl daemon-reload # Enable and start Gunicorn sudo systemctl enable gunicorn-myapp sudo systemctl start gunicorn-myapp # Check status sudo systemctl status gunicorn-myapp # Test Nginx configuration sudo nginx -t # Restart Nginx sudo systemctl restart nginx # Check logs sudo tail -f /var/log/gunicorn/error.log sudo tail -f /var/log/nginx/python-app-error.log

🔒 SELinux Configuration

SELinux for Python Web Apps

SELinux Setup
# Allow network connections (database, APIs) sudo setsebool -P httpd_can_network_connect on sudo setsebool -P httpd_can_network_connect_db on # Set contexts for application directory sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/flask-app(/.*)?" sudo semanage fcontext -a -t httpd_sys_script_exec_t "/var/www/flask-app/wsgi.py" sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/flask-app/media(/.*)?" # For Gunicorn socket sudo semanage fcontext -a -t httpd_var_run_t "/run/gunicorn-myapp.sock" # Restore contexts sudo restorecon -Rv /var/www/flask-app sudo restorecon -Rv /run/gunicorn-myapp.sock # Check for denials sudo ausearch -m avc -ts recent | grep python

ðŸ’ū Database Connection Examples

PyMySQL (Works with MySQL & MariaDB)

PyMySQL Connection Example pymysql_example.py
import pymysql from contextlib import contextmanager @contextmanager def get_db_connection(): conn = pymysql.connect( host='localhost', user='webapp_user', password='YourPassword', database='webapp_db', charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor ) try: yield conn conn.commit() except Exception: conn.rollback() raise finally: conn.close() # Usage with get_db_connection() as conn: with conn.cursor() as cursor: cursor.execute("SELECT * FROM users WHERE id = %s", (1,)) user = cursor.fetchone() print(user)

SQLAlchemy ORM (Recommended)

SQLAlchemy Connection sqlalchemy_example.py
from sqlalchemy import create_engine, Column, Integer, String from sqlalchemy.orm import sessionmaker, declarative_base # Database URL - Works with MySQL or MariaDB! DATABASE_URL = "mysql+pymysql://webapp_user:YourPassword@localhost/webapp_db" engine = create_engine(DATABASE_URL, pool_pre_ping=True) Session = sessionmaker(bind=engine) Base = declarative_base() # Define model class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(100)) email = Column(String(100)) # Usage session = Session() try: user = session.query(User).filter_by(id=1).first() print(f"User: {user.name} ({user.email})") finally: session.close()

🔍 Troubleshooting

Common Issues

Problem Solution
Can't connect to database SELinux: setsebool -P httpd_can_network_connect_db on
Check credentials and socket path
pip install fails Install python3-devel and gcc
Use --break-system-packages outside venv
ModuleNotFoundError Activate virtual environment
Check PYTHONPATH in systemd service
502 Bad Gateway (Nginx) Check Gunicorn is running
Verify socket path in Nginx config
Check SELinux contexts on socket
Permission denied errors chown -R apache:apache /var/www/app
restorecon -Rv /var/www/app

Useful Commands

# Check Python version python3 --version # List installed packages pip3 list # Check virtual environment which python echo $VIRTUAL_ENV # Test database connectivity python3 -c "import pymysql; pymysql.connect(host='localhost', user='webapp_user', password='YourPassword', database='webapp_db')" # Check Gunicorn status sudo systemctl status gunicorn-myapp sudo journalctl -u gunicorn-myapp -f # Test Nginx config sudo nginx -t # Check SELinux denials sudo ausearch -m avc -ts recent | grep python # View application logs sudo tail -f /var/log/gunicorn/error.log

✅ Best Practices Summary

ðŸ’Ą Python Web Development Best Practices
  • Always use virtual environments - Never install packages system-wide in production
  • Use requirements.txt - Pin all dependencies for reproducible deployments
  • Environment variables - Never hardcode secrets, use .env files or environment variables
  • Gunicorn + Nginx - Preferred over Apache mod_wsgi for pure Python apps
  • Database connection pooling - Use SQLAlchemy or configure pool settings
  • SELinux - Configure it properly, don't disable it
  • Logging - Configure proper application logging, not just print statements
  • Async when needed - Use FastAPI/asyncio for high-concurrency APIs
  • Static files - Let Nginx serve them, not Python
  • Security - Keep dependencies updated, use security headers, validate input

📚 Framework Quick Reference

Framework Best For Production Server Dev Server
Flask Small-medium apps, APIs, microservices Gunicorn flask run
Django Large apps, admin interfaces, CMS Gunicorn manage.py runserver
FastAPI Modern APIs, async, microservices Uvicorn/Gunicorn uvicorn --reload