811 lines
16 KiB
Markdown
811 lines
16 KiB
Markdown
---
|
|
version: "1.0"
|
|
date: "2025-01-15"
|
|
author: "DWS Foldsite Team"
|
|
title: "Production Deployment"
|
|
description: "Deploying Foldsite for production use"
|
|
summary: "Complete guide to deploying Foldsite in production - from VPS servers to static hosting, with security, performance, and reliability best practices."
|
|
quick_tips:
|
|
- "Use Gunicorn with multiple workers for dynamic deployments"
|
|
- "Static export is fastest and cheapest for sites that don't change frequently"
|
|
- "Always use HTTPS in production with proper SSL certificates"
|
|
---
|
|
|
|
# Production Deployment
|
|
|
|
Deploy Foldsite to serve real traffic with reliability, security, and performance.
|
|
|
|
## Deployment Options
|
|
|
|
### Option 1: Dynamic Server (Python)
|
|
|
|
**Best for:**
|
|
- Frequently updated content
|
|
- Sites needing template helpers in real-time
|
|
- Admin file browser interface
|
|
- Dynamic content generation
|
|
|
|
**Characteristics:**
|
|
- Runs Python/Gunicorn server
|
|
- Content updates appear immediately
|
|
- Template helpers work dynamically
|
|
- Requires server with Python
|
|
|
|
**Hosting:** VPS, dedicated server, PaaS platforms
|
|
|
|
### Option 2: Static Export
|
|
|
|
**Best for:**
|
|
- Infrequently updated sites
|
|
- Maximum performance
|
|
- Minimal cost
|
|
- CDN delivery
|
|
|
|
**Characteristics:**
|
|
- Pre-rendered HTML files
|
|
- Blazing fast delivery
|
|
- Can host anywhere (GitHub Pages, Netlify, S3)
|
|
- Rebuild required for updates
|
|
|
|
**Hosting:** Static hosts, CDN, object storage
|
|
|
|
## Dynamic Server Deployment
|
|
|
|
### Prerequisites
|
|
|
|
- Linux server (Ubuntu 20.04+ recommended)
|
|
- Python 3.10+
|
|
- Domain name
|
|
- SSH access
|
|
|
|
### Step 1: Server Setup
|
|
|
|
**Update system:**
|
|
```bash
|
|
sudo apt update
|
|
sudo apt upgrade -y
|
|
```
|
|
|
|
**Install dependencies:**
|
|
```bash
|
|
# Python and pip
|
|
sudo apt install -y python3.10 python3-pip python3-venv
|
|
|
|
# Build tools
|
|
sudo apt install -y build-essential python3-dev
|
|
|
|
# Nginx
|
|
sudo apt install -y nginx
|
|
|
|
# Certbot for SSL
|
|
sudo apt install -y certbot python3-certbot-nginx
|
|
```
|
|
|
|
### Step 2: Deploy Foldsite
|
|
|
|
**Create user:**
|
|
```bash
|
|
sudo useradd -m -s /bin/bash foldsite
|
|
sudo su - foldsite
|
|
```
|
|
|
|
**Clone and setup:**
|
|
```bash
|
|
# Clone repository
|
|
git clone https://github.com/DWSresearch/foldsite.git
|
|
cd foldsite
|
|
|
|
# Create virtual environment
|
|
python3 -m venv venv
|
|
source venv/bin/activate
|
|
|
|
# Install dependencies
|
|
pip install -r requirements.txt
|
|
pip install gunicorn # Production WSGI server
|
|
```
|
|
|
|
**Setup your content:**
|
|
```bash
|
|
# Create site structure
|
|
mkdir -p ~/site/content
|
|
mkdir -p ~/site/templates
|
|
mkdir -p ~/site/styles
|
|
|
|
# Copy your content
|
|
# (upload via SCP, rsync, or git)
|
|
```
|
|
|
|
**Create production config:**
|
|
```bash
|
|
vim ~/foldsite/production-config.toml
|
|
```
|
|
|
|
```toml
|
|
[paths]
|
|
content_dir = "/home/foldsite/site/content"
|
|
templates_dir = "/home/foldsite/site/templates"
|
|
styles_dir = "/home/foldsite/site/styles"
|
|
|
|
[server]
|
|
listen_address = "127.0.0.1" # Only accept local connections
|
|
listen_port = 8081
|
|
admin_browser = false # Disable for security
|
|
max_threads = 8 # Adjust based on server
|
|
debug = false # Never enable in production
|
|
access_log = true
|
|
```
|
|
|
|
### Step 3: Systemd Service
|
|
|
|
Create service file:
|
|
|
|
```bash
|
|
sudo vim /etc/systemd/system/foldsite.service
|
|
```
|
|
|
|
```ini
|
|
[Unit]
|
|
Description=Foldsite Web Server
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=foldsite
|
|
Group=foldsite
|
|
WorkingDirectory=/home/foldsite/foldsite
|
|
Environment="PATH=/home/foldsite/foldsite/venv/bin"
|
|
ExecStart=/home/foldsite/foldsite/venv/bin/gunicorn \
|
|
--workers 4 \
|
|
--bind 127.0.0.1:8081 \
|
|
--access-logfile /var/log/foldsite/access.log \
|
|
--error-logfile /var/log/foldsite/error.log \
|
|
--config /home/foldsite/foldsite/production-config.toml \
|
|
main:app
|
|
|
|
Restart=always
|
|
RestartSec=10
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
|
|
**Create log directory:**
|
|
```bash
|
|
sudo mkdir -p /var/log/foldsite
|
|
sudo chown foldsite:foldsite /var/log/foldsite
|
|
```
|
|
|
|
**Enable and start service:**
|
|
```bash
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable foldsite
|
|
sudo systemctl start foldsite
|
|
|
|
# Check status
|
|
sudo systemctl status foldsite
|
|
```
|
|
|
|
### Step 4: Nginx Reverse Proxy
|
|
|
|
**Create Nginx config:**
|
|
```bash
|
|
sudo vim /etc/nginx/sites-available/foldsite
|
|
```
|
|
|
|
```nginx
|
|
upstream foldsite {
|
|
server 127.0.0.1:8081;
|
|
keepalive 32;
|
|
}
|
|
|
|
server {
|
|
listen 80;
|
|
server_name your-domain.com www.your-domain.com;
|
|
|
|
# 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;
|
|
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
|
|
|
# Logs
|
|
access_log /var/log/nginx/foldsite-access.log;
|
|
error_log /var/log/nginx/foldsite-error.log;
|
|
|
|
# Max upload size
|
|
client_max_body_size 10M;
|
|
|
|
# Proxy to Foldsite
|
|
location / {
|
|
proxy_pass http://foldsite;
|
|
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;
|
|
}
|
|
|
|
# Cache static assets
|
|
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
|
|
proxy_pass http://foldsite;
|
|
expires 30d;
|
|
add_header Cache-Control "public, immutable";
|
|
access_log off;
|
|
}
|
|
|
|
# Security: deny access to hidden files
|
|
location ~ /\. {
|
|
deny all;
|
|
access_log off;
|
|
log_not_found off;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Enable site:**
|
|
```bash
|
|
sudo ln -s /etc/nginx/sites-available/foldsite /etc/nginx/sites-enabled/
|
|
sudo nginx -t # Test configuration
|
|
sudo systemctl reload nginx
|
|
```
|
|
|
|
### Step 5: SSL Certificate
|
|
|
|
**Get certificate with Certbot:**
|
|
```bash
|
|
sudo certbot --nginx -d your-domain.com -d www.your-domain.com
|
|
```
|
|
|
|
Follow prompts. Certbot will:
|
|
- Obtain certificate from Let's Encrypt
|
|
- Modify Nginx config for HTTPS
|
|
- Setup auto-renewal
|
|
|
|
**Verify auto-renewal:**
|
|
```bash
|
|
sudo certbot renew --dry-run
|
|
```
|
|
|
|
**Final Nginx config (with SSL):**
|
|
Certbot updates your config to include:
|
|
|
|
```nginx
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name your-domain.com www.your-domain.com;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
|
ssl_prefer_server_ciphers on;
|
|
|
|
# ... rest of config
|
|
}
|
|
|
|
# Redirect HTTP to HTTPS
|
|
server {
|
|
listen 80;
|
|
server_name your-domain.com www.your-domain.com;
|
|
return 301 https://$server_name$request_uri;
|
|
}
|
|
```
|
|
|
|
### Step 6: Firewall
|
|
|
|
**Configure UFW:**
|
|
```bash
|
|
sudo ufw allow OpenSSH
|
|
sudo ufw allow 'Nginx Full'
|
|
sudo ufw enable
|
|
sudo ufw status
|
|
```
|
|
|
|
### Verification
|
|
|
|
Visit your domain:
|
|
```
|
|
https://your-domain.com
|
|
```
|
|
|
|
Should see your Foldsite with valid SSL!
|
|
|
|
## Static Export Deployment
|
|
|
|
### Generate Static Files
|
|
|
|
*Note: Static export functionality may need to be implemented or use a static site generator mode*
|
|
|
|
**Conceptual approach:**
|
|
|
|
```python
|
|
# export.py
|
|
import os
|
|
from pathlib import Path
|
|
from src.rendering.renderer import render_page
|
|
from src.config.config import Configuration
|
|
|
|
def export_static(config_path, output_dir):
|
|
"""Export all pages to static HTML"""
|
|
config = Configuration(config_path)
|
|
config.load_config()
|
|
|
|
output = Path(output_dir)
|
|
output.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Walk content directory
|
|
for content_file in config.content_dir.rglob('*'):
|
|
if content_file.is_file() and not content_file.name.startswith('___'):
|
|
# Generate output path
|
|
rel_path = content_file.relative_to(config.content_dir)
|
|
out_path = output / rel_path.with_suffix('.html')
|
|
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Render and save
|
|
html = render_page(
|
|
content_file,
|
|
base_path=config.content_dir,
|
|
template_path=config.templates_dir,
|
|
style_path=config.styles_dir
|
|
)
|
|
|
|
with open(out_path, 'w') as f:
|
|
f.write(html)
|
|
|
|
# Copy styles
|
|
import shutil
|
|
shutil.copytree(config.styles_dir, output / 'styles')
|
|
|
|
if __name__ == '__main__':
|
|
import sys
|
|
export_static(sys.argv[1], sys.argv[2])
|
|
```
|
|
|
|
**Usage:**
|
|
```bash
|
|
python export.py config.toml ./dist
|
|
```
|
|
|
|
### Deploy Static Files
|
|
|
|
#### GitHub Pages
|
|
|
|
```bash
|
|
# Export to docs/
|
|
python export.py config.toml ./docs
|
|
|
|
# Commit and push
|
|
git add docs/
|
|
git commit -m "Update site"
|
|
git push
|
|
|
|
# Enable Pages in GitHub repo settings
|
|
# Source: docs/ folder
|
|
```
|
|
|
|
#### Netlify
|
|
|
|
```bash
|
|
# Export
|
|
python export.py config.toml ./dist
|
|
|
|
# Install Netlify CLI
|
|
npm install -g netlify-cli
|
|
|
|
# Deploy
|
|
netlify deploy --prod --dir=dist
|
|
```
|
|
|
|
**Or use continuous deployment:**
|
|
|
|
```yaml
|
|
# netlify.toml
|
|
[build]
|
|
command = "pip install -r requirements.txt && python export.py config.toml dist"
|
|
publish = "dist"
|
|
```
|
|
|
|
#### AWS S3 + CloudFront
|
|
|
|
```bash
|
|
# Export
|
|
python export.py config.toml ./dist
|
|
|
|
# Sync to S3
|
|
aws s3 sync ./dist s3://your-bucket-name --delete
|
|
|
|
# Invalidate CloudFront cache
|
|
aws cloudfront create-invalidation --distribution-id YOUR_DIST_ID --paths "/*"
|
|
```
|
|
|
|
#### Vercel
|
|
|
|
```bash
|
|
# Export
|
|
python export.py config.toml ./dist
|
|
|
|
# Deploy
|
|
vercel --prod ./dist
|
|
```
|
|
|
|
## Performance Optimization
|
|
|
|
### Nginx Caching
|
|
|
|
Add to Nginx config:
|
|
|
|
```nginx
|
|
# Define cache path
|
|
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=foldsite_cache:10m max_size=1g inactive=60m use_temp_path=off;
|
|
|
|
server {
|
|
# ...
|
|
|
|
location / {
|
|
proxy_cache foldsite_cache;
|
|
proxy_cache_valid 200 10m;
|
|
proxy_cache_valid 404 1m;
|
|
proxy_cache_bypass $cookie_session;
|
|
proxy_no_cache $cookie_session;
|
|
add_header X-Cache-Status $upstream_cache_status;
|
|
|
|
proxy_pass http://foldsite;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Gzip Compression
|
|
|
|
```nginx
|
|
# In /etc/nginx/nginx.conf
|
|
gzip on;
|
|
gzip_vary on;
|
|
gzip_min_length 1024;
|
|
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json;
|
|
```
|
|
|
|
### Image Optimization
|
|
|
|
Foldsite automatically generates thumbnails, but optimize source images:
|
|
|
|
```bash
|
|
# Install optimization tools
|
|
sudo apt install jpegoptim optipng
|
|
|
|
# Optimize JPEGs
|
|
find content/ -name "*.jpg" -exec jpegoptim --strip-all {} \;
|
|
|
|
# Optimize PNGs
|
|
find content/ -name "*.png" -exec optipng -o2 {} \;
|
|
```
|
|
|
|
### CDN Integration
|
|
|
|
Use CDN for static assets:
|
|
|
|
```nginx
|
|
# Separate static assets
|
|
location /styles/ {
|
|
alias /home/foldsite/site/styles/;
|
|
expires 1y;
|
|
add_header Cache-Control "public, immutable";
|
|
}
|
|
|
|
location /download/ {
|
|
# Proxy to Foldsite for thumbnails
|
|
proxy_pass http://foldsite;
|
|
expires 30d;
|
|
}
|
|
```
|
|
|
|
Then point DNS for `static.yourdomain.com` to CDN origin.
|
|
|
|
## Monitoring & Logging
|
|
|
|
### Application Logs
|
|
|
|
**View logs:**
|
|
```bash
|
|
# Systemd logs
|
|
sudo journalctl -u foldsite -f
|
|
|
|
# Application logs
|
|
tail -f /var/log/foldsite/error.log
|
|
tail -f /var/log/foldsite/access.log
|
|
```
|
|
|
|
### Nginx Logs
|
|
|
|
```bash
|
|
tail -f /var/log/nginx/foldsite-access.log
|
|
tail -f /var/log/nginx/foldsite-error.log
|
|
```
|
|
|
|
### Log Rotation
|
|
|
|
Create `/etc/logrotate.d/foldsite`:
|
|
|
|
```
|
|
/var/log/foldsite/*.log {
|
|
daily
|
|
missingok
|
|
rotate 14
|
|
compress
|
|
delaycompress
|
|
notifempty
|
|
create 0640 foldsite foldsite
|
|
sharedscripts
|
|
postrotate
|
|
systemctl reload foldsite
|
|
endscript
|
|
}
|
|
```
|
|
|
|
### Monitoring Tools
|
|
|
|
**Install basic monitoring:**
|
|
```bash
|
|
# Netdata (system monitoring)
|
|
bash <(curl -Ss https://my-netdata.io/kickstart.sh)
|
|
|
|
# Access at http://your-server:19999
|
|
```
|
|
|
|
**Check application health:**
|
|
```bash
|
|
#!/bin/bash
|
|
# health-check.sh
|
|
response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8081/)
|
|
|
|
if [ $response -eq 200 ]; then
|
|
echo "Foldsite is healthy"
|
|
exit 0
|
|
else
|
|
echo "Foldsite is down (HTTP $response)"
|
|
systemctl restart foldsite
|
|
exit 1
|
|
fi
|
|
```
|
|
|
|
Run via cron:
|
|
```bash
|
|
*/5 * * * * /usr/local/bin/health-check.sh >> /var/log/foldsite/health.log 2>&1
|
|
```
|
|
|
|
## Backup Strategy
|
|
|
|
### Content Backup
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# backup.sh
|
|
BACKUP_DIR="/backups/foldsite"
|
|
DATE=$(date +%Y%m%d_%H%M%S)
|
|
|
|
mkdir -p $BACKUP_DIR
|
|
|
|
# Backup content, templates, styles
|
|
tar czf $BACKUP_DIR/site-$DATE.tar.gz \
|
|
/home/foldsite/site/
|
|
|
|
# Keep only last 30 days
|
|
find $BACKUP_DIR -name "site-*.tar.gz" -mtime +30 -delete
|
|
|
|
echo "Backup completed: site-$DATE.tar.gz"
|
|
```
|
|
|
|
**Run daily via cron:**
|
|
```bash
|
|
0 2 * * * /usr/local/bin/backup.sh
|
|
```
|
|
|
|
### Remote Backup
|
|
|
|
```bash
|
|
# Sync to remote server
|
|
rsync -avz /home/foldsite/site/ user@backup-server:/backups/foldsite/
|
|
|
|
# Or sync to S3
|
|
aws s3 sync /home/foldsite/site/ s3://your-backup-bucket/foldsite/
|
|
```
|
|
|
|
## Updating Foldsite
|
|
|
|
### Update Process
|
|
|
|
```bash
|
|
# As foldsite user
|
|
cd ~/foldsite
|
|
|
|
# Pull latest code
|
|
git pull
|
|
|
|
# Activate venv
|
|
source venv/bin/activate
|
|
|
|
# Update dependencies
|
|
pip install -r requirements.txt
|
|
|
|
# Restart service
|
|
sudo systemctl restart foldsite
|
|
|
|
# Check logs
|
|
sudo journalctl -u foldsite -n 50
|
|
```
|
|
|
|
### Zero-Downtime Updates
|
|
|
|
Use multiple Gunicorn workers and graceful reloading:
|
|
|
|
```bash
|
|
# Graceful reload (workers restart one by one)
|
|
sudo systemctl reload foldsite
|
|
|
|
# Or send HUP signal to Gunicorn
|
|
sudo pkill -HUP gunicorn
|
|
```
|
|
|
|
## Security Hardening
|
|
|
|
### Disable Directory Listing
|
|
|
|
Nginx automatically prevents this, but verify:
|
|
|
|
```nginx
|
|
autoindex off;
|
|
```
|
|
|
|
### Rate Limiting
|
|
|
|
Add to Nginx config:
|
|
|
|
```nginx
|
|
limit_req_zone $binary_remote_addr zone=foldsite_limit:10m rate=10r/s;
|
|
|
|
server {
|
|
location / {
|
|
limit_req zone=foldsite_limit burst=20 nodelay;
|
|
# ... rest of config
|
|
}
|
|
}
|
|
```
|
|
|
|
### Fail2ban
|
|
|
|
Protect against brute force:
|
|
|
|
```bash
|
|
sudo apt install fail2ban
|
|
|
|
# Create /etc/fail2ban/jail.local
|
|
[nginx-foldsite]
|
|
enabled = true
|
|
port = http,https
|
|
filter = nginx-foldsite
|
|
logpath = /var/log/nginx/foldsite-access.log
|
|
maxretry = 5
|
|
bantime = 3600
|
|
```
|
|
|
|
### Security Headers
|
|
|
|
Already in Nginx config, but verify:
|
|
|
|
```nginx
|
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header X-XSS-Protection "1; mode=block" always;
|
|
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
|
add_header Content-Security-Policy "default-src 'self' https:; script-src 'self' 'unsafe-inline' https:; style-src 'self' 'unsafe-inline' https:;" always;
|
|
```
|
|
|
|
### File Permissions
|
|
|
|
Ensure proper permissions:
|
|
|
|
```bash
|
|
# Content should be readable by foldsite user
|
|
chmod -R 755 ~/site/content
|
|
chmod -R 755 ~/site/templates
|
|
chmod -R 755 ~/site/styles
|
|
|
|
# Application should not be writable
|
|
chmod -R 555 ~/foldsite/src
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Service Won't Start
|
|
|
|
```bash
|
|
# Check logs
|
|
sudo journalctl -u foldsite -xe
|
|
|
|
# Common issues:
|
|
# - Wrong Python path
|
|
# - Missing dependencies
|
|
# - Port already in use
|
|
# - Permission errors
|
|
```
|
|
|
|
### 502 Bad Gateway
|
|
|
|
Nginx can't reach Foldsite:
|
|
|
|
```bash
|
|
# Check if Foldsite is running
|
|
sudo systemctl status foldsite
|
|
|
|
# Check if port is listening
|
|
sudo netstat -tulpn | grep 8081
|
|
|
|
# Check Nginx error log
|
|
sudo tail /var/log/nginx/error.log
|
|
```
|
|
|
|
### High Memory Usage
|
|
|
|
```bash
|
|
# Check process memory
|
|
ps aux | grep gunicorn
|
|
|
|
# Reduce workers or add swap
|
|
sudo fallocate -l 2G /swapfile
|
|
sudo chmod 600 /swapfile
|
|
sudo mkswap /swapfile
|
|
sudo swapon /swapfile
|
|
```
|
|
|
|
### Slow Response Times
|
|
|
|
```bash
|
|
# Check Nginx access logs for slow requests
|
|
sudo tail -f /var/log/nginx/foldsite-access.log
|
|
|
|
# Enable query logging in Foldsite
|
|
# Check for expensive template helpers
|
|
# Consider caching with Redis/Memcached
|
|
```
|
|
|
|
## Platform-Specific Guides
|
|
|
|
### DigitalOcean
|
|
|
|
```bash
|
|
# Create droplet (Ubuntu 22.04)
|
|
# Follow server setup steps above
|
|
|
|
# Use DigitalOcean firewall for security
|
|
# Enable backups in control panel
|
|
```
|
|
|
|
### AWS EC2
|
|
|
|
```bash
|
|
# Launch Ubuntu instance
|
|
# Setup security groups (ports 22, 80, 443)
|
|
# Use Elastic IP for static IP
|
|
# Consider RDS for database if needed
|
|
```
|
|
|
|
### Hetzner Cloud
|
|
|
|
```bash
|
|
# Create CX11 or larger instance
|
|
# Follow server setup
|
|
# Use Hetzner firewall
|
|
# Consider Hetzner volumes for storage
|
|
```
|
|
|
|
## Next Steps
|
|
|
|
- **[Local Development](local-development.md)** - Development workflow
|
|
- **[Docker Deployment](docker.md)** - Container deployment
|
|
- **[Support](../support.md)** - Get help
|
|
|
|
Your Foldsite is now production-ready! Monitor it regularly, keep it updated, and enjoy your self-hosted corner of the internet.
|