# Installation — Firecrawl self-hosted sur Ubuntu (Nginx hors Docker)

Runbook pour le serveur de production (12 CPU / 32G, RAID1). À dérouler dans l'ordre.

> ⚠️ **Shell : ce runbook est écrit pour BASH.** Le shell de login du serveur est
> fish, qui ne supporte ni les heredocs (`<<'EOF'`), ni `$(. /etc/os-release …)`,
> ni `export`. Avant de coller un bloc de commandes : taper `bash`, coller le
> bloc tel quel, puis `exit`. (Erreurs vécues sinon : « Unsupported use of '=' »,
> « Expected a string, but found a redirection ».)

> ⚠️ **Permissions /data** : les `sudo mkdir` de l'étape 3 créent des dossiers
> possédés par root — un `scp`/`rsync` en utilisateur normal échouera. Faire
> d'abord : `sudo chown -R $USER:$USER /data/firecrawl/stack`.

## 0. Contrainte disque — À LIRE AVANT TOUT

```
/       19,5G  (md1, RAID1)  ← système, quasi plein par nature. RIEN d'autre n'y va.
/boot    506M  (md0, RAID1)
/data   208G   (md2, RAID1)  ← TOUT vit ici : data-root Docker, stack, données.
```

Le data-root Docker par défaut est `/var/lib/docker` (sur `/`) : les images
Firecrawl + Playwright + Postgres + RabbitMQ pèsent plusieurs Go et rempliraient
la partition système. **L'étape 2 (déplacement du data-root) doit être faite
AVANT le moindre `docker pull`.**

## 1. Installer Docker CE (dépôt officiel)

```bash
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
  https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" \
  | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
```

## 2. Déplacer le data-root Docker sur /data — AVANT tout pull

```bash
sudo systemctl stop docker
sudo mkdir -p /data/docker
sudo tee /etc/docker/daemon.json > /dev/null <<'EOF'
{
  "data-root": "/data/docker"
}
EOF
sudo systemctl start docker

# Vérifier — doit afficher /data/docker :
docker info | grep "Docker Root Dir"
```

## 3. Déployer la stack

```bash
sudo mkdir -p /data/firecrawl/{stack,redis,rabbitmq,postgres}

# Copier le contenu de ce dossier (docker-compose.yaml, rabbitmq/, searxng/,
# .env.example) dans /data/firecrawl/stack/ — par ex. depuis le poste local :
#   scp -r Nouvelle-conf/firecrawl/* sgwsnlcupaqyxdqux@163.172.71.169:/data/firecrawl/stack/

cd /data/firecrawl/stack
cp .env.example .env

# Générer les 4 secrets requis (le compose refuse de démarrer sans) :
openssl rand -hex 24   # → FIRECRAWL_API_KEY (préfixer fc-)
openssl rand -hex 24   # → BULL_AUTH_KEY
openssl rand -hex 24   # → POSTGRES_PASSWORD
openssl rand -hex 24   # → SEARXNG_SECRET
vim .env
```

## 4. Démarrer et vérifier

```bash
cd /data/firecrawl/stack
docker compose pull
docker compose up -d
docker compose ps        # tout doit passer running/healthy (api : ~1-2 min)

# L'API ne doit écouter QUE sur loopback :
ss -tlnp | grep 3002     # attendu : 127.0.0.1:3002 — PAS 0.0.0.0

# Health check — routes vérifiées dans apps/api/src/index.ts upstream
# (« /test » n'existe pas/plus) :
curl http://127.0.0.1:3002/              # → message d'accueil de l'API
curl http://127.0.0.1:3002/is-production # → {"isProduction":...}

# Test de scrape :
curl -X POST http://127.0.0.1:3002/v2/scrape \
  -H 'Content-Type: application/json' \
  -d '{"url": "https://docs.firecrawl.dev", "formats": ["rawHtml"]}'
```

Si `nuq-postgres` boucle sur des erreurs SQL avec des données Postgres
préexistantes dans `/data/firecrawl/postgres` : le schéma ne s'initialise que
sur un répertoire vide. Les données de queue sont jetables → `docker compose
down`, vider le répertoire, `docker compose up -d`.

## 5. Accès externe via Nginx (optionnel — option B)

Uniquement si un accès depuis l'extérieur est nécessaire (ex. dev depuis le
laptop). Suivre les instructions en tête de `nginx/crawl.elevaseo.app.conf`
(remplacement de la clé, symlink sites-enabled, `nginx -t`, certbot).

```bash
# Sans clé → 403 :
curl -s -o /dev/null -w "%{http_code}\n" https://crawl.elevaseo.app/test
# Avec clé → 200 :
curl -s -o /dev/null -w "%{http_code}\n" \
  -H "Authorization: Bearer fc-<clé>" https://crawl.elevaseo.app/test
```

Pare-feu : n'ouvrir que 80/443 (et SSH). Le port 3002 ne doit jamais être
ouvert — il n'est de toute façon mappé que sur loopback.

## 6. Brancher l'app seo-insight (co-localisée)

Dans l'env du service analytics (cf. `SEO-INSIGHT-REQUIREMENTS.md`) :

```
FIRECRAWL_API_URL=http://127.0.0.1:3002
FIRECRAWL_API_KEY=<le même que le .env de la stack>
```

## 7. Exploitation

- **UI queues** : `http://127.0.0.1:3002/admin/{BULL_AUTH_KEY}/queues` (via
  tunnel SSH : `ssh -L 3002:127.0.0.1:3002 user@serveur`), ou via le vhost
  Nginx si l'option B est en place.
- **Capacité** : `NUM_WORKERS_PER_QUEUE` (défaut 8) est LE bouton de capacité.
  L'augmenter si les jobs s'accumulent en file (deadline app : 5 min/job),
  en surveillant `docker stats` — playwright-service doit rester sous sa
  limite mémoire (6G).
- **Disque** : surveiller `df -h /data` (images + logs + volumes). Ménage :
  `docker system prune -af` après une mise à jour d'images.
- **Mise à jour** : `docker compose pull && docker compose up -d`.
- **Proxy anti-blacklist** (plus tard) : renseigner `PROXY_SERVER` /
  `PROXY_USERNAME` / `PROXY_PASSWORD` dans le `.env` puis
  `docker compose up -d` — zéro changement côté app.
