# Hetzner Deployment Guide

Complete deployment plan for your Astro blog on Hetzner using Docker, with your existing Gitea + Woodpecker setup.

## 🏗️ Architecture Overview

```
┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   Hetzner VM    │    │  Caddy Reverse   │    │   Plausible     │
│   (Ubuntu)      │◄──►│     Proxy        │◄──►│   Analytics     │
│                 │    │   (SSL/HTTP2)    │    │   (Existing)    │
└────────┬────────┘    └──────────────────┘    └─────────────────┘
         │
         │ Docker Network
         │
    ┌────▼─────┐    ┌──────────────┐    ┌──────────────┐
    │ Nginx    │    │   Redis      │    │   Website    │
    │ Static   │    │   Cache      │    │   (Astro)    │
    │ Server   │    │              │    │              │
    └──────────┘    └──────────────┘    └──────────────┘
```

## 📁 Project Structure

```
mintel.me/
├── docker/
│   ├── Dockerfile          # Multi-stage build
│   ├── nginx.conf          # Nginx config with caching
│   └── Caddyfile           # SSL reverse proxy
├── docker-compose.yml      # Local & Hetzner deployment
├── .woodpecker.yml         # CI/CD pipeline
├── deploy.sh               # Manual deployment script
├── src/
│   ├── utils/
│   │   └── cache/          # Cache abstraction layer
│   │       ├── interfaces.ts
│   │       ├── memory-adapter.ts
│   │       ├── redis-adapter.ts
│   │       ├── composite-adapter.ts
│   │       └── index.ts
│   ├── components/
│   │   └── Analytics.astro # Plausible integration
│   └── layouts/
│       └── BaseLayout.astro # Includes analytics
└── package.json            # Added ioredis dependency
```

## 🚀 Deployment Options

### Option 1: Manual Deployment (Quick Start)

```bash
# 1. Provision Hetzner VM (Ubuntu 22.04)
# 2. Install Docker + Docker Compose
ssh root@YOUR_HETZNER_IP "apt update && apt install -y docker.io docker-compose"

# 3. Copy files to server
scp -r . root@YOUR_HETZNER_IP:/opt/mintel

# 4. Set environment variables
ssh root@YOUR_HETZNER_IP "cat > /opt/mintel/.env << EOF
DOMAIN=mintel.me
ADMIN_EMAIL=admin@mintel.me
REDIS_URL=redis://redis:6379
EOF"

# 5. Deploy
ssh root@YOUR_HETZNER_IP "cd /opt/mintel && ./deploy.sh"
```

### Option 2: CI/CD with Woodpecker + Gitea (Recommended)

Your existing Woodpecker + Gitea setup will handle automatic deployments:

1. **Configure Woodpecker secrets** in your Gitea repo:
   - `SSH_PRIVATE_KEY` - Private key for Hetzner access
   - `DEPLOY_HOST` - Hetzner server IP
   - `DEPLOY_USER` - Usually `root`
   - `DOMAIN` - Your domain
   - `ADMIN_EMAIL` - Contact email

2. **Push to main branch** triggers automatic deployment

## 🔧 Environment Variables

See [ENV_SETUP.md](./ENV_SETUP.md) for detailed configuration guide.

**Quick setup:**
```bash
cp .env.example .env
# Edit .env with your values
```

**Required variables:**
- `DOMAIN` - Your domain (e.g., mintel.me)
- `ADMIN_EMAIL` - SSL certificate contact

**Optional variables:**
- `REDIS_URL` - Redis connection (default: `redis://redis:6379`)
- `PLAUSIBLE_DOMAIN` - Analytics domain
- `PLAUSIBLE_SCRIPT_URL` - Plausible script URL

## 🎯 Performance Optimization

### Redis Caching Strategy

The cache abstraction layer provides:

```typescript
// Usage in your Astro pages
import { withCache } from '../utils/cache';

// Cache expensive operations
const blogPosts = await withCache(
  'blog:posts',
  () => fetchBlogPosts(),
  3600 // 1 hour TTL
);
```

**Cache Layers:**
1. **Memory Cache** - Fast, single instance
2. **Redis Cache** - Distributed, multi-instance
3. **Composite Cache** - Auto-fallback Redis → Memory

### Nginx Caching

- **Static assets**: 1 year cache
- **HTML**: No cache (always fresh)
- **Gzip/Brotli**: Enabled

### Caddy Benefits

- **Automatic SSL** with Let's Encrypt
- **HTTP/2** out of the box
- **Automatic redirects** HTTP → HTTPS
- **Security headers** configured

## 📊 Analytics Integration

### Plausible Setup

Your existing Plausible instance is already self-hosted. Integration:

1. **Update Analytics.astro** with your Plausible domain:
   ```astro
   src="https://plausible.yourdomain.com/js/script.js"
   ```

2. **Add subdomain** to Caddyfile:
   ```caddy
   analytics.mintel.me {
       reverse_proxy http://YOUR_PLAUSIBLE_SERVER:8000
   }
   ```

3. **Track custom events**:
   ```javascript
   window.plausible('Page Load', { props: { loadTime: 123 }});
   ```

## 🔐 Security

### Caddy Security Headers
```caddy
header {
    Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
    X-Frame-Options "SAMEORIGIN"
    X-Content-Type-Options "nosniff"
    X-XSS-Protection "1; mode=block"
    Server "mintel"
}
```

### Docker Security
- Non-root user in containers
- Read-only filesystem where possible
- Limited capabilities

## 📈 Monitoring & Health

### Health Check Endpoint
```
https://mintel.me/health
```
Returns: `healthy`

### Docker Commands
```bash
# View logs
docker logs -f mintel-website

# Check Redis
docker exec -it mintel-redis redis-cli ping

# Restart services
docker-compose restart

# Update deployment
docker-compose pull && docker-compose up -d
```

## 🔄 CI/CD Pipeline Flow

1. **Push to Gitea** → Woodpecker triggers
2. **Test** → Run smoke tests
3. **Build** → Create Docker image
4. **Push** → Registry (optional)
5. **Deploy** → SSH to Hetzner, pull & restart

## 🚨 Troubleshooting

### SSL Issues
```bash
# Check Caddy logs
docker logs mintel-caddy

# Force certificate renewal
docker exec mintel-caddy caddy renew
```

### Redis Connection
```bash
# Test Redis
docker exec mintel-redis redis-cli ping

# Check Redis logs
docker logs mintel-redis
```

### Cache Issues
```bash
# Clear all cache
docker exec mintel-redis redis-cli FLUSHALL
```

## 📦 Dependencies

### Required
- `ioredis` - Redis client
- Already have: Astro, React, Tailwind

### Docker Images
- `nginx:alpine` - Web server
- `redis:7-alpine` - Cache
- `caddy:2-alpine` - Reverse proxy

## 🎯 Next Steps

1. ✅ **Configure** environment variables
2. ✅ **Test** locally with `docker-compose up`
3. ✅ **Setup** Woodpecker secrets
4. ✅ **Deploy** to Hetzner
5. ✅ **Monitor** with Plausible analytics

## 💡 Tips

- **First deployment**: Use manual `deploy.sh` to verify everything works
- **Updates**: CI/CD handles everything on git push
- **Rollback**: `docker-compose down && docker-compose up -d` with previous image
- **Scaling**: Add more web containers, Redis handles distributed cache

---

**Total setup time**: ~30 minutes  
**Maintenance**: Near zero (automated)  
**Performance**: Excellent (Redis + Nginx caching)  
**Analytics**: Privacy-focused (Plausible)