docs refactor
All checks were successful
All checks were successful
This commit is contained in:
735
docs/content/deployment/docker.md
Normal file
735
docs/content/deployment/docker.md
Normal file
@ -0,0 +1,735 @@
|
||||
---
|
||||
version: "1.0"
|
||||
date: "2025-01-15"
|
||||
author: "DWS Foldsite Team"
|
||||
title: "Docker Deployment"
|
||||
description: "Running Foldsite in Docker containers"
|
||||
summary: "Complete guide to deploying Foldsite with Docker for consistent, isolated, and portable deployments."
|
||||
quick_tips:
|
||||
- "Docker ensures consistent environments across development and production"
|
||||
- "Use docker-compose for easy multi-container setup"
|
||||
- "Mount content as volumes for live updates without rebuilding"
|
||||
---
|
||||
|
||||
# Docker Deployment
|
||||
|
||||
Docker provides isolated, reproducible deployments of Foldsite. Perfect for testing, staging, and production environments.
|
||||
|
||||
## Why Docker?
|
||||
|
||||
**Benefits:**
|
||||
- **Consistency** - Same environment everywhere
|
||||
- **Isolation** - Dependencies don't conflict with system
|
||||
- **Portability** - Run anywhere Docker runs
|
||||
- **Easy deployment** - Single command to start
|
||||
- **Version control** - Docker images are versioned
|
||||
|
||||
**Use cases:**
|
||||
- Team development (everyone has same environment)
|
||||
- Staging environments before production
|
||||
- Production deployments
|
||||
- CI/CD pipelines
|
||||
- Cloud platform deployments
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Install Docker
|
||||
|
||||
**macOS:**
|
||||
```bash
|
||||
# Download Docker Desktop from docker.com
|
||||
# Or use Homebrew
|
||||
brew install --cask docker
|
||||
```
|
||||
|
||||
**Linux (Ubuntu/Debian):**
|
||||
```bash
|
||||
# Update package index
|
||||
sudo apt update
|
||||
|
||||
# Install Docker
|
||||
sudo apt install docker.io docker-compose
|
||||
|
||||
# Add your user to docker group
|
||||
sudo usermod -aG docker $USER
|
||||
|
||||
# Log out and back in for group changes
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
```bash
|
||||
# Download Docker Desktop from docker.com
|
||||
# Requires WSL2
|
||||
```
|
||||
|
||||
**Verify installation:**
|
||||
```bash
|
||||
docker --version
|
||||
docker-compose --version
|
||||
```
|
||||
|
||||
## Quick Start with Docker Compose
|
||||
|
||||
### Step 1: Create docker-compose.yml
|
||||
|
||||
In your Foldsite directory:
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
foldsite:
|
||||
image: python:3.11-slim
|
||||
container_name: foldsite
|
||||
working_dir: /app
|
||||
command: >
|
||||
sh -c "pip install -r requirements.txt &&
|
||||
python main.py --config config.toml"
|
||||
ports:
|
||||
- "8081:8081"
|
||||
volumes:
|
||||
- .:/app
|
||||
- ./my-site/content:/app/content
|
||||
- ./my-site/templates:/app/templates
|
||||
- ./my-site/styles:/app/styles
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Step 2: Create config.toml for Docker
|
||||
|
||||
```toml
|
||||
[paths]
|
||||
content_dir = "/app/content"
|
||||
templates_dir = "/app/templates"
|
||||
styles_dir = "/app/styles"
|
||||
|
||||
[server]
|
||||
listen_address = "0.0.0.0" # Important: bind to all interfaces
|
||||
listen_port = 8081
|
||||
admin_browser = false
|
||||
max_threads = 4
|
||||
debug = false
|
||||
access_log = true
|
||||
```
|
||||
|
||||
### Step 3: Start Container
|
||||
|
||||
```bash
|
||||
# Start in background
|
||||
docker-compose up -d
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
|
||||
# Stop
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
Visit `http://localhost:8081` to see your site!
|
||||
|
||||
## Building a Custom Docker Image
|
||||
|
||||
For production, build a dedicated Foldsite image:
|
||||
|
||||
### Create Dockerfile
|
||||
|
||||
```dockerfile
|
||||
# Dockerfile
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
build-essential \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy requirements first (for caching)
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Create directories for content
|
||||
RUN mkdir -p /content /templates /styles
|
||||
|
||||
# Expose port
|
||||
EXPOSE 8081
|
||||
|
||||
# Run application
|
||||
CMD ["python", "main.py", "--config", "/app/config.toml"]
|
||||
```
|
||||
|
||||
### Build Image
|
||||
|
||||
```bash
|
||||
# Build image
|
||||
docker build -t foldsite:latest .
|
||||
|
||||
# Tag for versioning
|
||||
docker tag foldsite:latest foldsite:1.0.0
|
||||
```
|
||||
|
||||
### Run Container
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name foldsite \
|
||||
-p 8081:8081 \
|
||||
-v $(pwd)/my-site/content:/content \
|
||||
-v $(pwd)/my-site/templates:/templates \
|
||||
-v $(pwd)/my-site/styles:/styles \
|
||||
foldsite:latest
|
||||
```
|
||||
|
||||
## Development with Docker
|
||||
|
||||
### Hot Reload Setup
|
||||
|
||||
Mount your code as volumes for live updates:
|
||||
|
||||
```yaml
|
||||
# docker-compose.dev.yml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
foldsite:
|
||||
build: .
|
||||
container_name: foldsite-dev
|
||||
ports:
|
||||
- "8081:8081"
|
||||
volumes:
|
||||
# Mount everything for development
|
||||
- .:/app
|
||||
- ./my-site/content:/app/content
|
||||
- ./my-site/templates:/app/templates
|
||||
- ./my-site/styles:/app/styles
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
- FLASK_ENV=development
|
||||
command: >
|
||||
sh -c "pip install -r requirements.txt &&
|
||||
python main.py --config config.toml"
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
# Start development environment
|
||||
docker-compose -f docker-compose.dev.yml up
|
||||
|
||||
# Changes to content, templates, and styles appear immediately
|
||||
# Code changes require restart
|
||||
```
|
||||
|
||||
### Interactive Development
|
||||
|
||||
Run commands inside container:
|
||||
|
||||
```bash
|
||||
# Start bash session in running container
|
||||
docker exec -it foldsite bash
|
||||
|
||||
# Inside container, you can:
|
||||
python main.py --config config.toml
|
||||
pip install new-package
|
||||
ls /app/content
|
||||
```
|
||||
|
||||
## Production Docker Setup
|
||||
|
||||
### Multi-Stage Build
|
||||
|
||||
Optimize image size with multi-stage build:
|
||||
|
||||
```dockerfile
|
||||
# Dockerfile.production
|
||||
# Stage 1: Build dependencies
|
||||
FROM python:3.11-slim as builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install build dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
build-essential \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Python dependencies
|
||||
COPY requirements.txt .
|
||||
RUN pip install --user --no-cache-dir -r requirements.txt
|
||||
|
||||
# Stage 2: Runtime
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy Python dependencies from builder
|
||||
COPY --from=builder /root/.local /root/.local
|
||||
|
||||
# Copy application
|
||||
COPY . .
|
||||
|
||||
# Create non-root user
|
||||
RUN useradd -m -u 1000 foldsite && \
|
||||
chown -R foldsite:foldsite /app
|
||||
|
||||
# Switch to non-root user
|
||||
USER foldsite
|
||||
|
||||
# Make sure scripts in .local are usable
|
||||
ENV PATH=/root/.local/bin:$PATH
|
||||
|
||||
EXPOSE 8081
|
||||
|
||||
# Use Gunicorn for production
|
||||
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8081", "main:app"]
|
||||
```
|
||||
|
||||
### Production docker-compose.yml
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
foldsite:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.production
|
||||
container_name: foldsite-prod
|
||||
ports:
|
||||
- "8081:8081"
|
||||
volumes:
|
||||
- ./content:/app/content:ro # Read-only
|
||||
- ./templates:/app/templates:ro
|
||||
- ./styles:/app/styles:ro
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
restart: always
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8081/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
## Docker Compose Examples
|
||||
|
||||
### With Nginx Reverse Proxy
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
foldsite:
|
||||
build: .
|
||||
container_name: foldsite
|
||||
expose:
|
||||
- "8081"
|
||||
volumes:
|
||||
- ./content:/app/content:ro
|
||||
- ./templates:/app/templates:ro
|
||||
- ./styles:/app/styles:ro
|
||||
restart: always
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: nginx
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./ssl:/etc/nginx/ssl:ro
|
||||
depends_on:
|
||||
- foldsite
|
||||
restart: always
|
||||
```
|
||||
|
||||
**nginx.conf:**
|
||||
```nginx
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
upstream foldsite {
|
||||
server foldsite:8081;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-domain.com;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
# Cache static assets
|
||||
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
|
||||
proxy_pass http://foldsite;
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Multiple Sites
|
||||
|
||||
Run multiple Foldsite instances:
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
blog:
|
||||
build: .
|
||||
ports:
|
||||
- "8081:8081"
|
||||
volumes:
|
||||
- ./blog/content:/app/content
|
||||
- ./blog/templates:/app/templates
|
||||
- ./blog/styles:/app/styles
|
||||
restart: always
|
||||
|
||||
portfolio:
|
||||
build: .
|
||||
ports:
|
||||
- "8082:8081"
|
||||
volumes:
|
||||
- ./portfolio/content:/app/content
|
||||
- ./portfolio/templates:/app/templates
|
||||
- ./portfolio/styles:/app/styles
|
||||
restart: always
|
||||
```
|
||||
|
||||
## Volume Management
|
||||
|
||||
### Content Volumes
|
||||
|
||||
**Development** - Mount host directories:
|
||||
```yaml
|
||||
volumes:
|
||||
- ./my-site/content:/app/content
|
||||
- ./my-site/templates:/app/templates
|
||||
- ./my-site/styles:/app/styles
|
||||
```
|
||||
|
||||
**Production** - Read-only mounts:
|
||||
```yaml
|
||||
volumes:
|
||||
- ./content:/app/content:ro
|
||||
- ./templates:/app/templates:ro
|
||||
- ./styles:/app/styles:ro
|
||||
```
|
||||
|
||||
### Named Volumes
|
||||
|
||||
For persistent data:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
foldsite:
|
||||
volumes:
|
||||
- content-data:/app/content
|
||||
- templates-data:/app/templates
|
||||
|
||||
volumes:
|
||||
content-data:
|
||||
templates-data:
|
||||
```
|
||||
|
||||
**Backup named volumes:**
|
||||
```bash
|
||||
# Backup
|
||||
docker run --rm \
|
||||
-v foldsite_content-data:/data \
|
||||
-v $(pwd):/backup \
|
||||
alpine tar czf /backup/content-backup.tar.gz -C /data .
|
||||
|
||||
# Restore
|
||||
docker run --rm \
|
||||
-v foldsite_content-data:/data \
|
||||
-v $(pwd):/backup \
|
||||
alpine tar xzf /backup/content-backup.tar.gz -C /data
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Pass configuration via environment:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
foldsite:
|
||||
environment:
|
||||
- FOLDSITE_DEBUG=false
|
||||
- FOLDSITE_PORT=8081
|
||||
- FOLDSITE_MAX_THREADS=4
|
||||
```
|
||||
|
||||
**Use in config:**
|
||||
```python
|
||||
# In a config loader
|
||||
import os
|
||||
|
||||
debug = os.getenv('FOLDSITE_DEBUG', 'false').lower() == 'true'
|
||||
port = int(os.getenv('FOLDSITE_PORT', '8081'))
|
||||
```
|
||||
|
||||
## Common Docker Commands
|
||||
|
||||
### Container Management
|
||||
|
||||
```bash
|
||||
# Start containers
|
||||
docker-compose up -d
|
||||
|
||||
# Stop containers
|
||||
docker-compose down
|
||||
|
||||
# Restart
|
||||
docker-compose restart
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
|
||||
# View logs for specific service
|
||||
docker-compose logs -f foldsite
|
||||
|
||||
# Exec into running container
|
||||
docker exec -it foldsite bash
|
||||
|
||||
# View running containers
|
||||
docker ps
|
||||
|
||||
# View all containers (including stopped)
|
||||
docker ps -a
|
||||
```
|
||||
|
||||
### Image Management
|
||||
|
||||
```bash
|
||||
# Build image
|
||||
docker-compose build
|
||||
|
||||
# Pull images
|
||||
docker-compose pull
|
||||
|
||||
# List images
|
||||
docker images
|
||||
|
||||
# Remove unused images
|
||||
docker image prune
|
||||
|
||||
# Remove all unused data
|
||||
docker system prune -a
|
||||
```
|
||||
|
||||
### Debugging
|
||||
|
||||
```bash
|
||||
# Check container logs
|
||||
docker logs foldsite
|
||||
|
||||
# Follow logs
|
||||
docker logs -f foldsite
|
||||
|
||||
# Inspect container
|
||||
docker inspect foldsite
|
||||
|
||||
# View container stats
|
||||
docker stats foldsite
|
||||
|
||||
# Check container health
|
||||
docker ps --filter health=healthy
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Port Already in Use
|
||||
|
||||
```
|
||||
Error: port is already allocated
|
||||
```
|
||||
|
||||
**Solution:** Change port mapping:
|
||||
```yaml
|
||||
ports:
|
||||
- "8082:8081" # Map to different host port
|
||||
```
|
||||
|
||||
### Permission Errors
|
||||
|
||||
```
|
||||
PermissionError: [Errno 13] Permission denied
|
||||
```
|
||||
|
||||
**Solution:** Fix volume permissions:
|
||||
```bash
|
||||
# Fix ownership
|
||||
sudo chown -R $USER:$USER ./my-site
|
||||
|
||||
# Or run container as your user
|
||||
docker run --user $(id -u):$(id -g) ...
|
||||
```
|
||||
|
||||
### Container Won't Start
|
||||
|
||||
```bash
|
||||
# Check logs
|
||||
docker-compose logs
|
||||
|
||||
# Common issues:
|
||||
# 1. Missing requirements.txt
|
||||
# 2. Wrong working directory
|
||||
# 3. Port conflicts
|
||||
# 4. Volume mount errors
|
||||
```
|
||||
|
||||
### Changes Not Appearing
|
||||
|
||||
**Content changes:**
|
||||
- Should appear immediately (volumes mounted)
|
||||
- Try hard refresh in browser
|
||||
|
||||
**Code changes:**
|
||||
- Require container restart: `docker-compose restart`
|
||||
|
||||
**Template changes:**
|
||||
- Should appear immediately
|
||||
- Check volume mounts are correct
|
||||
|
||||
### Container Crashes
|
||||
|
||||
```bash
|
||||
# View exit reason
|
||||
docker ps -a
|
||||
|
||||
# Check logs
|
||||
docker logs foldsite
|
||||
|
||||
# Try running interactively
|
||||
docker run -it foldsite bash
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Resource Limits
|
||||
|
||||
Limit container resources:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
foldsite:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2.0'
|
||||
memory: 1G
|
||||
reservations:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
```
|
||||
|
||||
### Build Cache
|
||||
|
||||
Speed up builds:
|
||||
|
||||
```bash
|
||||
# Use BuildKit
|
||||
DOCKER_BUILDKIT=1 docker build .
|
||||
|
||||
# Cache from registry
|
||||
docker build --cache-from myregistry/foldsite:latest .
|
||||
```
|
||||
|
||||
### Layer Optimization
|
||||
|
||||
Order Dockerfile for better caching:
|
||||
|
||||
```dockerfile
|
||||
# Dependencies first (change rarely)
|
||||
COPY requirements.txt .
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
# Code last (changes often)
|
||||
COPY . .
|
||||
```
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
### GitHub Actions Example
|
||||
|
||||
```yaml
|
||||
# .github/workflows/docker.yml
|
||||
name: Build Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Build image
|
||||
run: docker build -t foldsite:${{ github.sha }} .
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
docker run -d --name test foldsite:${{ github.sha }}
|
||||
docker logs test
|
||||
docker stop test
|
||||
```
|
||||
|
||||
## Cloud Platform Deployment
|
||||
|
||||
### Deploy to AWS ECS
|
||||
|
||||
```bash
|
||||
# Build and push to ECR
|
||||
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com
|
||||
docker tag foldsite:latest 123456789.dkr.ecr.us-east-1.amazonaws.com/foldsite:latest
|
||||
docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/foldsite:latest
|
||||
```
|
||||
|
||||
### Deploy to Google Cloud Run
|
||||
|
||||
```bash
|
||||
# Build and push to GCR
|
||||
gcloud builds submit --tag gcr.io/PROJECT_ID/foldsite
|
||||
gcloud run deploy --image gcr.io/PROJECT_ID/foldsite --platform managed
|
||||
```
|
||||
|
||||
### Deploy to Azure Container Instances
|
||||
|
||||
```bash
|
||||
# Create container instance
|
||||
az container create \
|
||||
--resource-group myResourceGroup \
|
||||
--name foldsite \
|
||||
--image myregistry.azurecr.io/foldsite:latest \
|
||||
--ports 8081
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- **[Production Deployment](production.md)** - Production-grade setup
|
||||
- **[Local Development](local-development.md)** - Development workflow
|
||||
- **[Support](../support.md)** - Get help
|
||||
|
||||
Docker provides a solid foundation for deploying Foldsite anywhere. From development to production, containers ensure consistency and reliability.
|
380
docs/content/deployment/index.md
Normal file
380
docs/content/deployment/index.md
Normal file
@ -0,0 +1,380 @@
|
||||
---
|
||||
version: "1.0"
|
||||
date: "2025-01-15"
|
||||
author: "DWS Foldsite Team"
|
||||
title: "Deployment Overview"
|
||||
description: "Getting Foldsite running - from local development to production"
|
||||
summary: "Learn how to deploy Foldsite in various environments: local development, Docker containers, or production servers."
|
||||
quick_tips:
|
||||
- "Start with local development for fastest iteration"
|
||||
- "Docker provides consistent environments across machines"
|
||||
- "Production deployment supports both dynamic and static modes"
|
||||
---
|
||||
|
||||
# Deployment Overview
|
||||
|
||||
Foldsite is flexible in how you run it. Choose the deployment method that fits your needs:
|
||||
|
||||
## Deployment Options
|
||||
|
||||
### [Local Development](local-development.md)
|
||||
|
||||
**Best for:** Content creation, theme development, testing
|
||||
|
||||
Run Foldsite directly with Python for the fastest development cycle. Changes to content and templates appear immediately (no rebuild needed).
|
||||
|
||||
```bash
|
||||
python main.py --config config.toml
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- Fastest iteration - see changes instantly
|
||||
- Easy debugging with Python stack traces
|
||||
- Full access to logs and error messages
|
||||
|
||||
**Cons:**
|
||||
- Requires Python environment
|
||||
- Manual dependency management
|
||||
- Not suitable for production
|
||||
|
||||
**When to use:** Always use this during development. It's the fastest way to see your changes.
|
||||
|
||||
### [Docker Deployment](docker.md)
|
||||
|
||||
**Best for:** Consistent environments, easy deployment, testing production builds
|
||||
|
||||
Run Foldsite in a Docker container for isolated, reproducible deployments.
|
||||
|
||||
```bash
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- Consistent across development and production
|
||||
- Isolated dependencies
|
||||
- Easy to share with team members
|
||||
- Simplifies deployment to cloud platforms
|
||||
|
||||
**Cons:**
|
||||
- Slight overhead from containerization
|
||||
- Requires Docker knowledge
|
||||
- Extra layer to debug
|
||||
|
||||
**When to use:** Use Docker when you need consistency across environments, or when deploying to platforms that support containers.
|
||||
|
||||
### [Production Deployment](production.md)
|
||||
|
||||
**Best for:** Public-facing websites, high-traffic sites, static hosting
|
||||
|
||||
Deploy Foldsite as either a dynamic Python server or export to static files.
|
||||
|
||||
**Dynamic Mode:**
|
||||
```bash
|
||||
gunicorn -w 4 -b 0.0.0.0:8081 main:app
|
||||
```
|
||||
|
||||
**Static Export:**
|
||||
```bash
|
||||
# Generate static HTML files
|
||||
python export.py --output ./dist
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- Optimized for production workloads
|
||||
- Can use static hosting (cheap/free)
|
||||
- Supports CDN caching
|
||||
- Professional-grade performance
|
||||
|
||||
**Cons:**
|
||||
- More complex setup
|
||||
- Requires understanding of web servers
|
||||
- Static mode requires rebuilds for updates
|
||||
|
||||
**When to use:** Use this for your live website once development is complete.
|
||||
|
||||
## Quick Comparison
|
||||
|
||||
| Method | Speed | Complexity | Best For |
|
||||
|--------|-------|------------|----------|
|
||||
| **Local Development** | ⚡⚡⚡ | ⭐ | Creating content and themes |
|
||||
| **Docker** | ⚡⚡ | ⭐⭐ | Team collaboration, staging |
|
||||
| **Production (Dynamic)** | ⚡⚡ | ⭐⭐⭐ | Live sites with frequent updates |
|
||||
| **Production (Static)** | ⚡⚡⚡ | ⭐⭐⭐ | Live sites, maximum performance |
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### For All Deployment Methods
|
||||
|
||||
1. **Git** (to clone the repository)
|
||||
```bash
|
||||
git --version
|
||||
```
|
||||
|
||||
2. **A text editor** (VS Code, Sublime, vim, etc.)
|
||||
|
||||
### For Local Development
|
||||
|
||||
1. **Python 3.10+**
|
||||
```bash
|
||||
python3 --version
|
||||
```
|
||||
|
||||
2. **pip** (Python package manager)
|
||||
```bash
|
||||
pip --version
|
||||
```
|
||||
|
||||
### For Docker Deployment
|
||||
|
||||
1. **Docker** and **Docker Compose**
|
||||
```bash
|
||||
docker --version
|
||||
docker-compose --version
|
||||
```
|
||||
|
||||
### For Production Deployment
|
||||
|
||||
Choose based on your hosting strategy:
|
||||
|
||||
- **Dynamic mode:** Python 3.10+, web server (nginx/apache)
|
||||
- **Static mode:** Any web server or static host (GitHub Pages, Netlify, etc.)
|
||||
|
||||
## Getting the Source Code
|
||||
|
||||
All deployment methods start by getting Foldsite:
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/DWSresearch/foldsite.git
|
||||
cd foldsite
|
||||
|
||||
# Or download and extract the latest release
|
||||
wget https://github.com/DWSresearch/foldsite/archive/main.zip
|
||||
unzip main.zip
|
||||
cd foldsite-main
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Every deployment uses a `config.toml` file to specify paths and settings:
|
||||
|
||||
```toml
|
||||
[paths]
|
||||
content_dir = "/path/to/your/content"
|
||||
templates_dir = "/path/to/your/templates"
|
||||
styles_dir = "/path/to/your/styles"
|
||||
|
||||
[server]
|
||||
listen_address = "0.0.0.0"
|
||||
listen_port = 8081
|
||||
admin_browser = false
|
||||
admin_password = "change-me"
|
||||
max_threads = 4
|
||||
debug = false
|
||||
access_log = true
|
||||
```
|
||||
|
||||
**Important:** Adjust these paths before running Foldsite!
|
||||
|
||||
### Development Config Example
|
||||
|
||||
```toml
|
||||
[paths]
|
||||
content_dir = "./my-site/content"
|
||||
templates_dir = "./my-site/templates"
|
||||
styles_dir = "./my-site/styles"
|
||||
|
||||
[server]
|
||||
listen_address = "127.0.0.1" # Local only
|
||||
listen_port = 8081
|
||||
debug = true # Enable debug mode
|
||||
access_log = true
|
||||
```
|
||||
|
||||
### Production Config Example
|
||||
|
||||
```toml
|
||||
[paths]
|
||||
content_dir = "/var/www/site/content"
|
||||
templates_dir = "/var/www/site/templates"
|
||||
styles_dir = "/var/www/site/styles"
|
||||
|
||||
[server]
|
||||
listen_address = "0.0.0.0" # All interfaces
|
||||
listen_port = 8081
|
||||
debug = false # Disable debug mode
|
||||
access_log = true
|
||||
max_threads = 8 # More workers for production
|
||||
```
|
||||
|
||||
## Typical Deployment Workflow
|
||||
|
||||
### 1. Development Phase
|
||||
|
||||
Use **local development** for content creation and theme building:
|
||||
|
||||
```bash
|
||||
# Edit content
|
||||
vim content/my-post.md
|
||||
|
||||
# Run server
|
||||
python main.py --config config.toml
|
||||
|
||||
# Visit http://localhost:8081
|
||||
# See changes immediately
|
||||
```
|
||||
|
||||
### 2. Testing Phase
|
||||
|
||||
Use **Docker** to test in a production-like environment:
|
||||
|
||||
```bash
|
||||
# Build and run
|
||||
docker-compose up
|
||||
|
||||
# Test on http://localhost:8081
|
||||
# Verify everything works in container
|
||||
```
|
||||
|
||||
### 3. Deployment Phase
|
||||
|
||||
Deploy to **production** using your preferred method:
|
||||
|
||||
```bash
|
||||
# Option A: Dynamic server
|
||||
gunicorn -w 4 main:app
|
||||
|
||||
# Option B: Static export
|
||||
python export.py --output ./dist
|
||||
rsync -avz ./dist/ user@server:/var/www/site/
|
||||
```
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
### Scenario: Personal Blog
|
||||
|
||||
**Best deployment:** Local development + static export to GitHub Pages
|
||||
|
||||
```bash
|
||||
# Develop locally
|
||||
python main.py --config config.toml
|
||||
|
||||
# Export to static files
|
||||
python export.py --output ./docs
|
||||
|
||||
# Push to GitHub (Pages serves from /docs)
|
||||
git add docs/
|
||||
git commit -m "Update site"
|
||||
git push
|
||||
```
|
||||
|
||||
### Scenario: Team Documentation
|
||||
|
||||
**Best deployment:** Docker for everyone, dynamic server in production
|
||||
|
||||
```bash
|
||||
# Everyone on the team uses Docker
|
||||
docker-compose up
|
||||
|
||||
# Production uses dynamic Python server
|
||||
# for real-time updates when docs change
|
||||
```
|
||||
|
||||
### Scenario: Photography Portfolio
|
||||
|
||||
**Best deployment:** Local development, Docker for staging, static export for production
|
||||
|
||||
High-performance static site with CDN for fast image delivery.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Port Already in Use
|
||||
|
||||
```
|
||||
OSError: [Errno 48] Address already in use
|
||||
```
|
||||
|
||||
**Solution:** Change `listen_port` in config.toml or stop the other service using that port.
|
||||
|
||||
### Module Not Found
|
||||
|
||||
```
|
||||
ModuleNotFoundError: No module named 'flask'
|
||||
```
|
||||
|
||||
**Solution:** Install dependencies:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Permission Denied
|
||||
|
||||
```
|
||||
PermissionError: [Errno 13] Permission denied
|
||||
```
|
||||
|
||||
**Solution:** Check directory permissions:
|
||||
```bash
|
||||
chmod -R 755 content/ templates/ styles/
|
||||
```
|
||||
|
||||
### Template Not Found
|
||||
|
||||
```
|
||||
Exception: Base template not found
|
||||
```
|
||||
|
||||
**Solution:** Ensure `base.html` exists in templates directory:
|
||||
```bash
|
||||
ls templates/base.html
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
Choose your deployment path:
|
||||
|
||||
- **[Local Development Guide](deployment/local-development.md)** - Start here for development
|
||||
- **[Docker Deployment Guide](deployment/docker.md)** - Containerized deployment
|
||||
- **[Production Deployment Guide](deployment/production.md)** - Go live with your site
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Development
|
||||
|
||||
- **Only bind to localhost** (`listen_address = "127.0.0.1"`)
|
||||
- **Never commit config.toml** with sensitive data to version control
|
||||
|
||||
### Production
|
||||
|
||||
- **Use HTTPS** - Always use TLS/SSL in production
|
||||
- **Strong passwords** - If using admin interface, use strong passwords
|
||||
- **Firewall rules** - Only expose necessary ports
|
||||
- **Regular updates** - Keep Foldsite and dependencies updated
|
||||
- **Content validation** - Be careful with user-uploaded content
|
||||
|
||||
See [Production Deployment](production.md) for detailed security guidance.
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### For All Deployments
|
||||
|
||||
- **Use hidden files** for drafts (`___draft.md`) to avoid processing
|
||||
- **Optimize images** before uploading (Foldsite generates thumbnails, but smaller source = faster)
|
||||
- **Minimize template complexity** - Simple templates render faster
|
||||
|
||||
### For Production
|
||||
|
||||
- **Enable caching** at the web server level
|
||||
- **Use a CDN** for static assets
|
||||
- **Compress responses** with gzip/brotli
|
||||
- **Monitor resource usage** and scale as needed
|
||||
|
||||
## Getting Help
|
||||
|
||||
Need assistance with deployment?
|
||||
|
||||
- **[Support](../support.md)** - Community help and resources
|
||||
- **GitHub Issues** - Report bugs or ask questions
|
||||
- **Example Configs** - See `/examples` directory in repository
|
||||
|
||||
Happy deploying!
|
629
docs/content/deployment/local-development.md
Normal file
629
docs/content/deployment/local-development.md
Normal file
@ -0,0 +1,629 @@
|
||||
---
|
||||
version: "1.0"
|
||||
date: "2025-01-15"
|
||||
author: "DWS Foldsite Team"
|
||||
title: "Local Development"
|
||||
description: "Running Foldsite on your local machine for development"
|
||||
summary: "Complete guide to setting up and running Foldsite locally for the fastest development workflow."
|
||||
quick_tips:
|
||||
- "Local development gives instant feedback - no build step needed"
|
||||
- "Use debug mode to see template discovery and errors clearly"
|
||||
- "Changes to content and templates appear immediately on refresh"
|
||||
---
|
||||
|
||||
# Local Development
|
||||
|
||||
Running Foldsite locally is the fastest way to develop your site. Changes to content and templates appear instantly without any build process.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Required Software
|
||||
|
||||
**Python 3.10 or higher**
|
||||
```bash
|
||||
# Check your Python version
|
||||
python3 --version
|
||||
|
||||
# Should output: Python 3.10.x or higher
|
||||
```
|
||||
|
||||
If you don't have Python 3.10+:
|
||||
- **macOS:** `brew install python3`
|
||||
- **Ubuntu/Debian:** `sudo apt install python3.10`
|
||||
- **Windows:** Download from [python.org](https://www.python.org/downloads/)
|
||||
|
||||
**pip (Python package manager)**
|
||||
```bash
|
||||
# Check pip version
|
||||
pip --version
|
||||
|
||||
# If missing, install
|
||||
python3 -m ensurepip --upgrade
|
||||
```
|
||||
|
||||
**Git (recommended)**
|
||||
```bash
|
||||
git --version
|
||||
```
|
||||
|
||||
### Optional but Recommended
|
||||
|
||||
**Virtual environment support**
|
||||
```bash
|
||||
python3 -m venv --help
|
||||
```
|
||||
|
||||
**Text editor with syntax highlighting**
|
||||
- VS Code (recommended)
|
||||
- Sublime Text
|
||||
- Vim/Neovim
|
||||
- Any editor you prefer
|
||||
|
||||
## Installation
|
||||
|
||||
### Step 1: Get Foldsite
|
||||
|
||||
**Option A: Clone with Git (recommended)**
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/DWSresearch/foldsite.git
|
||||
cd foldsite
|
||||
```
|
||||
|
||||
**Option B: Download ZIP**
|
||||
```bash
|
||||
# Download latest release
|
||||
wget https://github.com/DWSresearch/foldsite/archive/main.zip
|
||||
unzip main.zip
|
||||
cd foldsite-main
|
||||
```
|
||||
|
||||
### Step 2: Create Virtual Environment
|
||||
|
||||
Using a virtual environment keeps dependencies isolated:
|
||||
|
||||
```bash
|
||||
# Create virtual environment
|
||||
python3 -m venv venv
|
||||
|
||||
# Activate it
|
||||
# On macOS/Linux:
|
||||
source venv/bin/activate
|
||||
|
||||
# On Windows:
|
||||
venv\Scripts\activate
|
||||
|
||||
# Your prompt should now show (venv)
|
||||
```
|
||||
|
||||
**Why virtual environment?**
|
||||
- Isolates project dependencies
|
||||
- Prevents conflicts with system Python
|
||||
- Easy to recreate or remove
|
||||
- Professional Python practice
|
||||
|
||||
### Step 3: Install Dependencies
|
||||
|
||||
```bash
|
||||
# Ensure pip is up to date
|
||||
pip install --upgrade pip
|
||||
|
||||
# Install Foldsite dependencies
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
**Dependencies installed:**
|
||||
- Flask - Web framework
|
||||
- Jinja2 - Template engine
|
||||
- mistune - Markdown parser
|
||||
- python-frontmatter - YAML frontmatter parsing
|
||||
- Pillow - Image processing
|
||||
- Gunicorn - WSGI server
|
||||
|
||||
**Troubleshooting:**
|
||||
|
||||
If you see compilation errors:
|
||||
```bash
|
||||
# Install build tools
|
||||
|
||||
# macOS:
|
||||
xcode-select --install
|
||||
|
||||
# Ubuntu/Debian:
|
||||
sudo apt install python3-dev build-essential
|
||||
|
||||
# Then retry:
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Step 4: Set Up Your Content
|
||||
|
||||
Create your site directory structure:
|
||||
|
||||
```bash
|
||||
# Create your site directories
|
||||
mkdir -p my-site/content
|
||||
mkdir -p my-site/templates
|
||||
mkdir -p my-site/styles
|
||||
|
||||
# Create a basic index page
|
||||
echo "# Welcome to My Site" > my-site/content/index.md
|
||||
|
||||
# Copy example templates to start
|
||||
cp -r example_site/template/* my-site/templates/
|
||||
cp -r example_site/style/* my-site/styles/
|
||||
```
|
||||
|
||||
### Step 5: Configure Foldsite
|
||||
|
||||
Create a configuration file:
|
||||
|
||||
```bash
|
||||
# Copy example config
|
||||
cp config.toml my-site-config.toml
|
||||
```
|
||||
|
||||
Edit `my-site-config.toml`:
|
||||
|
||||
```toml
|
||||
[paths]
|
||||
content_dir = "/absolute/path/to/my-site/content"
|
||||
templates_dir = "/absolute/path/to/my-site/templates"
|
||||
styles_dir = "/absolute/path/to/my-site/styles"
|
||||
|
||||
[server]
|
||||
listen_address = "127.0.0.1" # Only accessible from your machine
|
||||
listen_port = 8081
|
||||
admin_browser = false # Disable admin interface for now
|
||||
max_threads = 4
|
||||
debug = true # Enable debug mode for development
|
||||
access_log = true
|
||||
```
|
||||
|
||||
**Important:** Use absolute paths in development to avoid confusion.
|
||||
|
||||
**Find absolute path:**
|
||||
```bash
|
||||
# macOS/Linux:
|
||||
cd my-site && pwd
|
||||
|
||||
# Windows:
|
||||
cd my-site && cd
|
||||
```
|
||||
|
||||
## Running Foldsite
|
||||
|
||||
### Start the Server
|
||||
|
||||
```bash
|
||||
# Make sure virtual environment is activated
|
||||
source venv/bin/activate # or venv\Scripts\activate on Windows
|
||||
|
||||
# Run Foldsite
|
||||
python main.py --config my-site-config.toml
|
||||
```
|
||||
|
||||
**Expected output:**
|
||||
```
|
||||
[2025-01-15 10:30:45] [INFO] Starting Foldsite server
|
||||
[2025-01-15 10:30:45] [INFO] Content directory: /path/to/my-site/content
|
||||
[2025-01-15 10:30:45] [INFO] Templates directory: /path/to/my-site/templates
|
||||
[2025-01-15 10:30:45] [INFO] Listening on http://127.0.0.1:8081
|
||||
```
|
||||
|
||||
### Visit Your Site
|
||||
|
||||
Open your browser to:
|
||||
```
|
||||
http://localhost:8081
|
||||
```
|
||||
|
||||
You should see your site!
|
||||
|
||||
### Stop the Server
|
||||
|
||||
Press `Ctrl+C` in the terminal where Foldsite is running.
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### The Edit-Refresh Cycle
|
||||
|
||||
Foldsite has **no build step**. Changes appear immediately:
|
||||
|
||||
1. **Edit content** - Modify markdown files
|
||||
2. **Save file** - Ctrl+S / Cmd+S
|
||||
3. **Refresh browser** - F5 or Cmd+R
|
||||
4. **See changes instantly**
|
||||
|
||||
**What updates live:**
|
||||
- Content (markdown files)
|
||||
- Templates (HTML files)
|
||||
- Styles (CSS files)
|
||||
- Configuration (requires restart)
|
||||
|
||||
### Example Workflow
|
||||
|
||||
**Scenario:** Adding a blog post
|
||||
|
||||
```bash
|
||||
# 1. Create new post
|
||||
vim my-site/content/blog/my-new-post.md
|
||||
```
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: "My New Blog Post"
|
||||
date: "2025-01-15"
|
||||
tags: ["tutorial", "foldsite"]
|
||||
---
|
||||
|
||||
# My New Blog Post
|
||||
|
||||
This is my latest post!
|
||||
```
|
||||
|
||||
```bash
|
||||
# 2. Save file and switch to browser
|
||||
# 3. Visit http://localhost:8081/blog/my-new-post.md
|
||||
# 4. See your post rendered immediately
|
||||
```
|
||||
|
||||
**No restart needed!**
|
||||
|
||||
### Working with Templates
|
||||
|
||||
```bash
|
||||
# 1. Edit template
|
||||
vim my-site/templates/__file.md.html
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- Add a new section -->
|
||||
<article>
|
||||
<header>
|
||||
<h1>{{ metadata.title }}</h1>
|
||||
<time>{{ metadata.date }}</time>
|
||||
</header>
|
||||
|
||||
{{ content|safe }}
|
||||
|
||||
<!-- NEW: Add related posts -->
|
||||
{% set related = get_related_posts(currentPath, limit=3) %}
|
||||
{% if related %}
|
||||
<aside class="related">
|
||||
<h3>Related Posts</h3>
|
||||
{% for post in related %}
|
||||
<a href="{{ post.url }}">{{ post.title }}</a>
|
||||
{% endfor %}
|
||||
</aside>
|
||||
{% endif %}
|
||||
</article>
|
||||
```
|
||||
|
||||
```bash
|
||||
# 2. Save and refresh browser
|
||||
# 3. See related posts section appear
|
||||
```
|
||||
|
||||
### Working with Styles
|
||||
|
||||
```bash
|
||||
# 1. Edit CSS
|
||||
vim my-site/styles/base.css
|
||||
```
|
||||
|
||||
```css
|
||||
/* Add new styles */
|
||||
.related {
|
||||
margin-top: 2rem;
|
||||
padding: 1rem;
|
||||
background: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.related h3 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
# 2. Save and hard refresh (Cmd+Shift+R / Ctrl+Shift+R)
|
||||
# 3. See styled related posts
|
||||
```
|
||||
|
||||
## Debug Mode
|
||||
|
||||
Enable debug mode for helpful development information:
|
||||
|
||||
```toml
|
||||
[server]
|
||||
debug = true
|
||||
```
|
||||
|
||||
**What debug mode shows:**
|
||||
- Template discovery process
|
||||
- Which templates were considered
|
||||
- Which template was chosen
|
||||
- Detailed error messages with stack traces
|
||||
- Template variables and context
|
||||
|
||||
**Example debug output:**
|
||||
|
||||
When visiting a page, console shows:
|
||||
```
|
||||
[DEBUG] Template search for: blog/my-post.md
|
||||
[DEBUG] Checking: templates/blog/my-post.html - Not found
|
||||
[DEBUG] Checking: templates/blog/__file.md.html - Found!
|
||||
[DEBUG] Using template: templates/blog/__file.md.html
|
||||
[DEBUG] Available variables: content, metadata, currentPath, styles
|
||||
```
|
||||
|
||||
**View in browser:**
|
||||
|
||||
With debug mode, error pages show:
|
||||
- Full Python stack trace
|
||||
- Template rendering context
|
||||
- What went wrong and where
|
||||
- Suggestions for fixes
|
||||
|
||||
## Common Development Tasks
|
||||
|
||||
### Creating New Pages
|
||||
|
||||
```bash
|
||||
# Simple page
|
||||
echo "# About Me\n\nI'm a web developer." > my-site/content/about.md
|
||||
|
||||
# With frontmatter
|
||||
cat > my-site/content/projects.md << 'EOF'
|
||||
---
|
||||
title: "My Projects"
|
||||
description: "Things I've built"
|
||||
---
|
||||
|
||||
# My Projects
|
||||
|
||||
Here are some things I've worked on...
|
||||
EOF
|
||||
```
|
||||
|
||||
### Organizing Content
|
||||
|
||||
```bash
|
||||
# Create a blog section
|
||||
mkdir -p my-site/content/blog
|
||||
|
||||
# Add posts
|
||||
for i in {1..5}; do
|
||||
cat > my-site/content/blog/post-$i.md << EOF
|
||||
---
|
||||
title: "Blog Post $i"
|
||||
date: "2024-01-$i"
|
||||
tags: ["example"]
|
||||
---
|
||||
|
||||
# Post $i
|
||||
|
||||
Content here...
|
||||
EOF
|
||||
done
|
||||
```
|
||||
|
||||
### Testing Template Helpers
|
||||
|
||||
Create a test page to experiment:
|
||||
|
||||
```bash
|
||||
cat > my-site/content/test.md << 'EOF'
|
||||
---
|
||||
title: "Template Helper Test"
|
||||
---
|
||||
|
||||
# Testing Helpers
|
||||
|
||||
## Recent Posts
|
||||
{% for post in get_recent_posts(limit=5) %}
|
||||
- [{{ post.title }}]({{ post.url }}) - {{ post.date }}
|
||||
{% endfor %}
|
||||
|
||||
## All Tags
|
||||
{% for tag in get_all_tags() %}
|
||||
- {{ tag.name }} ({{ tag.count }})
|
||||
{% endfor %}
|
||||
|
||||
## Current Path Info
|
||||
- Path: {{ currentPath }}
|
||||
- Metadata: {{ metadata }}
|
||||
EOF
|
||||
```
|
||||
|
||||
Visit `/test.md` to see helper output.
|
||||
|
||||
### Hiding Work in Progress
|
||||
|
||||
Use the `___` prefix:
|
||||
|
||||
```bash
|
||||
# Hidden draft
|
||||
vim my-site/content/___draft-post.md
|
||||
|
||||
# Hidden development folder
|
||||
mkdir my-site/content/___testing
|
||||
```
|
||||
|
||||
These won't appear in navigation or listings.
|
||||
|
||||
## Editor Setup
|
||||
|
||||
### VS Code
|
||||
|
||||
Recommended extensions:
|
||||
- **Python** (Microsoft)
|
||||
- **Jinja** (wholroyd.jinja)
|
||||
- **Markdown All in One** (yzhang.markdown-all-in-one)
|
||||
|
||||
**Settings:**
|
||||
```json
|
||||
{
|
||||
"files.associations": {
|
||||
"*.html": "jinja-html"
|
||||
},
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
```
|
||||
|
||||
### Vim/Neovim
|
||||
|
||||
Add to `.vimrc` or `init.vim`:
|
||||
```vim
|
||||
" Jinja syntax for .html files
|
||||
autocmd BufNewFile,BufRead */templates/*.html set filetype=jinja
|
||||
|
||||
" Markdown settings
|
||||
autocmd FileType markdown setlocal spell spelllang=en_us
|
||||
autocmd FileType markdown setlocal textwidth=80
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Port Already in Use
|
||||
|
||||
```
|
||||
Error: [Errno 48] Address already in use
|
||||
```
|
||||
|
||||
**Solution 1:** Change port in config:
|
||||
```toml
|
||||
[server]
|
||||
listen_port = 8082
|
||||
```
|
||||
|
||||
**Solution 2:** Find and kill process:
|
||||
```bash
|
||||
# Find process using port 8081
|
||||
lsof -i :8081
|
||||
|
||||
# Kill it
|
||||
kill -9 <PID>
|
||||
```
|
||||
|
||||
### Module Not Found
|
||||
|
||||
```
|
||||
ModuleNotFoundError: No module named 'flask'
|
||||
```
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Ensure virtual environment is activated
|
||||
source venv/bin/activate
|
||||
|
||||
# Reinstall dependencies
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Template Not Found
|
||||
|
||||
```
|
||||
Exception: Base template not found
|
||||
```
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Check templates directory exists and has base.html
|
||||
ls my-site/templates/base.html
|
||||
|
||||
# If missing, copy from example
|
||||
cp example_site/template/base.html my-site/templates/
|
||||
```
|
||||
|
||||
### Changes Not Appearing
|
||||
|
||||
**Possible causes:**
|
||||
|
||||
1. **Browser cache** - Hard refresh (Cmd/Ctrl + Shift + R)
|
||||
2. **Wrong file** - Check you're editing the file Foldsite is using
|
||||
3. **Configuration issue** - Verify config.toml paths
|
||||
4. **Syntax error** - Check console for error messages
|
||||
|
||||
**Debug:**
|
||||
```bash
|
||||
# Enable debug mode
|
||||
# Edit config.toml
|
||||
[server]
|
||||
debug = true
|
||||
|
||||
# Restart Foldsite
|
||||
# Check console output
|
||||
```
|
||||
|
||||
### Permission Denied
|
||||
|
||||
```
|
||||
PermissionError: [Errno 13] Permission denied
|
||||
```
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Fix permissions
|
||||
chmod -R 755 my-site/
|
||||
|
||||
# Or run with correct user
|
||||
sudo chown -R $USER:$USER my-site/
|
||||
```
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### Development Speed
|
||||
|
||||
**Fast iteration:**
|
||||
- Keep browser DevTools open
|
||||
- Use multiple browser windows for comparison
|
||||
- Enable auto-reload browser extensions
|
||||
- Use terminal multiplexer (tmux/screen)
|
||||
|
||||
**Organize workspace:**
|
||||
```
|
||||
Terminal 1: Foldsite server
|
||||
Terminal 2: Content editing
|
||||
Terminal 3: Git operations
|
||||
Browser: Live preview
|
||||
Editor: Code/templates
|
||||
```
|
||||
|
||||
### Working with Large Sites
|
||||
|
||||
If your site has many files:
|
||||
|
||||
```toml
|
||||
[server]
|
||||
max_threads = 8 # Increase workers
|
||||
```
|
||||
|
||||
**Cache considerations:**
|
||||
- Template helpers are cached automatically
|
||||
- Folder contents cached for 5 minutes
|
||||
- Recent posts cached for 10 minutes
|
||||
- Restart server to clear caches
|
||||
|
||||
## Next Steps
|
||||
|
||||
Now that you have local development running:
|
||||
|
||||
- **[Templates Guide](../templates/)** - Learn the template system
|
||||
- **[Template Helpers](../templates/template-helpers.md)** - Explore helper functions
|
||||
- **[Recipes](../recipes/)** - Copy working examples
|
||||
- **[Docker Deployment](docker.md)** - Test in container
|
||||
- **[Production Deployment](production.md)** - Go live
|
||||
|
||||
## Tips from Developers
|
||||
|
||||
> "Keep the server running in a dedicated terminal. Switch to it to see errors immediately." — Foldsite contributor
|
||||
|
||||
> "Use `___testing/` folder for experimenting. It's ignored so you can mess around without affecting the site." — Content creator
|
||||
|
||||
> "Debug mode is your friend. Always enable it in development." — Theme developer
|
||||
|
||||
> "Create test pages to try helper functions. Much faster than reading docs." — Documentation writer
|
||||
|
||||
Happy developing! Local development is where the magic happens. 🚀
|
810
docs/content/deployment/production.md
Normal file
810
docs/content/deployment/production.md
Normal file
@ -0,0 +1,810 @@
|
||||
---
|
||||
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.
|
Reference in New Issue
Block a user