Files
StarPunk/docs/deployment/container-deployment.md
Phil Skentelbery 8d593ca1b9 docs: add container deployment guide and implementation report
Complete Phase 5 containerization documentation:
- Add comprehensive container deployment guide (500+ lines)
- Document Podman and Docker deployment workflows
- Include reverse proxy setup for Caddy and Nginx
- Add troubleshooting, monitoring, and maintenance sections
- Document --userns=keep-id requirement for Podman
- Add backup/restore procedures
- Include performance tuning guidelines
- Add security best practices

Implementation report includes:
- Technical implementation details
- Testing results and metrics
- Challenge resolution (Podman permissions)
- Security and compliance verification
- Integration with RSS feed
- Lessons learned and recommendations

Updated CHANGELOG.md:
- Document container features in v0.6.0
- Add configuration variables
- List deployment capabilities
- Note Podman and Docker compatibility

Phase 5 containerization: 100% complete
2025-11-19 10:14:35 -07:00

660 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# StarPunk Container Deployment Guide
**Version**: 0.6.0
**Last Updated**: 2025-11-19
## Overview
This guide covers deploying StarPunk in a production environment using containers (Podman or Docker). StarPunk is packaged as a lightweight, production-ready container image that includes:
- Python 3.11 runtime
- Gunicorn WSGI server (4 workers)
- Multi-stage build for optimized size (174MB)
- Non-root user security
- Health check endpoint
- Volume mounts for data persistence
## Prerequisites
### Required
- **Container Runtime**: Podman 3.0+ or Docker 20.10+
- **Storage**: Minimum 500MB for image + data
- **Memory**: Minimum 512MB RAM (recommended 1GB)
- **Network**: Port 8000 available for container
### Recommended
- **Reverse Proxy**: Caddy 2.0+ or Nginx 1.18+
- **TLS Certificate**: Let's Encrypt via certbot or Caddy auto-HTTPS
- **Domain**: Public domain name for HTTPS and IndieAuth
## Quick Start
### 1. Build the Container
```bash
cd /path/to/starpunk
podman build -t starpunk:0.6.0 -f Containerfile .
```
**Expected output**:
- Build completes in 2-3 minutes
- Final image size: ~174MB
- Multi-stage build optimizes dependencies
### 2. Prepare Data Directory
```bash
mkdir -p container-data/notes
```
### 3. Configure Environment
```bash
cp .env.example .env
# Edit .env with your values:
nano .env
```
**Required settings**:
```bash
SITE_URL=https://your-domain.com
SITE_NAME=Your Site Name
ADMIN_ME=https://your-identity.com
SESSION_SECRET=<generate-random-secret>
```
**Generate session secret**:
```bash
python3 -c "import secrets; print(secrets.token_hex(32))"
```
### 4. Run the Container
#### Using Podman
```bash
podman run -d \
--name starpunk \
--userns=keep-id \
-p 127.0.0.1:8000:8000 \
-v $(pwd)/container-data:/data:rw \
--env-file .env \
starpunk:0.6.0
```
**Note**: The `--userns=keep-id` flag is **required** for Podman to properly handle file permissions with volume mounts.
#### Using Docker
```bash
docker run -d \
--name starpunk \
-p 127.0.0.1:8000:8000 \
-v $(pwd)/container-data:/data:rw \
--env-file .env \
starpunk:0.6.0
```
### 5. Verify Container is Running
```bash
# Check health endpoint
curl http://localhost:8000/health
# Expected output:
# {"status": "healthy", "version": "0.6.0", "environment": "production"}
```
## Container Orchestration
### Using Compose (Recommended)
The included `compose.yaml` provides a complete orchestration configuration.
#### Podman Compose
**Install podman-compose** (if not installed):
```bash
pip install podman-compose
```
**Run**:
```bash
podman-compose up -d
```
**View logs**:
```bash
podman-compose logs -f
```
**Stop**:
```bash
podman-compose down
```
#### Docker Compose
```bash
docker compose up -d
docker compose logs -f
docker compose down
```
### Compose Configuration
The `compose.yaml` includes:
- Automatic restart policy
- Health checks
- Resource limits (1 CPU, 512MB RAM)
- Log rotation (10MB max, 3 files)
- Network isolation
- Volume persistence
## Production Deployment
### Architecture
```
Internet → HTTPS (443)
Reverse Proxy (Caddy/Nginx)
HTTP (8000) → Container
Volume Mount → /data (persistent storage)
```
### Reverse Proxy Setup
#### Option 1: Caddy (Recommended)
**Advantages**:
- Automatic HTTPS with Let's Encrypt
- Minimal configuration
- Built-in security headers
**Installation**:
```bash
# Install Caddy
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
```
**Configuration**:
```bash
# Copy example config
cp Caddyfile.example Caddyfile
# Edit domain
nano Caddyfile
# Replace "your-domain.com" with your actual domain
# Run Caddy
sudo systemctl enable --now caddy
```
**Caddyfile** (minimal):
```caddy
your-domain.com {
reverse_proxy localhost:8000
}
```
Caddy will automatically:
- Obtain SSL certificate from Let's Encrypt
- Redirect HTTP to HTTPS
- Renew certificates before expiry
#### Option 2: Nginx
**Installation**:
```bash
sudo apt install nginx certbot python3-certbot-nginx
```
**Configuration**:
```bash
# Copy example config
sudo cp nginx.conf.example /etc/nginx/sites-available/starpunk
# Edit domain
sudo nano /etc/nginx/sites-available/starpunk
# Replace "your-domain.com" with your actual domain
# Enable site
sudo ln -s /etc/nginx/sites-available/starpunk /etc/nginx/sites-enabled/
# Test configuration
sudo nginx -t
# Obtain SSL certificate
sudo certbot --nginx -d your-domain.com
# Reload Nginx
sudo systemctl reload nginx
```
### Environment Configuration for Production
Update `.env` for production:
```bash
# Site Configuration
SITE_URL=https://your-domain.com
SITE_NAME=Your Site Name
SITE_AUTHOR=Your Name
SITE_DESCRIPTION=Your site description
# Authentication
ADMIN_ME=https://your-identity.com
SESSION_SECRET=<your-random-secret>
# Flask Configuration
FLASK_ENV=production
FLASK_DEBUG=0
# Container paths (these are set by compose.yaml)
DATA_PATH=/data
NOTES_PATH=/data/notes
DATABASE_PATH=/data/starpunk.db
# RSS Feed
FEED_MAX_ITEMS=50
FEED_CACHE_SECONDS=300
# Application
VERSION=0.6.0
ENVIRONMENT=production
```
**Important**: Never set `DEV_MODE=true` in production!
## Data Persistence
### Volume Mounts
All application data is stored in the mounted volume:
```
container-data/
├── notes/ # Markdown note files
└── starpunk.db # SQLite database
```
### Backup Strategy
**Manual Backup**:
```bash
# Create timestamped backup
tar -czf starpunk-backup-$(date +%Y%m%d).tar.gz container-data/
# Copy to safe location
cp starpunk-backup-*.tar.gz /backup/location/
```
**Automated Backup** (cron):
```bash
# Add to crontab
crontab -e
# Daily backup at 2 AM
0 2 * * * cd /path/to/starpunk && tar -czf /backup/starpunk-$(date +\%Y\%m\%d).tar.gz container-data/
```
### Restore from Backup
```bash
# Stop container
podman stop starpunk
podman rm starpunk
# Restore data
rm -rf container-data
tar -xzf starpunk-backup-20251119.tar.gz
# Restart container
podman-compose up -d
```
## Health Checks and Monitoring
### Health Check Endpoint
The container includes a `/health` endpoint that checks:
- Database connectivity
- Filesystem access
- Application state
**Usage**:
```bash
curl http://localhost:8000/health
```
**Response**:
```json
{
"status": "healthy",
"version": "0.6.0",
"environment": "production"
}
```
**Status Codes**:
- `200`: Application healthy
- `500`: Application unhealthy (check logs)
### Container Health Check
The Containerfile includes an automatic health check that runs every 30 seconds:
```bash
# View health status
podman inspect starpunk | grep -A 5 Health
# Docker
docker inspect starpunk | grep -A 5 Health
```
### Log Monitoring
**View logs**:
```bash
# Real-time logs
podman logs -f starpunk
# Last 100 lines
podman logs --tail 100 starpunk
# Docker
docker logs -f starpunk
```
**Log rotation** is configured in `compose.yaml`:
- Max size: 10MB per file
- Max files: 3
- Total max: 30MB
## Troubleshooting
### Container Won't Start
**Check logs**:
```bash
podman logs starpunk
```
**Common issues**:
1. **Port already in use**:
```bash
# Find process using port 8000
lsof -i :8000
# Change port in compose.yaml or run command
-p 127.0.0.1:8080:8000
```
2. **Permission denied on volume**:
```bash
# Podman: Use --userns=keep-id
podman run --userns=keep-id ...
# Or fix ownership
chown -R $(id -u):$(id -g) container-data
```
3. **Database initialization fails**:
```bash
# Check volume mount
podman inspect starpunk | grep Mounts -A 10
# Verify directory exists
ls -la container-data/
```
### Health Check Fails
**Symptoms**: `curl http://localhost:8000/health` returns error or no response
**Checks**:
```bash
# 1. Is container running?
podman ps | grep starpunk
# 2. Check container logs
podman logs starpunk | tail -20
# 3. Verify port binding
podman port starpunk
# 4. Test from inside container
podman exec starpunk curl localhost:8000/health
```
### IndieAuth Not Working
**Requirements**:
- SITE_URL must be HTTPS (not HTTP)
- SITE_URL must match your public domain exactly
- ADMIN_ME must be a valid IndieAuth identity
**Test**:
```bash
# Verify SITE_URL in container
podman exec starpunk env | grep SITE_URL
# Should output: SITE_URL=https://your-domain.com
```
### Data Not Persisting
**Verify volume mount**:
```bash
# Check bind mount
podman inspect starpunk | grep -A 5 Mounts
# Should show:
# "Source": "/path/to/container-data"
# "Destination": "/data"
```
**Test persistence**:
```bash
# Create test file
podman exec starpunk touch /data/test.txt
# Stop and remove container
podman stop starpunk && podman rm starpunk
# Check if file exists on host
ls -la container-data/test.txt
# Restart container
podman-compose up -d
# Verify file still exists
podman exec starpunk ls /data/test.txt
```
## Performance Tuning
### Worker Configuration
The default configuration uses 4 Gunicorn workers. Adjust based on CPU cores:
**Formula**: `workers = (2 × CPU_cores) + 1`
**Update in compose.yaml**:
```yaml
environment:
- WORKERS=8 # For 4 CPU cores
```
### Memory Limits
Default limits in `compose.yaml`:
```yaml
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
```
**Increase for high-traffic sites**:
```yaml
deploy:
resources:
limits:
cpus: '2.0'
memory: 1G
```
### Database Optimization
For sites with many notes (>1000):
```bash
# Run SQLite VACUUM periodically
podman exec starpunk sqlite3 /data/starpunk.db "VACUUM;"
# Add to cron (monthly)
0 3 1 * * podman exec starpunk sqlite3 /data/starpunk.db "VACUUM;"
```
## Security Best Practices
### 1. Non-Root User
The container runs as user `starpunk` (UID 1000), not root.
**Verify**:
```bash
podman exec starpunk whoami
# Output: starpunk
```
### 2. Network Isolation
Bind to localhost only:
```yaml
ports:
- "127.0.0.1:8000:8000" # ✓ Secure
# Not: "8000:8000" # ✗ Exposes to internet
```
### 3. Secrets Management
**Never commit `.env` to version control!**
**Generate strong secrets**:
```bash
python3 -c "import secrets; print(secrets.token_hex(32))"
```
### 4. Regular Updates
**Update base image**:
```bash
# Rebuild with latest Python 3.11
podman build --no-cache -t starpunk:0.6.0 -f Containerfile .
```
**Update dependencies**:
```bash
# Update requirements.txt
uv pip compile requirements.txt --upgrade
# Rebuild container
podman build -t starpunk:0.6.0 -f Containerfile .
```
### 5. TLS/HTTPS Only
**Required for IndieAuth!**
- Use reverse proxy with HTTPS
- Set `SITE_URL=https://...` (not http://)
- Enforce HTTPS redirects
## Maintenance
### Regular Tasks
**Weekly**:
- Check logs for errors
- Verify backups are running
- Monitor disk space
**Monthly**:
- Update dependencies and rebuild
- Vacuum SQLite database
- Review resource usage
**Quarterly**:
- Security audit
- Review and rotate secrets
- Test backup restore procedure
### Updating StarPunk
```bash
# 1. Backup data
tar -czf backup-pre-update.tar.gz container-data/
# 2. Stop container
podman stop starpunk
podman rm starpunk
# 3. Pull/build new version
git pull
podman build -t starpunk:0.7.0 -f Containerfile .
# 4. Update compose.yaml version
sed -i 's/starpunk:0.6.0/starpunk:0.7.0/' compose.yaml
# 5. Restart
podman-compose up -d
# 6. Verify
curl http://localhost:8000/health
```
## Resources
### Documentation
- [Phase 5 Design](../designs/phase-5-rss-and-container.md)
- [Containerfile](../../Containerfile)
- [Compose Configuration](../../compose.yaml)
- [Caddy Example](../../Caddyfile.example)
- [Nginx Example](../../nginx.conf.example)
### External Resources
- [Podman Documentation](https://docs.podman.io/)
- [Docker Documentation](https://docs.docker.com/)
- [Gunicorn Configuration](https://docs.gunicorn.org/)
- [Caddy Documentation](https://caddyserver.com/docs/)
- [Nginx Documentation](https://nginx.org/en/docs/)
## Support
For issues or questions:
- Check this documentation first
- Review container logs: `podman logs starpunk`
- Verify health endpoint: `curl http://localhost:8000/health`
- Check GitHub issues (if project is on GitHub)
---
**Document Version**: 1.0
**StarPunk Version**: 0.6.0
**Last Updated**: 2025-11-19