Skip to main content

Installation Overview

This guide covers detailed installation and configuration for both development and production environments. Choose the setup that matches your needs.

Development Setup

Full-featured development environment with debugging and hot reload

Production Deployment

Secure, optimized production configuration with Gunicorn and PostgreSQL

System Requirements

Minimum Requirements:
  • Python 3.8 or higher
  • 512MB RAM (2GB+ recommended for production)
  • PostgreSQL 12+ (or SQLite for development)
  • 500MB disk space

Supported Platforms

  • Linux: Ubuntu 20.04+, Debian 11+, CentOS 8+, RHEL 8+
  • macOS: 10.15 (Catalina) or later
  • Windows: Windows 10/11, Windows Server 2019+

Development Installation

Step 1: Python Environment Setup

1

Install Python

Ensure Python 3.8 or higher is installed:
sudo apt update
sudo apt install python3 python3-pip python3-venv
Verify installation:
python3 --version
# Should output: Python 3.8.x or higher
2

Install PostgreSQL (Recommended)

While SQLite works for development, PostgreSQL is recommended:
sudo apt install postgresql postgresql-contrib
sudo systemctl start postgresql
sudo systemctl enable postgresql
Create the database:
sudo -u postgres psql
CREATE DATABASE mathsoc;
CREATE USER mathsoc_user WITH PASSWORD 'your_secure_password';
GRANT ALL PRIVILEGES ON DATABASE mathsoc TO mathsoc_user;
\q
3

Clone Repository

Clone the Maths Society Platform repository:
git clone https://github.com/yousuf-shahzad/maths-soc-source.git
cd maths-soc-source

Step 2: Virtual Environment & Dependencies

1

Create Virtual Environment

Create an isolated Python environment:
python3 -m venv venv
source venv/bin/activate
You’ll know the virtual environment is activated when you see (venv) in your terminal prompt.
2

Install Dependencies

Install all required packages:
pip install --upgrade pip
pip install -r requirements.txt
Core dependencies installed:
  • Flask==2.1.0 - Web framework
  • SQLAlchemy==1.4.31 - ORM
  • Flask-SQLAlchemy==2.5.1 - Flask-SQLAlchemy integration
  • Flask-Login==0.5.0 - Authentication
  • Flask-Migrate==3.1.0 - Database migrations
  • Flask-WTF==0.15.1 - Forms and CSRF
  • Flask-CKEditor==1.0.0 - Rich text editor
  • psycopg2==2.9.3 - PostgreSQL adapter
  • Flask-Talisman==1.0.0 - Security headers
  • Flask-Limiter==3.5.0 - Rate limiting
  • gunicorn==22.0.0 - Production WSGI server
  • pytest==8.3.3 - Testing framework

Step 3: Configuration

Create a .env file in the project root:
.env (Development)
# ===========================================
# Maths Society Platform - Development Config
# ===========================================

# Security (CHANGE THIS!)
SECRET_KEY=dev-secret-key-replace-with-random-string

# Database Configuration
DATABASE_TYPE=postgresql
DB_USERNAME=mathsoc_user
DB_PASSWORD=your_secure_password
DB_HOST=localhost
DB_NAME=mathsoc

# Alternative: Use DATABASE_URL (overrides above settings)
# DATABASE_URL=postgresql://mathsoc_user:password@localhost/mathsoc

# Environment
FLASK_ENV=development
APP_ENVIRONMENT=development
ENV=dev

# Logging
LOG_TO_STDOUT=false

# Rate Limiting (optional)
RATELIMIT_ENABLED=true
RATELIMIT_STORAGE_URI=memory://

# Server (optional)
# SERVER_NAME=localhost:5000
Required Settings:
  • SECRET_KEY: Used for session signing and CSRF tokens. Generate with:
    python -c "import os; print(os.urandom(24).hex())"
    
Database Settings:
  • DATABASE_URL: Full database connection string (takes precedence)
  • DATABASE_TYPE: postgresql or sqlite
  • DB_USERNAME, DB_PASSWORD, DB_HOST, DB_NAME: PostgreSQL credentials
Environment Settings:
  • FLASK_ENV: development, testing, or production
  • APP_ENVIRONMENT: Alternative environment indicator
  • ENV: Another environment flag (dev, test, prod)
Logging:
  • LOG_TO_STDOUT: Set to true for container deployments
