feat: add production docker-compose configuration and deployment documentation for Portainer integration
This commit is contained in:
133
DEPLOYMENT_GUIDE.md
Normal file
133
DEPLOYMENT_GUIDE.md
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# 🎯 Overdrachtsdocument – Productie-deployment van **Respellion Learning Platform** met Portainer
|
||||||
|
|
||||||
|
> **Doel** – Dit document geeft een *complete* handleiding zodat een ontwikkelaar of AI-agent de Respellion Learning Platform applicatie kan bouwen, configureren en in productie draaien op een Hetzner-VPS via Portainer.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Project-structuur
|
||||||
|
|
||||||
|
```
|
||||||
|
learning-platform/
|
||||||
|
│
|
||||||
|
├─ Dockerfile # Productie Dockerfile (Nginx + React/Vite)
|
||||||
|
├─ nginx.conf # Nginx configuratie voor Anthropic API proxy
|
||||||
|
├─ docker-compose.prod.yml # Productie-compose configuratie (Caddy SSL + Frontend)
|
||||||
|
└─ src/ # React frontend broncode
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Opmerking**: Deze applicatie is een 100% frontend applicatie ("Single Page Application"). De interne state wordt opgeslagen in `localStorage`. Er is **geen externe backend of database (zoals Postgres)** nodig. De enige netwerk calls gaan via de interne Nginx proxy direct naar de Anthropic API.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Kernbestanden – inhoud & rationale
|
||||||
|
|
||||||
|
### 2.1 `docker-compose.prod.yml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
frontend:
|
||||||
|
build: .
|
||||||
|
restart: always
|
||||||
|
expose:
|
||||||
|
- "80"
|
||||||
|
|
||||||
|
caddy:
|
||||||
|
image: caddy:2-alpine
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
environment:
|
||||||
|
- DOMAIN_NAME=${DOMAIN_NAME}
|
||||||
|
entrypoint:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
# Build Caddyfile on container start
|
||||||
|
CLEAN_DOMAIN=$(printf '%s' "$DOMAIN_NAME" | tr -d ';')
|
||||||
|
cat > /etc/caddy/Caddyfile <<EOF
|
||||||
|
${CLEAN_DOMAIN} {
|
||||||
|
reverse_proxy /* frontend:80
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
# Run Caddy with the generated file
|
||||||
|
caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
|
||||||
|
volumes:
|
||||||
|
- caddy_data:/data
|
||||||
|
- caddy_config:/config
|
||||||
|
depends_on:
|
||||||
|
- frontend
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
caddy_data:
|
||||||
|
caddy_config:
|
||||||
|
```
|
||||||
|
|
||||||
|
**Belangrijkste punten**
|
||||||
|
|
||||||
|
| Item | Waarom |
|
||||||
|
|------|--------|
|
||||||
|
| **`frontend` (Nginx)** | Bevat de React applicatie en proxy-configuratie voor Anthropic. Exposeert intern poort 80. |
|
||||||
|
| **`caddy` (Reverse Proxy)** | Ontvangt alle externe requests op poort 80 en 443 en haalt volautomatisch een SSL Let's Encrypt certificaat op voor `DOMAIN_NAME`. Stuurt verkeer door naar `frontend:80`. |
|
||||||
|
| **Volumes `caddy_data/config`** | Slaan de SSL certificaten persistent op zodat Caddy niet bij elke herstart de rate-limits van Let's Encrypt overschrijdt. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2.2 `Dockerfile` (Productie)
|
||||||
|
|
||||||
|
De applicatie wordt in 2 fasen ("multi-stage") gebouwd:
|
||||||
|
1. **Node.js Build:** `npm install` en `npm run build` worden uitgevoerd om de statische Vite applicatie te compileren naar de `dist/` map.
|
||||||
|
2. **Nginx Serve:** Een lightweight Nginx Alpine image wordt gestart. Het pakt de `dist/` bestanden en past `nginx.conf` toe om API-aanroepen (CORS blokkades) te omzeilen door als proxy te fungeren.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Omgevingsvariabelen (te definiëren in Portainer)
|
||||||
|
|
||||||
|
| Variabele | Voorbeeld | Toelichting |
|
||||||
|
|-----------|-----------|-------------|
|
||||||
|
| `DOMAIN_NAME` | `leren.mijndomein.nl` | De domeinnaam die Caddy zal beveiligen met HTTPS. Zorg dat je DNS A-record naar je VPS-IP wijst. |
|
||||||
|
|
||||||
|
*(De Anthropic API key wordt ingevuld via de instellingen in de applicatie (Settings menu) door een admin, en veilig opgeslagen in localStorage van de gebruiker. Hij hoeft dus niet in Portainer te staan!)*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Deploy-procedure (stap-voor-stap)
|
||||||
|
|
||||||
|
### Portainer – stack aanmaken / bijwerken
|
||||||
|
|
||||||
|
| Stap | Actie | Details |
|
||||||
|
|------|-------|---------|
|
||||||
|
| **1** | Navigeer naar **Stacks → + Add stack** | Geef een herkenbare naam (bijv. `respellion-platform`). |
|
||||||
|
| **2** | **Repository URL** invullen | `https://github.com/<gebruiker>/learning-platform.git` |
|
||||||
|
| **3** | **Compose path** = `docker-compose.prod.yml` | Zorg dat dit exact overeenkomt met de bestandsnaam in de repo. |
|
||||||
|
| **4** | **Environment variables** toevoegen | Voeg `DOMAIN_NAME` toe. |
|
||||||
|
| **5** | **Deploy the stack** | Portainer start de applicatie en Caddy haalt direct het SSL certificaat op (max. 60 sec). |
|
||||||
|
| **6** | **Controle** | Surf naar `https://jouw-domein.nl`. Je zou de Respellion inlogscherm moeten zien. |
|
||||||
|
|
||||||
|
### Handmatige Force-Rebuild
|
||||||
|
|
||||||
|
Wanneer de repo is geüpdatet en je wil de nieuwe versie afdwingen via command-line:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.prod.yml up -d --build --no-cache
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Veelvoorkomende valkuilen & Fixes
|
||||||
|
|
||||||
|
| Symptom | Oorzaak | Oplossing |
|
||||||
|
|---------|---------|-----------|
|
||||||
|
| **SSL Error / ERR_CONNECTION_REFUSED** | DNS is nog niet actief of poort 443 staat dicht in je firewall (Hetzner). | Check Hetzner Cloud Console en open TCP poort 80 & 443 in de Inbound Firewall. |
|
||||||
|
| **Anthropic API Werkt Niet** | Foute key of Nginx proxy mist configuratie. | Open de app, ga naar Admin -> Settings en vul je Anthropic Key in. Controleer ook of `nginx.conf` correct wordt ingeladen in Nginx. |
|
||||||
|
| **Witte pagina na deploy** | Oude Docker image wordt gebruikt. | Doe een force-rebuild (`--build --no-cache`) in Portainer of via de console. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Checklist voor een nieuwe implementatie
|
||||||
|
|
||||||
|
- [ ] Repository up-to-date en gepusht naar `main`.
|
||||||
|
- [ ] `docker-compose.prod.yml` is gedefinieerd in Portainer.
|
||||||
|
- [ ] Environment variable `DOMAIN_NAME` is correct ingesteld.
|
||||||
|
- [ ] DNS A-Record staat op de juiste server.
|
||||||
|
- [ ] Na eerste inlog: Open "Admin" -> "Settings" en voeg de Anthropic API Key toe, klik op Opslaan.
|
||||||
37
docker-compose.prod.yml
Normal file
37
docker-compose.prod.yml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
services:
|
||||||
|
frontend:
|
||||||
|
build: .
|
||||||
|
restart: always
|
||||||
|
expose:
|
||||||
|
- "80"
|
||||||
|
|
||||||
|
caddy:
|
||||||
|
image: caddy:2-alpine
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
environment:
|
||||||
|
- DOMAIN_NAME=${DOMAIN_NAME}
|
||||||
|
entrypoint:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
# Build Caddyfile on container start
|
||||||
|
CLEAN_DOMAIN=$(printf '%s' "$DOMAIN_NAME" | tr -d ';')
|
||||||
|
cat > /etc/caddy/Caddyfile <<EOF
|
||||||
|
${CLEAN_DOMAIN} {
|
||||||
|
reverse_proxy /* frontend:80
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
# Run Caddy with the generated file
|
||||||
|
caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
|
||||||
|
volumes:
|
||||||
|
- caddy_data:/data
|
||||||
|
- caddy_config:/config
|
||||||
|
depends_on:
|
||||||
|
- frontend
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
caddy_data:
|
||||||
|
caddy_config:
|
||||||
Reference in New Issue
Block a user