Guide d’Installation : Supabase Auto-hébergé avec Traefik

Ce document détaille la procédure de déploiement d’une instance Supabase sur un serveur VPS disposant déjà d’une stack Docker et Traefik fonctionnelle.

Prérequis

Avant de commencer, validez que les éléments suivants sont en place :

  • Serveur VPS avec Docker et Docker Compose (v2+) installés.
  • Instance Traefik active et configurée.
  • Réseau Docker externe pour Traefik (ex: traefik_proxy).
  • Entrypoints Traefik pour les ports 80 (web) et 443 (websecure).
  • Résolveur de certificats Let’s Encrypt fonctionnel (ex: letsencrypt).
  • Enregistrements DNS de type A pointant vers l’IP du VPS pour les domaines choisis (ex: api.votre-domaine.com et studio.votre-domaine.com).

Préparation des Fichiers de Configuration

L’installation de Supabase nécessite des fichiers de configuration qui orchestrent les différents services. Le clonage du dépôt officiel est donc une étape indispensable pour les récupérer.

Clonez le dépôt officiel de Supabase dans un répertoire de travail (ex: /opt/supabase)

# Create the installation directory and navigate into it
mkdir -p /opt/supabase
cd /opt/supabase

# Clone the official repository
git clone --depth 1 [https://github.com/supabase/supabase](https://github.com/supabase/supabase)

Accédez au répertoire de configuration Docker. Toutes les commandes suivantes doivent être exécutées depuis cet emplacement

cd supabase/docker

Sécurisation Automatique de la Configuration

Cette étape prépare et sécurise le fichier d’environnement .env.

Créez le fichier de configuration à partir du modèle fourni.

cp .env.example .env

Exécutez le script de sécurisation ci-dessous. Il lit le fichier .env et remplace automatiquement tous les secrets par défaut par des valeurs cryptographiquement robustes

#!/bin/bash
# secure-supabase-final.sh - Script final et robuste pour sécuriser le fichier .env de Supabase.

# Le script s'arrêtera immédiatement si une commande échoue, y compris dans un pipeline.
set -eo pipefail

ENV_FILE=".env"

if [ ! -f "$ENV_FILE" ]; then
    echo "## ERREUR : Fichier .env non trouvé. Assurez-vous d'être dans le bon répertoire." >&2
    exit 1
fi

# Fonction pour générer des secrets en hexadécimal (évite les caractères spéciaux).
generate_secret() {
    openssl rand -hex 32
}

# Fonction pour encoder en Base64URL, nécessaire pour les JWT.
base64_url_encode() {
    printf "%s" "$1" | base64 | tr '+/' '-_' | tr -d '='
}

echo ">> Étape 1: Génération des nouveaux secrets..."
NEW_POSTGRES_PASSWORD=$(generate_secret)
NEW_JWT_SECRET=$(generate_secret)
NEW_DASHBOARD_PASSWORD=$(generate_secret)
NEW_SECRET_KEY_BASE=$(generate_secret)
NEW_VAULT_ENC_KEY=$(generate_secret)
NEW_PG_META_CRYPTO_KEY=$(generate_secret)

echo ">> Étape 2: Génération des clés JWT complètes avec auto-vérification..."
HEADER_B64=$(base64_url_encode '{"alg":"HS256","typ":"JWT"}')

# --- Génération de la clé ANON ---
ANON_PAYLOAD_B64=$(base64_url_encode '{"role":"anon","iss":"supabase"}')
ANON_UNSIGNED_TOKEN="$HEADER_B64.$ANON_PAYLOAD_B64"
#ANON_SIGNATURE=$(printf "%s" "$ANON_UNSIGNED_TOKEN" | openssl dgst -sha256 -mac HMAC -macopt key:"$NEW_JWT_SECRET" -binary | base64_url_encode)
ANON_SIGNATURE=$(echo -n "$ANON_UNSIGNED_TOKEN" | openssl dgst -sha256 -hmac "$NEW_JWT_SECRET" -binary | base64 | tr '+/' '-_' | tr -d '=')


if [ -z "$ANON_SIGNATURE" ]; then
    echo "## ERREUR CRITIQUE : La génération de la signature ANON a échoué. Arrêt." >&2
    exit 1
fi
NEW_ANON_KEY="$ANON_UNSIGNED_TOKEN.$ANON_SIGNATURE"

# --- Génération de la clé SERVICE_ROLE ---
SERVICE_PAYLOAD_B64=$(base64_url_encode '{"role":"service_role","iss":"supabase"}')
SERVICE_UNSIGNED_TOKEN="$HEADER_B64.$SERVICE_PAYLOAD_B64"
#SERVICE_SIGNATURE=$(printf "%s" "$SERVICE_UNSIGNED_TOKEN" | openssl dgst -sha256 -mac HMAC -macopt key:"$NEW_JWT_SECRET" -binary | base64_url_encode)
SERVICE_SIGNATURE=$(echo -n "$SERVICE_UNSIGNED_TOKEN" | openssl dgst -sha256 -hmac "$NEW_JWT_SECRET" -binary | base64 | tr '+/' '-_' | tr -d '=')

if [ -z "$SERVICE_SIGNATURE" ]; then
    echo "## ERREUR CRITIQUE : La génération de la signature SERVICE_ROLE a échoué. Arrêt." >&2
    exit 1
fi
NEW_SERVICE_ROLE_KEY="$SERVICE_UNSIGNED_TOKEN.$SERVICE_SIGNATURE"

echo ">> Étape 3: Mise à jour du fichier .env..."
sed -i "s#^POSTGRES_PASSWORD=.*#POSTGRES_PASSWORD=${NEW_POSTGRES_PASSWORD}#" "$ENV_FILE"
sed -i "s#^JWT_SECRET=.*#JWT_SECRET=${NEW_JWT_SECRET}#" "$ENV_FILE"
sed -i "s#^ANON_KEY=.*#ANON_KEY=${NEW_ANON_KEY}#" "$ENV_FILE"
sed -i "s#^SERVICE_ROLE_KEY=.*#SERVICE_ROLE_KEY=${NEW_SERVICE_ROLE_KEY}#" "$ENV_FILE"
sed -i "s#^DASHBOARD_PASSWORD=.*#DASHBOARD_PASSWORD=${NEW_DASHBOARD_PASSWORD}#" "$ENV_FILE"
sed -i "s#^SECRET_KEY_BASE=.*#SECRET_KEY_BASE=${NEW_SECRET_KEY_BASE}#" "$ENV_FILE"
sed -i "s#^VAULT_ENC_KEY=.*#VAULT_ENC_KEY=${NEW_VAULT_ENC_KEY}#" "$ENV_FILE"
sed -i "s#^PG_META_CRYPTO_KEY=.*#PG_META_CRYPTO_KEY=${NEW_PG_META_CRYPTO_KEY}#" "$ENV_FILE"

echo "------------------------------------------------------------"
echo "✅ Fichier .env sécurisé avec succès."
echo "Vérification de la clé générée :"
grep "SERVICE_ROLE_KEY" "$ENV_FILE"
echo "------------------------------------------------------------"

Personnalisation du Fichier .env

Ouvrez le fichier nano .env et modifiez les sections suivantes avec vos propres informations.

URLs de l’application

Modifiez ces URLs pour correspondre à vos noms de domaine.

# The public URL of your frontend application (used in auth emails)
SITE_URL=https://app.votre-domaine.com

# The public URL of your Supabase API
API_EXTERNAL_URL=https://api.votre-domaine.com

# The URL the dashboard will use to connect to the API
SUPABASE_PUBLIC_URL=https://api.votre-domaine.com

# Domains for Traefik routers
API_DOMAIN=api.votre-domaine.com
STUDIO_DOMAIN=studio.votre-domaine.com

Configuration SMTP (Email)

Remplacez les valeurs par défaut par les identifiants de votre service d’email transactionnel (ex: Brevo, SendGrid).

# The email address that sends system emails (must be verified in your SMTP provider)
[email protected]

# SMTP provider credentials
SMTP_HOST=<votre_hôte_smtp>
SMTP_PORT=<port_smtp>
SMTP_USER=<votre_login_smtp>
SMTP_PASS=<votre_mot_de_passe_ou_cle_api_smtp>
SMTP_SENDER_NAME=<LeNomDeVotreApp>

Configuration Analytics (Logflare)

Les services analytics et vector échoueront au démarrage sans clés valides.

  1. Créez un compte sur Logflare.
  2. Créez une « Source » et notez son Source ID (ressemble à UUID).
  3. Créez un « Access Token » avec le scope Ingest et notez la Clé API générée.

Remplissez ces valeurs dans le .env :

# Votre Source ID de Logflare
LOGFLARE_PUBLIC_ACCESS_TOKEN=<VOTRE_SOURCE_ID>

# Votre Access Token "Ingest" de Logflare
LOGFLARE_PRIVATE_ACCESS_TOKEN=<VOTRE_CLE_API_INGEST>

Sécurisation de Supabase Studio

Par défaut, l’accès à Supabase Studio est public. Il est indispensable de le protéger via une authentification gérée par Traefik.

sudo apt-get update && sudo apt-get install apache2-utils
printf "      - \"traefik.http.middlewares.supabase-studio-auth.basicauth.users=%s\"\n" "$(htpasswd -nb VOTRE_USER VOTRE_MOT_DE_PASSE | sed 's/\$/\$\$/g')"

Copiez la ligne de sortie complète (ex: - "traefik.http.middlewares.supabase-studio-auth.basicauth.users=VOTRE_USER:$$apr1$$...$$...").

Modifiez à nouveau le fichier nano docker-compose.override.yml et ajoutez les labels de middleware au service studio.

Intégration avec Traefik

Créez un fichier de surcharge docker-compose.override.yml pour définir la configuration spécifique à Traefik sans modifier les fichiers de base.

  1. Créez le fichier.nano docker-compose.override.yml
  2. Collez le contenu suivant, en remplaçant les domaines d’exemple par les vôtres.
# /opt/supabase/supabase/docker/docker-compose.override.yml
# This file contains overrides for Traefik integration.

services:
  studio:
    networks:
      - supabase
      - traefik_proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.supabase-studio.rule=Host(`${STUDIO_DOMAIN}`)"
      - "traefik.http.routers.supabase-studio.entrypoints=websecure"
      - "traefik.http.routers.supabase-studio.tls.certresolver=letsencrypt"
      - "traefik.http.services.supabase-studio.loadbalancer.server.port=3000"
      # Remplacez la ligne ci-dessous par la sortie 'htpasswd' (doublez les $$)
      - "traefik.http.middlewares.supabase-studio-auth.basicauth.users=USER:PWD"
      - "traefik.http.routers.supabase-studio.middlewares=supabase-studio-auth"

  kong:
    ports: [] # Disable direct port exposure
    networks:
      - supabase
      - traefik_proxy
    labels:
      # API Gateway Router (for REST, Auth, Storage, Functions)
      - "traefik.enable=true"
      - "traefik.http.routers.supabase-api.rule=Host(`${API_DOMAIN}`)"
      - "traefik.http.routers.supabase-api.entrypoints=websecure"
      - "traefik.http.routers.supabase-api.tls.certresolver=letsencrypt"
      
      # Websocket Router (for Realtime)
      # Realtime uses the same host but a specific path. Kong handles the upgrade.
      # No specific router is needed unless you have specific middleware for websockets.
      # The main router handles it.

      # Service definition for Kong
      - "traefik.http.services.supabase-kong.loadbalancer.server.port=8000"

  # The following services are accessed internally via Kong and do not need to be exposed.
  auth:
    networks:
      - supabase
  rest:
    networks:
      - supabase
  realtime:
    networks:
      - supabase
  storage:
    networks:
      - supabase
  imgproxy:
    networks:
      - supabase
  meta:
    networks:
      - supabase
  functions:
    networks:
      - supabase
  analytics:
    networks:
      - supabase
  db:
    networks:
      - supabase
  vector:
    networks:
      - supabase
  supavisor:
    networks:
      - supabase

networks:
  supabase:
    driver: bridge
  traefik_proxy:
    external: true

5.2 Configuration du MCP (Accès IA)

Pour permettre aux outils d’IA (Cursor, etc.) de se connecter, l’endpoint MCP doit être exposé via Kong et sécurisé.

  1. Trouver le sous-réseau (CIDR) de Traefik Cette étape est cruciale pour autoriser Kong à accepter les requêtes venant de Traefik.
docker network inspect traefik_proxy | grep "Subnet"
# Sortie exemple: "Subnet": "172.18.0.0/16"

Notez la plage CIDR (ex: 172.18.0.0/16).

Modifier la configuration de Kong Ouvrez le fichier kong.yml (ce fichier existe déjà).

nano volumes/api/kong.yml

Faites défiler jusqu’à la fin des services. Insérez le bloc mcp suivant avant le service final « catch-all » (- name: dashboard).

# /opt/supabase/supabase/docker/volumes/api/kong.yml

# ... (services existants: meta, etc.) ...

  - name: meta
    # ... (configuration 'meta' existante) ...
    plugins:
      # ...
      - name: acl
        # ...
        allow:
          - admin

  ###
  ### BLOC MCP À AJOUTER
  ###
  - name: mcp
    _comment: 'MCP: /mcp -> http://studio:3000/api/mcp (local access)'
    url: http://studio:3000/api/mcp
    routes:
      - name: mcp
        strip_path: true
        paths:
          - /mcp
    plugins:
      # Autorise l'accès depuis localhost et le réseau Traefik
      - name: cors
      - name: ip-restriction
        config:
          allow:
            - 127.0.0.1
            - ::1
            - <VOTRE_CIDR_TRAEFIK> # Ex: 172.18.0.0/16
          deny: []
  ###
  ### FIN DU BLOC MCP
  ###

  ## Protected Dashboard - catch all remaining routes
  - name: dashboard
    _comment: 'Studio: /* -> http://studio:3000/*'
# ... (reste du fichier) ...

Lancement

Lancez l’ensemble de la stack Supabase. Docker téléchargera les images nécessaires et démarrera tous les services en arrière-plan.

docker compose up -d
docker compose ps

Le déploiement est terminé. Les services seront accessibles via les URL configurées après quelques instants, le temps que Traefik génère les certificats SSL.

7. Vérification Post-Installation

Une fois les services démarrés, vous pouvez valider le bon fonctionnement des points d’accès principaux depuis votre machine locale.

Remplacez les URL et les clés par les vôtres.

7.1 Tester l’API REST

Cette commande vérifie que l’API (passant par Kong) est accessible et que la clé ANON_KEY est valide.

Note : Vous devez remplacer VOTRE_ANON_KEY par la clé générée dans votre .env et nom_de_votre_table par une table existante (ou attendez-vous à une erreur 404/403).

Bash

# Test de l'API REST
curl -i 'https://api.votre-domaine.com/rest/v1/nom_de_votre_table' \
  -H "apikey: VOTRE_ANON_KEY" \
  -H "Authorization: Bearer VOTRE_ANON_KEY"
  • Réponse attendue : HTTP/2 200 OK (ou HTTP/2 403 si la RLS bloque la table, ce qui est normal).

7.2 Tester le serveur MCP (Accès IA)

Cette commande valide que la route mcp dans kong.yml et le plugin ip-restriction fonctionnent.

Bash

# Test de l'endpoint MCP
curl -i 'https://api.votre-domaine.com/mcp' \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "MCP-Protocol-Version: 2025-06-18" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2025-06-18",
      "capabilities": {
        "elicitation": {}
      },
      "clientInfo": {
        "name": "test-client",
        "title": "Test Client",
        "version": "1.0.0"
      }
    }
  }'
  • Réponse attendue : HTTP/2 200 OK avec une réponse JSON {"result":{...}}.

Publications similaires