Overview
This guide covers production deployment of the CS Interview Assistant, including server configuration, security hardening, and scaling considerations.
This is a production deployment guide. For local development, see the RUN_GUIDE.md in the source repository.
Pre-Deployment Checklist
Before deploying to production, ensure you have:
Server Requirements
Recommended Specifications
Compute
4+ CPU cores (3.0 GHz+)
16+ GB RAM
50+ GB SSD storage
Network
Static IP address
100 Mbps+ bandwidth
WebSocket support
Operating System
Recommended: Ubuntu Server 22.04 LTS or later
# Update system
sudo apt update && sudo apt upgrade -y
# Install essential packages
sudo apt install -y python3.10 python3-pip python3-venv \
nodejs npm postgresql postgresql-contrib \
nginx supervisor git build-essential
Step 1: Environment Setup
1.1 Create Application User
# Create dedicated user for security
sudo useradd -m -s /bin/bash interview
sudo usermod -aG sudo interview
1.2 Clone Repository
sudo su - interview
git clone < repository-ur l > /home/interview/app
cd /home/interview/app
Create production .env file:
cd /home/interview/app/backend
touch .env
chmod 600 .env # Secure permissions
Add production configuration:
# AI Configuration
MISTRAL_API_KEY=<your-production-mistral-key>
MISTRAL_MODEL=mistral-large-latest
# Security (GENERATE NEW KEYS!)
SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
JWT_SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
# Database
DATABASE_URL=postgresql://interview_user:secure_password@localhost:5432/interview_db
# Optional: AssemblyAI for voice interviews
ASSEMBLYAI_API_KEY=<your-assemblyai-key>
Never use default security keys in production! Generate unique keys using the commands shown above.
Step 2: Database Setup
2.1 Install and Configure PostgreSQL
# PostgreSQL should already be installed from Step 1
sudo systemctl start postgresql
sudo systemctl enable postgresql
2.2 Create Database and User
# Switch to postgres user
sudo -u postgres psql
-- Create database
CREATE DATABASE interview_db ;
-- Create user with strong password
CREATE USER interview_user WITH ENCRYPTED PASSWORD 'your_secure_password_here' ;
-- Grant privileges
GRANT ALL PRIVILEGES ON DATABASE interview_db TO interview_user;
-- Exit
\q
2.3 Update Application Configuration
Modify backend/config.py to use PostgreSQL:
class Config :
# Use environment variable for database URL
SQLALCHEMY_DATABASE_URI = os.getenv(
'DATABASE_URL' ,
'sqlite:///interview_prep.db' # Fallback for development
)
SQLALCHEMY_TRACK_MODIFICATIONS = False
2.4 Migrate from SQLite (If Applicable)
If migrating from SQLite development database:
# Export data from SQLite
sqlite3 instance/interview_prep.db .dump > backup.sql
# Convert and import to PostgreSQL
# (This requires manual SQL conversion for schema differences)
For production deployments, use PostgreSQL from the start to avoid migration complexity.
Step 3: Backend Setup
3.1 Create Virtual Environment
cd /home/interview/app/backend
python3 -m venv venv
source venv/bin/activate
3.2 Install Dependencies
pip install --upgrade pip
pip install -r requirements.txt
# For production, also install Gunicorn
pip install gunicorn eventlet
3.3 Build Knowledge Base
# Prepare knowledge base
python scripts/prepare_kb.py
# Build FAISS indices
python scripts/reindex_mistral.py
FAISS index building can take 10-30 minutes depending on knowledge base size and server specs.
3.4 Initialize Database
# Run migrations and create tables
python backend/app.py
# Press Ctrl+C after seeing "Database initialized"
Create gunicorn_config.py in backend directory:
import multiprocessing
# Server socket
bind = '127.0.0.1:8000'
backlog = 2048
# Worker processes
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'eventlet'
worker_connections = 1000
timeout = 300 # 5 minutes for long-running AI operations
keepalive = 2
# Logging
accesslog = '/var/log/interview/gunicorn_access.log'
errorlog = '/var/log/interview/gunicorn_error.log'
loglevel = 'info'
# Process naming
proc_name = 'interview_backend'
# Server mechanics
daemon = False
pidfile = '/var/run/interview/gunicorn.pid'
user = 'interview'
group = 'interview'
Create log directory:
sudo mkdir -p /var/log/interview /var/run/interview
sudo chown interview:interview /var/log/interview /var/run/interview
Step 4: Frontend Build
4.1 Install Dependencies
cd /home/interview/app/frontend
npm install --production
4.2 Build Production Bundle
This creates an optimized production build in frontend/build/.
Update frontend/src/config.js (or equivalent) to point to production backend:
const API_BASE_URL = process . env . REACT_APP_API_URL || 'https://api.yourdomain.com' ;
const WS_BASE_URL = process . env . REACT_APP_WS_URL || 'wss://api.yourdomain.com' ;
Step 5: Nginx Configuration
sudo apt install nginx -y
Create Nginx configuration /etc/nginx/sites-available/interview:
# Upstream backend
upstream backend {
server 127.0.0.1:8000 fail_timeout = 0 ;
}
server {
listen 80 ;
server_name yourdomain.com www.yourdomain.com;
# Redirect to HTTPS
return 301 https://$ server_name $ request_uri ;
}
server {
listen 443 ssl http2;
server_name yourdomain.com www.yourdomain.com;
# SSL Configuration
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;
ssl_prefer_server_ciphers on ;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header X-XSS-Protection "1; mode=block" always;
# Static files (React frontend)
location / {
root /home/interview/app/frontend/build;
try_files $ uri $ uri / /index.html;
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable" ;
}
}
# Backend API
location /api {
proxy_pass http://backend;
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 ;
# Increase timeout for AI operations
proxy_read_timeout 300s ;
proxy_connect_timeout 300s ;
}
# WebSocket support
location /socket.io {
proxy_pass http://backend;
proxy_http_version 1.1 ;
proxy_set_header Upgrade $ http_upgrade ;
proxy_set_header Connection "upgrade" ;
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 ;
# WebSocket timeout
proxy_read_timeout 3600s ;
}
# File uploads
client_max_body_size 16M ;
}
5.2 Enable Configuration
sudo ln -s /etc/nginx/sites-available/interview /etc/nginx/sites-enabled/
sudo nginx -t # Test configuration
sudo systemctl restart nginx
sudo systemctl enable nginx
Step 6: SSL/TLS Certificate
6.1 Install Certbot
sudo apt install certbot python3-certbot-nginx -y
6.2 Obtain Certificate
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Follow the prompts to:
Enter email address
Agree to terms
Choose to redirect HTTP to HTTPS
6.3 Auto-Renewal
Certbot automatically sets up renewal. Test it:
sudo certbot renew --dry-run
Step 7: Process Management
7.1 Create Supervisor Configuration
Create /etc/supervisor/conf.d/interview.conf:
[program:interview_backend]
command =/home/interview/app/backend/venv/bin/gunicorn -c gunicorn_config.py app:app
directory =/home/interview/app/backend
user =interview
autostart =true
autorestart =true
startretries =3
redirect_stderr =true
stdout_logfile =/var/log/interview/supervisor.log
stdout_logfile_maxbytes =50MB
stdout_logfile_backups =10
environment = PATH = "/home/interview/app/backend/venv/bin"
7.2 Start Services
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start interview_backend
sudo supervisorctl status
7.3 Enable Supervisor on Boot
sudo systemctl enable supervisor
Step 8: CORS Configuration
Update backend/app.py CORS settings for production:
from flask_cors import CORS
# Replace:
# CORS(app, supports_credentials=True)
# With:
CORS(app,
supports_credentials = True ,
origins = [
'https://yourdomain.com' ,
'https://www.yourdomain.com'
],
allow_headers = [ 'Content-Type' , 'Authorization' ],
expose_headers = [ 'Content-Type' , 'Authorization' ],
methods = [ 'GET' , 'POST' , 'PUT' , 'DELETE' , 'OPTIONS' ]
)
Step 9: Monitoring and Logging
9.1 Application Logs
Centralize logging:
# View backend logs
sudo tail -f /var/log/interview/gunicorn_access.log
sudo tail -f /var/log/interview/gunicorn_error.log
# View Nginx logs
sudo tail -f /var/nginx/access.log
sudo tail -f /var/nginx/error.log
9.2 System Monitoring
Install monitoring tools:
# Install htop for system monitoring
sudo apt install htop -y
# Install prometheus-node-exporter (optional)
sudo apt install prometheus-node-exporter -y
9.3 Log Rotation
Create /etc/logrotate.d/interview:
/var/log/interview/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 interview interview
sharedscripts
postrotate
supervisorctl restart interview_backend > /dev/null 2>&1
endscript
}
Step 10: Backup Strategy
10.1 Database Backups
Create backup script /home/interview/backup_db.sh:
#!/bin/bash
BACKUP_DIR = "/home/interview/backups/db"
DATE = $( date +%Y%m%d_%H%M%S )
mkdir -p $BACKUP_DIR
pg_dump -U interview_user interview_db | gzip > $BACKUP_DIR /interview_db_ $DATE .sql.gz
# Keep only last 30 days
find $BACKUP_DIR -name "*.sql.gz" -mtime +30 -delete
Make executable and schedule:
chmod +x /home/interview/backup_db.sh
# Add to crontab (daily at 2 AM)
crontab -e
0 2 * * * /home/interview/backup_db.sh
10.2 FAISS Index Backups
#!/bin/bash
BACKUP_DIR = "/home/interview/backups/faiss"
SOURCE_DIR = "/home/interview/app/data/processed/faiss_mistral"
DATE = $( date +%Y%m%d )
mkdir -p $BACKUP_DIR
tar -czf $BACKUP_DIR /faiss_index_ $DATE .tar.gz -C $SOURCE_DIR .
# Keep only last 7 days (large files)
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete
10.3 User Uploads Backups
rsync -av --delete /home/interview/app/uploads/ /home/interview/backups/uploads/
Security Hardening
11.1 Firewall Configuration
# Install UFW
sudo apt install ufw -y
# Allow SSH, HTTP, HTTPS
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Enable firewall
sudo ufw enable
sudo ufw status
11.2 Fail2Ban (Optional)
Protect against brute-force attacks:
sudo apt install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
11.3 Disable Directory Listing
In Nginx config, add:
11.4 Rate Limiting
Add to Nginx config:
limit_req_zone $ binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $ binary_remote_addr zone=login:10m rate=5r/m;
location /api {
limit_req zone=api burst=20 nodelay;
# ... rest of config
}
location /api/login {
limit_req zone=login burst=3 nodelay;
# ... rest of config
}
Scaling Considerations
12.1 Horizontal Scaling
For high traffic, consider:
Multiple Gunicorn workers : Already configured in gunicorn_config.py
Load balancer : Use Nginx upstream with multiple backend servers
Redis for sessions : Replace Flask sessions with Redis-backed sessions
Separate FAISS server : Move RAG to dedicated service
12.2 Vertical Scaling
Monitor and upgrade:
RAM : Most critical for FAISS indices (add 8GB per 100k documents)
CPU : Increase cores for concurrent user sessions
Storage : Monitor growth of uploads and database
12.3 Caching
Implement caching for:
Static files : Already handled by Nginx
API responses : Use Flask-Caching
FAISS queries : Cache frequent queries in Redis
Troubleshooting
Backend Not Starting
# Check supervisor logs
sudo supervisorctl tail interview_backend stderr
# Check Gunicorn logs
tail -f /var/log/interview/gunicorn_error.log
# Test manually
cd /home/interview/app/backend
source venv/bin/activate
python app.py
Database Connection Errors
# Verify PostgreSQL is running
sudo systemctl status postgresql
# Test connection
psql -U interview_user -d interview_db -h localhost
# Check DATABASE_URL in .env
cat backend/.env | grep DATABASE_URL
Nginx 502 Bad Gateway
# Check if backend is running
sudo supervisorctl status interview_backend
# Check backend is listening
sudo netstat -tulpn | grep 8000
# Check Nginx error log
sudo tail -f /var/log/nginx/error.log
WebSocket Connection Failed
Verify Nginx WebSocket configuration
Check firewall allows port 443
Ensure SSL certificate is valid
Test with: wscat -c wss://yourdomain.com/socket.io
Enable Gzip Compression
In Nginx config:
gzip on ;
gzip_vary on ;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
Database Connection Pooling
Add to config.py:
SQLALCHEMY_ENGINE_OPTIONS = {
'pool_size' : 10 ,
'pool_recycle' : 3600 ,
'pool_pre_ping' : True ,
}
Health Checks
Create health check endpoint in app.py:
@app.route ( '/health' )
def health_check ():
return jsonify({
'status' : 'healthy' ,
'database' : 'connected' if db.session.execute( 'SELECT 1' ).scalar() == 1 else 'error' ,
'timestamp' : datetime.utcnow().isoformat()
})
Next Steps
Requirements Review system requirements
Environment Variables Configure application settings