# 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
