Files
mintel.me/apps/web/DEPLOYMENT_SUMMARY.md
Marc Mintel 103d71851c
Some checks failed
🧪 CI (QA) / 🧪 Quality Assurance (push) Failing after 1m3s
chore: overhaul infrastructure and integrate @mintel packages
- Restructure to pnpm monorepo (site moved to apps/web)
- Integrate @mintel/tsconfig, @mintel/eslint-config, @mintel/husky-config
- Implement Docker service architecture (Varnish, Directus, Gatekeeper)
- Setup environment-aware Gitea Actions deployment
2026-02-05 14:18:51 +01:00

4.9 KiB

🚀 Hetzner Deployment - Complete Plan

What's Ready

1. Docker Setup

  • Multi-stage Dockerfile (optimized build)
  • Nginx config with caching & security headers
  • Caddy reverse proxy with automatic SSL
  • Redis container for caching
  • Docker Compose for local & production

2. CI/CD Pipeline

  • Woodpecker .woodpecker.yml config
  • Automatic testing, building, deployment
  • Slack notifications (optional)
  • Gitea integration ready

3. Cache Architecture (Professional DI)

src/utils/cache/
├── interfaces.ts        # Contracts
├── memory-adapter.ts    # In-memory implementation
├── redis-adapter.ts     # Redis implementation  
└── index.ts            # Service + Factory

Usage:

// Choose your adapter at construction
const cache = new CacheService(new RedisCacheAdapter(config));
// or
const cache = new CacheService(new MemoryCacheAdapter(config));

// Use it
await cache.wrap('key', asyncFn, ttl);

4. Clean Analytics Architecture (Constructor DI)

src/utils/analytics/
├── interfaces.ts        # Contracts
├── plausible-adapter.ts # Plausible implementation
└── index.ts            # Service + Factory

Usage:

// Choose adapter at construction
const analytics = new AnalyticsService(new PlausibleAdapter(config));

// Track events
await analytics.trackEvent('Page Load', { loadTime: 123 });
await analytics.trackOutboundLink(url, text);
await analytics.trackSearch(query, path);

Astro Component:

---
import { createPlausibleAnalytics } from '../utils/analytics';
const analytics = createPlausibleAnalytics({ domain, scriptUrl });
const adapter = analytics.getAdapter();
const scriptTag = adapter.getScriptTag?.();
---
{scriptTag && <Fragment set:html={scriptTag} />}

5. Deployment Scripts

  • deploy.sh - Manual deployment
  • docker-compose.yml - Container orchestration
  • DEPLOYMENT.md - Complete documentation

🎯 Simple Deployment Flow

Option A: Manual (5 minutes)

# On Hetzner VM
git clone your-repo /opt/mintel
cd /opt/mintel
echo "DOMAIN=mintel.me" > .env
echo "ADMIN_EMAIL=admin@mintel.me" >> .env
./deploy.sh

Option B: CI/CD (Zero touch)

# On your machine
git push origin main
# Woodpecker handles everything

📊 Performance Features

Feature Implementation Benefit
Static Caching Nginx 1-year cache Instant asset loading
Redis Cache CacheService wrapper Fast data retrieval
Gzip/Brotli Nginx compression ~70% smaller transfers
HTTP/2 Caddy reverse proxy Multiplexed requests
Auto-SSL Let's Encrypt via Caddy Zero config HTTPS

🔒 Security

  • HSTS headers
  • XSS protection
  • Clickjacking prevention
  • Server header hiding
  • Automatic SSL renewal

🏗️ Architecture (Clean & Decoupled)

Cache Pattern:

Your Code → CacheService → [Redis | Memory]Adapter
                    ↓
              CacheAdapter Interface

Analytics Pattern:

Your Code → AnalyticsService → [Plausible]Adapter
                    ↓
              AnalyticsAdapter Interface

Constructor Injection:

// Cache - Production
const cache = new CacheService(new RedisCacheAdapter(config));

// Cache - Development/Testing
const cache = new CacheService(new MemoryCacheAdapter(config));

// Analytics
const analytics = new AnalyticsService(new PlausibleAdapter(config));

📁 Files Created

mintel.me/
├── docker/
│   ├── Dockerfile
│   ├── nginx.conf
│   └── Caddyfile
├── docker-compose.yml
├── .woodpecker.yml
├── deploy.sh
├── DEPLOYMENT.md
├── DEPLOYMENT_SUMMARY.md
├── src/
│   ├── utils/cache/
│   │   ├── interfaces.ts
│   │   ├── memory-adapter.ts
│   │   ├── redis-adapter.ts
│   │   └── index.ts
│   ├── utils/analytics/
│   │   ├── interfaces.ts
│   │   ├── plausible-adapter.ts
│   │   └── index.ts
│   ├── components/
│   │   └── Analytics.astro (clean DI pattern)
│   └── layouts/
│       └── BaseLayout.astro (includes analytics)
└── package.json (ioredis added)

🎯 Next Steps

  1. Configure environment variables
  2. Test locally: docker-compose up
  3. Setup Woodpecker secrets in Gitea
  4. Deploy to Hetzner
  5. Monitor with Plausible

🚀 Result

  • Fast: Redis + Nginx caching
  • Secure: Auto SSL + security headers
  • Simple: One command deployment
  • Clean: Proper DI architecture
  • Smart: CI/CD automation
  • Private: Self-hosted analytics

Total setup time: ~15 minutes
Maintenance: Near zero
Performance: Excellent
Security: Production-ready


Ready to deploy! 🎉