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
12 KiB
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
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
mkdir -p container-data/notes
3. Configure Environment
cp .env.example .env
# Edit .env with your values:
nano .env
Required settings:
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:
python3 -c "import secrets; print(secrets.token_hex(32))"
4. Run the Container
Using Podman
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
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
# 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):
pip install podman-compose
Run:
podman-compose up -d
View logs:
podman-compose logs -f
Stop:
podman-compose down
Docker Compose
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:
# 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:
# 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):
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:
sudo apt install nginx certbot python3-certbot-nginx
Configuration:
# 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:
# 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:
# 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):
# 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
# 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:
curl http://localhost:8000/health
Response:
{
"status": "healthy",
"version": "0.6.0",
"environment": "production"
}
Status Codes:
200: Application healthy500: Application unhealthy (check logs)
Container Health Check
The Containerfile includes an automatic health check that runs every 30 seconds:
# View health status
podman inspect starpunk | grep -A 5 Health
# Docker
docker inspect starpunk | grep -A 5 Health
Log Monitoring
View logs:
# 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:
podman logs starpunk
Common issues:
-
Port already in use:
# Find process using port 8000 lsof -i :8000 # Change port in compose.yaml or run command -p 127.0.0.1:8080:8000 -
Permission denied on volume:
# Podman: Use --userns=keep-id podman run --userns=keep-id ... # Or fix ownership chown -R $(id -u):$(id -g) container-data -
Database initialization fails:
# 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:
# 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:
# 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:
# Check bind mount
podman inspect starpunk | grep -A 5 Mounts
# Should show:
# "Source": "/path/to/container-data"
# "Destination": "/data"
Test persistence:
# 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:
environment:
- WORKERS=8 # For 4 CPU cores
Memory Limits
Default limits in compose.yaml:
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
Increase for high-traffic sites:
deploy:
resources:
limits:
cpus: '2.0'
memory: 1G
Database Optimization
For sites with many notes (>1000):
# 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:
podman exec starpunk whoami
# Output: starpunk
2. Network Isolation
Bind to localhost only:
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:
python3 -c "import secrets; print(secrets.token_hex(32))"
4. Regular Updates
Update base image:
# Rebuild with latest Python 3.11
podman build --no-cache -t starpunk:0.6.0 -f Containerfile .
Update dependencies:
# 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
# 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
External Resources
- Podman Documentation
- Docker Documentation
- Gunicorn Configuration
- Caddy Documentation
- Nginx Documentation
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