Python course Β· Module 5: Flask and Web Apps
Deployment - publishing on the internet
In this lesson15
Congratulations! Darwin here with the last lesson of Module 5 - deploying a Flask application!
You've built a functioning Safari Database application with forms, authentication and templates. Now it's time to publish it on the internet so the world can use it!
Safari Analogy: Deployment is like opening the park to the public - until now the park only operated internally (localhost), now you're opening the gates for tourists from all over the world (internet)!
Development vs Production
Development (localhost)
app.run(debug=True) - development server
Auto-reload after code changes
Detailed errors in the browser
Localhost only - not accessible from the internet
Slow - not optimized
Production (internet)
WSGI server (Gunicorn, uWSGI)
No debug=True - security!
Environment variables - SECRET_KEY, DATABASE_URL
Publicly accessible - anyone can visit
Fast - optimized
NEVER use app.run(debug=True) in production!
WSGI Server - Gunicorn
Gunicorn (Green Unicorn) is a popular WSGI server for Flask/Django applications.
WSGI = Web Server Gateway Interface - a standard for communication between a server and a Python application.
Installation
1pip install gunicornRunning
1# Basic
2gunicorn app:app
3
4# With port and workers specified
5gunicorn app:app --bind 0.0.0.0:8000 --workers 4
6
7# With reload (dev only)
8gunicorn app:app --reloadFormat: gunicorn <module>:<app_variable>
app- file name (app.py)app- Flask variable name (app = Flask(name))
Preparing for deployment
1. requirements.txt file
Save all dependencies:
1pip freeze > requirements.txtrequirements.txt:
1Flask==3.0.0
2Flask-WTF==1.2.1
3Flask-Login==0.6.3
4Werkzeug==3.0.1
5gunicorn==21.2.02. Environment variables
NEVER commit SECRET_KEY and sensitive data to git!
Use environment variables:
1# app.py
2import os
3
4app = Flask(__name__)
5
6# Get from environment variables
7app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY') or 'dev-fallback-key'
8app.config['DATABASE_URL'] = os.environ.get('DATABASE_URL') or 'sqlite:///dev.db'
9
10# Debug only in dev
11app.config['DEBUG'] = os.environ.get('FLASK_ENV') == 'development'3. .gitignore
1# .gitignore
2venv/
3__pycache__/
4*.pyc
5*.pyo
6.env
7instance/
8.DS_Store4. Procfile (for platforms like Render/Heroku)
1web: gunicorn app:appProcfile tells the platform how to run your application.
5. runtime.txt (optional)
1python-3.11.0Specifies the Python version.
Deployment on Render (FREE!)
Render is a modern hosting platform with a free plan. A great alternative to Heroku!
Step 1: Prepare the application
Project structure:
1safari_web/
2βββ app.py
3βββ models.py
4βββ forms.py
5βββ requirements.txt
6βββ Procfile
7βββ .gitignore
8βββ templates/
9β βββ ...
10βββ static/
11 βββ ...app.py - last lines:
1if __name__ == '__main__':
2 # Only for local development
3 app.run(debug=True)Render will run via Procfile, so if __name__ won't execute!
Step 2: Push to GitHub
1git init
2git add .
3git commit -m "Initial commit - Safari Database"
4git branch -M main
5git remote add origin https://github.com/your-username/safari-web.git
6git push -u origin mainStep 3: Create Web Service on Render
Go to https://render.com and register (FREE)
Click "New +" β "Web Service"
Connect your GitHub repository
Select the
safari-webrepositoryConfiguration:
- Name: safari-database
- Environment: Python 3
- Build Command:
pip install -r requirements.txt - Start Command:
gunicorn app:app - Plan: Free
Environment Variables - add:
SECRET_KEY=your-random-secret-key-hereFLASK_ENV=production
Click "Create Web Service"
Step 4: Deploy!
Render automatically:
- Installs dependencies (
pip install -r requirements.txt) - Runs Gunicorn (
gunicorn app:app) - Assigns a domain (
https://safari-database.onrender.com)
Congratulations! Your application is LIVE!
Deployment on Railway (also FREE!)
Railway is another great platform with a simple setup.
Steps:
- https://railway.app - register
- "New Project" β "Deploy from GitHub repo"
- Select the repository
- Railway automatically detects Flask!
- Add Environment Variables:
SECRET_KEYFLASK_ENV=production
- Railway automatically deploys!
Domain: https://safari-database.up.railway.app
Deployment checklist
Before deployment make sure:
Security
DEBUG = Falsein production- SECRET_KEY as environment variable
- Passwords hashed (werkzeug.security)
- CSRF protection enabled (Flask-WTF)
- .gitignore contains
venv/,.env
Files
- requirements.txt is up to date
- Procfile created
- .gitignore created
- Static files properly configured
Database
- In dev: SQLite
- In prod: PostgreSQL (Render/Railway FREE tier!)
- DATABASE_URL as environment variable
Testing
- Application works locally
- All routes work
- Forms work
- Login/registration works
Database in production
SQLite (Development)
1app.config['DATABASE_URL'] = 'sqlite:///safari.db'Problems: SQLite is not recommended for production (concurrency issues).
PostgreSQL (Production)
Render/Railway offer FREE PostgreSQL!
Render: Add PostgreSQL Database β copy DATABASE_URL
1import os
2
3database_url = os.environ.get('DATABASE_URL')
4
5if database_url and database_url.startswith('postgres://'):
6 # Render uses postgres://, but SQLAlchemy needs postgresql://
7 database_url = database_url.replace('postgres://', 'postgresql://', 1)
8
9app.config['SQLALCHEMY_DATABASE_URI'] = database_url or 'sqlite:///dev.db'Static files in production
Flask automatically serves files from static/, but in production it's better to use a CDN or Nginx.
Basic Flask configuration:
1# app.py
2app = Flask(__name__)
3
4# Flask automatically serves static files from /static/
5# http://yourapp.com/static/css/style.cssIn templates:
1<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
2<script src="{{ url_for('static', filename='js/script.js') }}"></script>Monitoring and logs
Render Logs
Dashboard β Your Web Service β Logs
Railway Logs
Project β Your Service β Logs
Example logs:
1[2024-01-15 10:23:45] INFO: Starting gunicorn 21.2.0
2[2024-01-15 10:23:45] INFO: Listening at: http://0.0.0.0:10000
3[2024-01-15 10:23:45] INFO: Using worker: sync
4[2024-01-15 10:23:45] INFO: Booting worker with pid: 42Updating the application
Render/Railway
- Make code changes locally
- Commit to git:
1git add . 2git commit -m "Update: Add new feature" 3git push origin main - Render/Railway automatically deploys the new version!
Auto-deploy = push to GitHub β automatic deploy!
Custom domain (optional)
Instead of safari-database.onrender.com you can use your own domain:
- Buy a domain (e.g.
safaridatabase.com) - Render/Railway β Settings β Custom Domain
- Add a CNAME record at your domain registrar
- Done!
https://safaridatabase.comβ Your application
Alternative platforms
1. Fly.io
- FREE tier
- Global deployment
- Great documentation
2. PythonAnywhere
- FREE tier for Flask
- Simple setup
- Good for beginners
3. DigitalOcean App Platform
- $5/month
- More control
- Scalable
4. AWS/Google Cloud/Azure
- Advanced
- More expensive
- Full control
Example production-ready app.py
1# app.py
2import os
3from flask import Flask, render_template, redirect, url_for, flash
4from flask_login import LoginManager, login_user, logout_user, login_required, current_user
5from flask_sqlalchemy import SQLAlchemy
6
7app = Flask(__name__)
8
9# Configuration from environment variables
10app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY') or 'dev-key-change-in-production'
11app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL') or 'sqlite:///dev.db'
12app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
13
14# Fix for Render PostgreSQL URL
15if app.config['SQLALCHEMY_DATABASE_URI'].startswith('postgres://'):
16 app.config['SQLALCHEMY_DATABASE_URI'] = app.config['SQLALCHEMY_DATABASE_URI'].replace('postgres://', 'postgresql://', 1)
17
18db = SQLAlchemy(app)
19login_manager = LoginManager(app)
20login_manager.login_view = 'login'
21
22# Models, routes, etc...
23
24if __name__ == '__main__':
25 # Only for local development
26 port = int(os.environ.get('PORT', 5000))
27 app.run(host='0.0.0.0', port=port, debug=os.environ.get('FLASK_ENV') == 'development')Summary
In this lesson you learned:
- Difference between development and production
- WSGI servers (Gunicorn)
- Preparing applications for deployment
- Environment variables (SECRET_KEY, DATABASE_URL)
- Deployment on Render (FREE!)
- Deployment on Railway (FREE!)
- Deployment checklist
- Databases in production (PostgreSQL)
- Static files in production
- Monitoring and logs
- Updating applications (auto-deploy)
Final Safari Analogy: Deployment is like the grand opening of a safari park - you prepared the infrastructure (code), hired staff (WSGI server), secured the park (SECRET_KEY, HTTPS), and opened the gates for tourists from all over the world (internet)! Your Flask application lives and runs 24/7 in the cloud!
Congratulations on Module 5!
You've built a complete web application with:
- Frontend (HTML, CSS, JavaScript)
- Backend (Flask)
- Routing and view functions
- Jinja2 templates
- Forms with validation (Flask-WTF)
- Authentication (Flask-Login)
- Deployment on the internet (Render/Railway)
Safari Database is now LIVE and accessible to everyone on the internet!
Darwin is proud of you! Get ready for the next modules, where you'll learn advanced Python and AI techniques!
Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What is web application deployment?
2. What is Gunicorn?
These are 2 of 3 questions for this lesson. Solve the rest in the game.
Hands-on tasks in the game
- Vertical ordering
Arrange the typical steps for deploying a Flask application:
- Click in order
Arrange the command that generates requirements.txt:
- Code editor
Write a Config class with SECRET_KEY retrieved from an environment variable.
- Horizontal ordering
Arrange the elements of a route decorator:
- Vertical ordering
Arrange the steps for creating a Flask application:
- Code editor
Write a '/api/species' route that returns a list of species as JSON.
- Code editor
Create a '/' route in Flask that returns 'Welcome to Safari!'.
- Code editor
Render the 'index.html' template passing a title variable.
- Vertical ordering
Arrange the stages of handling an HTTP request:
- Code editor
Define a LoginForm class with username and password fields.
- Code editor
Build a Safari Species Portal application with: a species list, adding new ones, user login, and basic CSS styling.