This guide covers production deployment strategies for the SWL Library Management System, including server configuration, security considerations, and best practices.
Production vs Development
Development Mode
The default run.py configuration runs in development mode (run.py:54):
app.run(host='0.0.0.0', port=5000, debug=True)
Development Settings:
- Debug mode enabled
- Auto-reload on code changes
- Detailed error pages
- SQLite database
- Flask development server
NEVER run with debug=True in production. This exposes sensitive information and allows arbitrary code execution.
Production Requirements
Critical Changes:
- ✓ Disable debug mode
- ✓ Use PostgreSQL instead of SQLite
- ✓ Use production WSGI server (gunicorn/uwsgi)
- ✓ Set secure
SECRET_KEY
- ✓ Configure proper logging
- ✓ Enable HTTPS/SSL
- ✓ Set up reverse proxy (nginx/apache)
Production Deployment Options
Option 1: Gunicorn (Recommended)
Install Gunicorn:
Run Application:
gunicorn -w 4 -b 0.0.0.0:5000 "app:create_app()"
Gunicorn Configuration (gunicorn_config.py):
import multiprocessing
# Binding
bind = "0.0.0.0:5000"
# Workers
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "sync"
# Logging
accesslog = "logs/gunicorn_access.log"
errorlog = "logs/gunicorn_error.log"
loglevel = "info"
# Process naming
proc_name = "swl_library"
# Timeout
timeout = 120
# Daemon mode
daemon = False
Run with config:
gunicorn -c gunicorn_config.py "app:create_app()"
Option 2: uWSGI
Install uWSGI:
uWSGI Configuration (uwsgi.ini):
[uwsgi]
module = wsgi:app
master = true
processes = 4
socket = /tmp/library.sock
chmod-socket = 660
vacuum = true
die-on-term = true
# Logging
logto = logs/uwsgi.log
Run uWSGI:
PostgreSQL Configuration
1. Install PostgreSQL
# Ubuntu/Debian
sudo apt update
sudo apt install postgresql postgresql-contrib
# Start and enable service
sudo systemctl start postgresql
sudo systemctl enable postgresql
2. Create Production Database
-- Create database
CREATE DATABASE library_production;
-- Create user with strong password
CREATE USER library_user WITH PASSWORD 'CHANGE_THIS_SECURE_PASSWORD';
-- Grant privileges
GRANT ALL PRIVILEGES ON DATABASE library_production TO library_user;
-- Grant schema privileges (PostgreSQL 15+)
GRANT ALL ON SCHEMA public TO library_user;
\q
3. Install Python PostgreSQL Driver
pip install psycopg2-binary
Environment Configuration
Production Environment Variables
Create a .env file (never commit to git):
# Security
SECRET_KEY=your-secure-random-key-generated-with-secrets-module
# Database
DATABASE_URL=postgresql://library_user:SECURE_PASSWORD@localhost/library_production
# Business Settings
PENALTY_FEE_PER_DAY=5000.0
# Scheduler
SCHEDULER_TIMEZONE=America/Bogota
Generate Secure Secret Key:
python -c "import secrets; print(secrets.token_hex(32))"
Loading Environment Variables
Option 1: python-dotenv
pip install python-dotenv
Create wsgi.py:
from dotenv import load_dotenv
import os
# Load environment variables
load_dotenv()
from app import create_app
app = create_app()
if __name__ == "__main__":
app.run()
Option 2: systemd environment file (see Systemd Service section)
Network Configuration
Binding to Network Interfaces
The application binds to all interfaces (run.py:54):
app.run(host='0.0.0.0', port=5000)
Network Binding Options:
| Host | Description | Use Case |
|---|
127.0.0.1 | Localhost only | Development, reverse proxy |
0.0.0.0 | All interfaces | Production (behind firewall) |
192.168.1.10 | Specific IP | Multi-interface servers |
When using a reverse proxy (nginx/apache), bind to 127.0.0.1 for security.
Firewall Configuration
# Allow only from reverse proxy (if on same machine)
sudo ufw allow from 127.0.0.1 to any port 5000
# Or allow from specific network
sudo ufw allow from 192.168.1.0/24 to any port 5000
# Enable firewall
sudo ufw enable
Reverse Proxy Setup
Nginx Configuration
Install Nginx:
Create site configuration (/etc/nginx/sites-available/library):
server {
listen 80;
server_name library.example.com;
# Redirect to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name library.example.com;
# SSL certificates (use Let's Encrypt)
ssl_certificate /etc/letsencrypt/live/library.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/library.example.com/privkey.pem;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Logging
access_log /var/log/nginx/library_access.log;
error_log /var/log/nginx/library_error.log;
# Proxy to application
location / {
proxy_pass http://127.0.0.1:5000;
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;
}
# Static files (if served separately)
location /static {
alias /path/to/app/static;
expires 30d;
}
}
Enable site:
sudo ln -s /etc/nginx/sites-available/library /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
SSL/TLS with Let’s Encrypt
# Install Certbot
sudo apt install certbot python3-certbot-nginx
# Obtain certificate
sudo certbot --nginx -d library.example.com
# Auto-renewal is configured automatically
# Test renewal
sudo certbot renew --dry-run
Systemd Service Setup
Create Service File
Create /etc/systemd/system/library.service:
[Unit]
Description=SWL Library Management System
After=network.target postgresql.service
Requires=postgresql.service
[Service]
Type=notify
User=library
Group=library
WorkingDirectory=/opt/library
# Environment file
EnvironmentFile=/opt/library/.env
# Virtual environment
Environment="PATH=/opt/library/venv/bin"
# Run gunicorn
ExecStart=/opt/library/venv/bin/gunicorn \
-c gunicorn_config.py \
--log-file=- \
"wsgi:app"
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
# Restart policy
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
Create System User
# Create dedicated user (no login)
sudo useradd -r -s /bin/false library
# Set ownership
sudo chown -R library:library /opt/library
Manage Service
# Reload systemd configuration
sudo systemctl daemon-reload
# Enable service (start on boot)
sudo systemctl enable library
# Start service
sudo systemctl start library
# Check status
sudo systemctl status library
# View logs
sudo journalctl -u library -f
# Restart service
sudo systemctl restart library
# Stop service
sudo systemctl stop library
Security Considerations
1. Secret Key Security
The SECRET_KEY is used for session signing and CSRF protection. If compromised, attackers can forge sessions.
Requirements:
- Minimum 32 bytes of randomness
- Never use default development key
- Never commit to version control
- Rotate periodically (invalidates sessions)
Generate secure key:
import secrets
print(secrets.token_hex(32))
2. Database Security
Checklist:
- ✓ Use strong database passwords (20+ characters)
- ✓ Restrict PostgreSQL network access
- ✓ Use connection pooling
- ✓ Enable SSL for database connections
- ✓ Regular backups with encryption
- ✓ Limit database user privileges
PostgreSQL SSL Configuration (postgresql.conf):
ssl = on
ssl_cert_file = '/etc/ssl/certs/server.crt'
ssl_key_file = '/etc/ssl/private/server.key'
3. Application Security
File Permissions:
# Application files
chmod 755 /opt/library
chmod 644 /opt/library/*.py
# Environment file (sensitive)
chmod 600 /opt/library/.env
# Log directory
chmod 755 /opt/library/logs
chmod 644 /opt/library/logs/*.log
Disable Debug Mode:
# Never in production
app.run(debug=False)
4. Change Default Credentials
CRITICAL: The default admin account uses well-known credentials:
- Document ID:
1000000000
- Password:
admin123
Change this immediately after deployment!
Steps:
- Log in with default credentials
- Navigate to profile/settings
- Change password to strong, unique value
- Update email to valid administrator email
5. HTTPS/TLS
Requirements:
- Use TLS 1.2 or higher
- Strong cipher suites
- Valid SSL certificate
- HSTS headers
Nginx HSTS Configuration:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
6. Rate Limiting
Install Flask-Limiter:
pip install Flask-Limiter
Configure (app/__init__.py):
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
limiter = Limiter(
app,
key_func=get_remote_address,
default_limits=["200 per day", "50 per hour"]
)
Monitoring and Logging
Application Logs
Logs are written to logs/library.log with rotation (app/__init__.py:49):
- Max file size: 1MB
- Backup count: 3 files
- Log level: INFO
Log Format:
2026-03-04 10:23:45,123 INFO: LMS Startup - Technical Specs Initialized [in app/__init__.py:56]
System Monitoring
View application logs:
tail -f /opt/library/logs/library.log
View systemd logs:
sudo journalctl -u library -f
Monitor database:
# Active connections
sudo -u postgres psql -c "SELECT count(*) FROM pg_stat_activity WHERE datname='library_production';"
# Database size
sudo -u postgres psql -c "SELECT pg_size_pretty(pg_database_size('library_production'));"
1. Database Connection Pooling
Configure SQLAlchemy (config.py):
SQLALCHEMY_ENGINE_OPTIONS = {
'pool_size': 10,
'pool_recycle': 3600,
'pool_pre_ping': True,
'max_overflow': 20
}
2. Worker Configuration
Gunicorn workers:
# Formula: (2 x CPU cores) + 1
workers = multiprocessing.cpu_count() * 2 + 1
3. Static File Serving
Nginx static file caching:
location /static {
alias /opt/library/app/static;
expires 30d;
add_header Cache-Control "public, immutable";
}
Deployment Checklist
Pre-Deployment
Deployment
Post-Deployment
Troubleshooting
Application Won’t Start
Check logs:
sudo journalctl -u library -n 50
Common issues:
- Database connection errors → verify
DATABASE_URL
- Import errors → check virtual environment
- Permission denied → verify file ownership
Database Connection Failures
Test connection:
psql -U library_user -h localhost library_production
Check PostgreSQL:
sudo systemctl status postgresql
sudo tail -f /var/log/postgresql/postgresql-14-main.log
502 Bad Gateway (Nginx)
Causes:
- Application not running
- Wrong proxy_pass address
- Firewall blocking connection
Check:
# Is application running?
sudo systemctl status library
# Test direct connection
curl http://127.0.0.1:5000
# Check nginx error log
sudo tail -f /var/log/nginx/library_error.log
See Also