Rate Limiting:
  • RATELIMIT_ENABLED: Enable/disable rate limiting
  • RATELIMIT_STORAGE_URI: Storage backend (memory:// or redis://)

Step 4: Database Setup

1

Initialize Database Schema

Choose one of these methods:
2

Verify Database

Check that tables were created:
psql -U mathsoc_user -d mathsoc -c "\dt"
You should see tables like:
  • user
  • challenge
  • article
  • competition
  • submission
  • etc.

Step 5: Run Development Server

1

Start Flask

Run the development server:
python run.py
Output:
* Serving Flask app 'app'
* Debug mode: on
WARNING: This is a development server. Do not use it in production.
* Running on http://127.0.0.1:5000
2

Access Application

Open your browser to: http://127.0.0.1:5000Login with default admin credentials:
  • Username: admin
  • Password: admin123
Change the default admin password immediately!

Production Installation

Production deployment requires:
  • Secure SECRET_KEY
  • PostgreSQL database (not SQLite)
  • HTTPS/SSL certificates
  • Gunicorn or similar WSGI server
  • Reverse proxy (nginx/Apache)
  • Proper firewall configuration

Production Environment Setup

1

Server Preparation

Update system and install dependencies:
sudo apt update && sudo apt upgrade -y
sudo apt install python3.11 python3.11-venv python3-pip postgresql nginx -y
2

Create Application User

Run the application as a dedicated user:
sudo useradd -m -s /bin/bash mathsoc
sudo su - mathsoc
3

Deploy Application

Clone and setup:
git clone https://github.com/yousuf-shahzad/maths-soc-source.git /home/mathsoc/app
cd /home/mathsoc/app
python3.11 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
4

Production Configuration

Create production .env:
.env (Production)
# CRITICAL: Generate secure random key!
SECRET_KEY=your-production-secret-key-min-32-chars-random

# Production Database
DATABASE_URL=postgresql://mathsoc_prod:strong_password@localhost/mathsoc_prod

# Environment
FLASK_ENV=production
APP_ENVIRONMENT=production
ENV=prod

# Logging
LOG_TO_STDOUT=true

# Rate Limiting (Redis recommended for production)
RATELIMIT_ENABLED=true
RATELIMIT_STORAGE_URI=redis://localhost:6379

# Server
SERVER_NAME=yourdomain.com
Generate strong SECRET_KEY:
python3 -c "import secrets; print(secrets.token_hex(32))"
5

Database Setup

Create production database:
sudo -u postgres psql
CREATE DATABASE mathsoc_prod;
CREATE USER mathsoc_prod WITH PASSWORD 'strong_random_password';
GRANT ALL PRIVILEGES ON DATABASE mathsoc_prod TO mathsoc_prod;
ALTER DATABASE mathsoc_prod OWNER TO mathsoc_prod;
\q
Run migrations:
export FLASK_APP=run:app
flask db upgrade
6

Gunicorn Configuration

The platform includes gunicorn.conf.py:
gunicorn.conf.py
import multiprocessing

bind = "127.0.0.1:8000"
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "sync"
worker_connections = 1000
timeout = 30
keepalive = 2

# Logging
accesslog = "-"
errorlog = "-"
loglevel = "info"

# Security
limit_request_line = 4094
limit_request_fields = 100
limit_request_field_size = 8190
Test Gunicorn:
gunicorn -c gunicorn.conf.py wsgi:app
7

Systemd Service

Create /etc/systemd/system/mathsoc.service:
/etc/systemd/system/mathsoc.service
[Unit]
Description=Maths Society Platform
After=network.target postgresql.service
Requires=postgresql.service

[Service]
Type=notify
User=mathsoc
Group=mathsoc
WorkingDirectory=/home/mathsoc/app
Environment="PATH=/home/mathsoc/app/venv/bin"
ExecStart=/home/mathsoc/app/venv/bin/gunicorn -c gunicorn.conf.py wsgi:app
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable mathsoc
sudo systemctl start mathsoc
sudo systemctl status mathsoc
8

Nginx Reverse Proxy

Create /etc/nginx/sites-available/mathsoc:
/etc/nginx/sites-available/mathsoc
upstream mathsoc {
    server 127.0.0.1:8000;
}

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name yourdomain.com www.yourdomain.com;

    # SSL Configuration (use certbot for Let's Encrypt)
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    # Security Headers (Flask-Talisman handles most, but add extras)
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # File Upload Size
    client_max_body_size 20M;

    # Static Files
    location /static {
        alias /home/mathsoc/app/app/static;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # Proxy to Gunicorn
    location / {
        proxy_pass http://mathsoc;
        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;
        proxy_redirect off;
    }
}
Enable site:
sudo ln -s /etc/nginx/sites-available/mathsoc /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
9

SSL Certificate

Install Let’s Encrypt certificate:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Auto-renewal is configured automatically.

Database Configuration Details

The platform supports flexible database configuration through config.py:
config.py (excerpt)
class Config:
    @classmethod
    def get_database_uri(cls, database_name=None):
        # Prioritize DATABASE_URL environment variable
        if os.environ.get('DATABASE_URL'):
            return os.environ.get('DATABASE_URL')
        
        # Build URI from components
        db_type = os.environ.get('DATABASE_TYPE', 'postgresql')
        db_username = os.environ.get('DB_USERNAME', 'postgres')
        db_password = os.environ.get('DB_PASSWORD', '')
        db_host = os.environ.get('DB_HOST', 'localhost')
        db_name = database_name or os.environ.get('DB_NAME', 'myapp')
        
        if db_type == 'postgresql':
            return f'postgresql://{db_username}:{db_password}@{db_host}/{db_name}'
        elif db_type == 'sqlite':
            return f'sqlite:///{os.path.join(cls.BASE_DIR, f"{db_name}.db")}'
Use DATABASE_URL for platforms like Heroku, Railway, or Render that provide it automatically.

Running Tests

The platform includes a comprehensive test suite:
# Run all tests
pytest

# Run with coverage
pytest --cov=app --cov-report=html

# Run specific test file
pytest tests/test_models.py

# Run with verbose output
pytest -v
Test configuration in config.py:
config.py (excerpt)
class TestingConfig(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
    WTF_CSRF_ENABLED = False
    CSP_ALLOW_UNSAFE_EVAL = True
    RATELIMIT_ENABLED = False

Environment Detection

The platform automatically detects the environment:
config.py (excerpt)
@classmethod
def is_production(cls):
    return os.environ.get('FLASK_ENV') == 'production' or \
           os.environ.get('APP_ENVIRONMENT') == 'production' or \
           os.environ.get('ENV') == 'prod'
Any of these environment variables can indicate production mode.

Monitoring & Maintenance

The platform includes built-in health checks:
app/__init__.py (excerpt)
@app.route('/healthz')
def healthz():
    return {"status": "ok"}, 200

@app.route('/readyz')
def readyz():
    try:
        db.session.execute(text('SELECT 1'))
        return {"status": "ready"}, 200
    except Exception:
        return {"status": "degraded"}, 503
Monitor these endpoints:
curl https://yourdomain.com/healthz
curl https://yourdomain.com/readyz
View application logs:
# Systemd logs
sudo journalctl -u mathsoc -f

# Nginx logs
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log

# Application logs (if LOG_TO_STDOUT=false)
tail -f /home/mathsoc/app/app.log
Automate PostgreSQL backups:
# Manual backup
pg_dump -U mathsoc_prod mathsoc_prod > backup_$(date +%Y%m%d).sql

# Restore
psql -U mathsoc_prod mathsoc_prod < backup_20260303.sql
Create cron job for daily backups:
0 2 * * * pg_dump -U mathsoc_prod mathsoc_prod | gzip > /backups/mathsoc_$(date +\%Y\%m\%d).sql.gz
Update the application:
cd /home/mathsoc/app
git pull origin main
source venv/bin/activate
pip install -r requirements.txt
flask db upgrade
sudo systemctl restart mathsoc

Security Checklist

Before going to production, verify:
  • Strong random SECRET_KEY (minimum 32 characters)
  • PostgreSQL with strong passwords (no default credentials)
  • HTTPS enabled with valid SSL certificate
  • Firewall configured (only ports 80, 443, 22 open)
  • Database backups configured
  • FLASK_ENV=production set
  • Default admin password changed
  • Rate limiting enabled
  • Log monitoring configured
  • Security updates automated
  • File upload size limits set
  • CSP headers properly configured

Troubleshooting

Check systemd status:
sudo systemctl status mathsoc
sudo journalctl -u mathsoc -n 50
Common issues:
  • Database connection failed (check credentials)
  • Port already in use (check other services)
  • Permission errors (check file ownership)
  • Missing environment variables (check .env)
Indicates Gunicorn isn’t running or nginx can’t reach it:
# Check if Gunicorn is running
sudo systemctl status mathsoc

# Check if port 8000 is listening
sudo netstat -tlnp | grep 8000

# Test Gunicorn directly
curl http://127.0.0.1:8000/healthz
If migrations fail:
# Check current revision
flask db current

# Show migration history
flask db history

# Downgrade one revision
flask db downgrade

# Upgrade again
flask db upgrade
Ensure nginx has permission:
sudo chown -R mathsoc:mathsoc /home/mathsoc/app/app/static
sudo chmod -R 755 /home/mathsoc/app/app/static
Check nginx configuration:
sudo nginx -t
sudo systemctl reload nginx

Next Steps

Configure Your Platform

Customize settings, features, and branding

Create Challenges

Start adding mathematical challenges for students

User Management

Add students and manage permissions

API Reference

Integrate with external systems
Your Maths Society Platform is now installed and ready! Visit your domain to start using the platform.

Build docs developers (and LLMs) love