Readur/docs/deployment.md

418 lines
9.8 KiB
Markdown

# Deployment Guide
This guide covers production deployment strategies, SSL setup, monitoring, backups, and best practices for running Readur in production.
> 🆕 **New in 2.5.4**: S3 storage backend support! See the [Migration Guide](migration-guide.md) to migrate from local storage to S3, and the [S3 Storage Guide](s3-storage-guide.md) for complete setup instructions.
## Table of Contents
- [Production Docker Compose](#production-docker-compose)
- [Network Filesystem Mounts](#network-filesystem-mounts)
- [NFS Mounts](#nfs-mounts)
- [SMB/CIFS Mounts](#smbcifs-mounts)
- [S3 Mounts](#s3-mounts)
- [SSL/HTTPS Setup](#sslhttps-setup)
- [Nginx Configuration](#nginx-configuration)
- [Traefik Configuration](#traefik-configuration)
- [Health Checks](#health-checks)
- [Backup Strategy](#backup-strategy)
- [Monitoring](#monitoring)
- [Deployment Platforms](#deployment-platforms)
- [Docker Swarm](#docker-swarm)
- [Kubernetes](#kubernetes)
- [Cloud Platforms](#cloud-platforms)
- [Security Considerations](#security-considerations)
## Production Docker Compose
For production deployments, create a custom `docker-compose.prod.yml`:
```yaml
services:
readur:
image: readur:latest
ports:
- "8000:8000"
environment:
# Core Configuration
- DATABASE_URL=postgresql://readur:${DB_PASSWORD}@postgres:5432/readur
- JWT_SECRET=${JWT_SECRET}
- SERVER_ADDRESS=0.0.0.0:8000
# File Storage
- UPLOAD_PATH=/app/uploads
- WATCH_FOLDER=/app/watch
- ALLOWED_FILE_TYPES=pdf,png,jpg,jpeg,tiff,bmp,gif,txt,doc,docx
# Watch Folder Settings
- WATCH_INTERVAL_SECONDS=30
- FILE_STABILITY_CHECK_MS=500
- MAX_FILE_AGE_HOURS=168
# OCR Configuration
- OCR_LANGUAGE=eng
- CONCURRENT_OCR_JOBS=4
- OCR_TIMEOUT_SECONDS=300
- MAX_FILE_SIZE_MB=100
# Performance Tuning
- MEMORY_LIMIT_MB=1024
- CPU_PRIORITY=normal
- ENABLE_COMPRESSION=true
volumes:
# Document storage
- ./data/uploads:/app/uploads
# Watch folder - mount your network drives here
- /mnt/nfs/documents:/app/watch
# or SMB: - /mnt/smb/shared:/app/watch
# or S3: - /mnt/s3/bucket:/app/watch
depends_on:
- postgres
restart: unless-stopped
# Resource limits for production
deploy:
resources:
limits:
memory: 2G
cpus: '2.0'
reservations:
memory: 512M
cpus: '0.5'
postgres:
image: postgres:15
environment:
- POSTGRES_USER=readur
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=readur
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=en_US.UTF-8 --lc-ctype=en_US.UTF-8
volumes:
- postgres_data:/var/lib/postgresql/data
- ./postgres-config:/etc/postgresql/conf.d:ro
# PostgreSQL optimization for document search
command: >
postgres
-c shared_buffers=256MB
-c effective_cache_size=1GB
-c max_connections=100
-c default_text_search_config=pg_catalog.english
restart: unless-stopped
# Don't expose port in production
# ports:
# - "5433:5432"
volumes:
postgres_data:
driver: local
```
Deploy with environment file:
```bash
# Create .env file with secrets
cat > .env << EOF
JWT_SECRET=$(openssl rand -base64 64)
DB_PASSWORD=$(openssl rand -base64 32)
EOF
# Deploy
docker compose -f docker-compose.prod.yml --env-file .env up -d
```
## Network Filesystem Mounts
### NFS Mounts
```bash
# Mount NFS share
sudo mount -t nfs 192.168.1.100:/documents /mnt/nfs/documents
# Add to docker-compose.yml
volumes:
- /mnt/nfs/documents:/app/watch
environment:
- WATCH_INTERVAL_SECONDS=60
- FILE_STABILITY_CHECK_MS=1000
- FORCE_POLLING_WATCH=1
```
### SMB/CIFS Mounts
```bash
# Mount SMB share
sudo mount -t cifs //server/share /mnt/smb/shared -o username=user,password=pass
# Docker volume configuration
volumes:
- /mnt/smb/shared:/app/watch
environment:
- WATCH_INTERVAL_SECONDS=30
- FILE_STABILITY_CHECK_MS=2000
```
### S3 Mounts
```bash
# Mount S3 bucket using s3fs
s3fs mybucket /mnt/s3/bucket -o passwd_file=~/.passwd-s3fs
# Docker configuration for S3
volumes:
- /mnt/s3/bucket:/app/watch
environment:
- WATCH_INTERVAL_SECONDS=120
- FILE_STABILITY_CHECK_MS=5000
- FORCE_POLLING_WATCH=1
```
## SSL/HTTPS Setup
### Nginx Configuration
```nginx
server {
listen 443 ssl http2;
server_name readur.yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:8000;
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;
# For file uploads
client_max_body_size 100M;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
}
```
### Traefik Configuration
```yaml
services:
readur:
labels:
- "traefik.enable=true"
- "traefik.http.routers.readur.rule=Host(`readur.yourdomain.com`)"
- "traefik.http.routers.readur.tls=true"
- "traefik.http.routers.readur.tls.certresolver=letsencrypt"
```
> 📘 **For more reverse proxy configurations** including Apache, Caddy, custom ports, load balancing, and advanced scenarios, see [REVERSE_PROXY.md](./REVERSE_PROXY.md).
## Health Checks
Add health checks to your Docker configuration. The Readur Docker image includes `curl` for health checking.
**Important:** The port in the healthcheck URL must match your `SERVER_PORT` or the port specified in `SERVER_ADDRESS`:
```yaml
services:
readur:
environment:
# If using SERVER_ADDRESS
- SERVER_ADDRESS=0.0.0.0:8000 # Port 8000
# Or if using SERVER_PORT
# - SERVER_PORT=8000 # Port 8000
healthcheck:
# Port in URL must match the SERVER_PORT/SERVER_ADDRESS port above
test: ["CMD", "curl", "-f", "http://localhost:8000/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
```
For example, if you change the server to run on port 3000:
- Set `SERVER_PORT=3000` or `SERVER_ADDRESS=0.0.0.0:3000`
- Update healthcheck to: `http://localhost:3000/api/health`
## Backup Strategy
Create an automated backup script:
```bash
#!/bin/bash
# backup.sh - Automated backup script
BACKUP_DIR="/path/to/backups"
DATE=$(date +%Y%m%d_%H%M%S)
# Create backup directory
mkdir -p "$BACKUP_DIR"
# Backup database
docker exec readur-postgres-1 pg_dump -U readur readur | gzip > "$BACKUP_DIR/db_backup_$DATE.sql.gz"
# Backup uploaded files
tar -czf "$BACKUP_DIR/uploads_backup_$DATE.tar.gz" -C ./data uploads/
# Clean old backups (keep 30 days)
find "$BACKUP_DIR" -name "db_backup_*.sql.gz" -mtime +30 -delete
find "$BACKUP_DIR" -name "uploads_backup_*.tar.gz" -mtime +30 -delete
echo "Backup completed: $DATE"
```
Add to crontab for daily backups:
```bash
0 2 * * * /path/to/backup.sh >> /var/log/readur-backup.log 2>&1
```
### Restore from Backup
```bash
# Restore database
gunzip -c db_backup_20240101_020000.sql.gz | docker exec -i readur-postgres-1 psql -U readur readur
# Restore files
tar -xzf uploads_backup_20240101_020000.tar.gz -C ./data
```
## Monitoring
Monitor your deployment with Docker stats:
```bash
# Real-time resource usage
docker stats
# Container logs
docker compose logs -f readur
# Watch folder activity
docker compose logs -f readur | grep watcher
# PostgreSQL query performance
docker exec readur-postgres-1 psql -U readur -c "SELECT * FROM pg_stat_statements ORDER BY total_time DESC LIMIT 10;"
```
### Prometheus Metrics
Readur exposes metrics at `/metrics` endpoint:
```yaml
# prometheus.yml
scrape_configs:
- job_name: 'readur'
static_configs:
- targets: ['readur:8000']
```
## Deployment Platforms
### Docker Swarm
```yaml
version: '3.8'
services:
readur:
image: readur:latest
deploy:
replicas: 2
restart_policy:
condition: on-failure
placement:
constraints: [node.role == worker]
networks:
- readur-network
secrets:
- jwt_secret
- db_password
secrets:
jwt_secret:
external: true
db_password:
external: true
```
### Kubernetes
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: readur
spec:
replicas: 3
selector:
matchLabels:
app: readur
template:
spec:
containers:
- name: readur
image: readur:latest
env:
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: readur-secrets
key: jwt-secret
resources:
limits:
memory: "2Gi"
cpu: "2"
requests:
memory: "512Mi"
cpu: "500m"
```
### Cloud Platforms
- **AWS**: Use ECS with RDS PostgreSQL
- **Google Cloud**: Deploy to Cloud Run with Cloud SQL
- **Azure**: Use Container Instances with Azure Database
- **DigitalOcean**: App Platform with Managed Database
## Security Considerations
### Production Checklist
- [ ] Change default admin password
- [ ] Generate strong JWT secret
- [ ] Use HTTPS/SSL in production
- [ ] Restrict database network access
- [ ] Set proper file permissions
- [ ] Enable firewall rules
- [ ] Regular security updates
- [ ] Monitor access logs
- [ ] Implement rate limiting
- [ ] Enable audit logging
### Recommended Production Setup
```bash
# Generate secure secrets
JWT_SECRET=$(openssl rand -base64 64)
DB_PASSWORD=$(openssl rand -base64 32)
# Restrict file permissions
chmod 600 .env
chmod 700 ./data/uploads
# Use read-only root filesystem
docker run --read-only --tmpfs /tmp ...
```
## Next Steps
- Configure [monitoring and alerting](monitoring-usage)
- Review [security best practices](security)
- Set up [automated backups](#backup-strategy)
- Explore [database guardrails](dev/DATABASE_GUARDRAILS.md)