Guide de déploiement

Héberger Grist sur un VPS : tableur-base de données open-source

Déployer sur un VPS Cloud →

Tutoriel

Héberger Grist sur un VPS : tableur-base de données open-source

Self-hosting7 min de lecture3 étapes

Google Sheets stocke des listes. Airtable ajoute des relations entre tables, mais facture 20 $ par utilisateur par mois. Grist fait les deux, en open-source, sur votre propre serveur : des colonnes typées, des références entre tables, des formules Python qui s'exécutent côté serveur, et une API REST générée automatiquement sur chaque document. Résultat : un outil de modélisation de données complet, sans abonnement, sans vendor lock-in.

Sommaire· Pourquoi Grist plutôt qu'un Google Sheets ou un Airtable1/9
  1. 01Pourquoi Grist plutôt qu'un Google Sheets ou un Airtable
  2. 02Ce que Grist fait que Google Sheets ne fait pas
  3. 03Prérequis et architecture Docker
  4. 04Docker Compose complet et déploiement
  5. 05Formules Grist avancées : VLOOKUP et références croisées
  6. 06Authentification SSO avec OIDC (facultatif)
  7. 07Sauvegardes : volumes Docker et export
  8. 08Dépannage courant
  9. 09Grist vs NocoDB vs Baserow : choisir son tableur open-source

Pourquoi Grist plutôt qu'un Google Sheets ou un Airtable

Google Sheets est un tableur : il stocke des données à plat, les calculs sont limités à des fonctions de feuille, et les relations entre feuilles reposent sur des VLOOKUP fragiles. Airtable résout cela avec des bases relationnelles, mais à 20 $ par utilisateur par mois, l'addition monte vite pour une agence ou une équipe technique.

Grist combine les deux mondes : une interface tableur familière sur une base de données relationnelle réelle. Chaque document contient des tables avec des colonnes typées (texte, numérique, date, référence, pièce jointe), des relations explicites entre tables via des colonnes de référence, et des formules Python qui s'exécutent côté serveur — pas dans le navigateur. Sur votre VPS, Grist coûte 0 $ par mois supplémentaire.

Ce que Grist fait que Google Sheets ne fait pas

  • Relations entre tables : une colonne de référence dans Commandes pointe vers Clients — pas un VLOOKUP cassable, une vraie clé étrangère avec autocomplétion.
  • Formules Python côté serveur : import datetime, import re, import uuid — et toute bibliothèque Python pure. Les formules s'exécutent dans un sandbox serveur, pas dans le navigateur.
  • API REST automatique : chaque document expose /api/docs/<id>/tables/<table>/records sans configuration. Intégrez Grist dans n8n, Make ou tout client HTTP.
  • Vues multiples sur les mêmes données : grid, carte, graphique, calendrier, widget personnalisé — chaque vue est filtrée et configurée indépendamment sans dupliquer les données.
  • Règles d'accès par ligne et par colonne : limitez la lecture ou l'écriture d'une ligne selon l'attribut de l'utilisateur — assez fin pour des documents multi-tenant.

Prérequis et architecture Docker

Un VPS avec 1 Go de RAM minimum et Docker installé suffit pour démarrer. Grist se déploie en un seul conteneur gristlabs/grist qui embarque Node.js (serveur web), Python (sandbox de formules) et SQLite (stockage des documents). Un volume nommé /persist stocke les fichiers .grist — un fichier par document. Aucun service externe requis : pas de PostgreSQL, pas de Redis, pas de Celery.

Le port par défaut est 8484. Vérifiez qu'il est accessible depuis localhost avant de configurer le reverse proxy (curl -sI http://localhost:8484/).

Docker Compose complet et déploiement

  1. Créer l'arborescence et le docker-compose.yml

    Connectez-vous en SSH sur votre VPS et créez le répertoire de stack :

    mkdir -p /opt/stacks/grist && cd /opt/stacks/grist

    Créez le fichier docker-compose.yml suivant :

    services:
      grist:
        image: gristlabs/grist:1.7.20
        container_name: grist
        restart: unless-stopped
        ports:
          - "127.0.0.1:8484:8484"
        volumes:
          - grist_data:/persist
        environment:
          GRIST_SESSION_SECRET: "REMPLACEZ_PAR_openssl_rand_hex_32"
          APP_HOME_URL: "https://grist.votredomaine.com"
          GRIST_DEFAULT_EMAIL: "[email protected]"
          GRIST_SANDBOX_FLAVOR: "gvisor"
    
    volumes:
      grist_data:

    Générez le secret de session avant de démarrer : openssl rand -hex 32. Ne réutilisez jamais cette valeur entre deux instances.

  2. Démarrer et vérifier

    docker compose up -d
    docker compose logs -f grist

    Attendez le message Grist server ready. Vérifiez le statut HTTP :

    curl -sI http://localhost:8484/

    Vous devez obtenir HTTP/1.1 200 OK ou une redirection 302. Si le conteneur redémarre en boucle, consultez la section Dépannage ci-dessous.

  3. Configurer nginx comme reverse proxy HTTPS

    Installez nginx et certbot sur le VPS, puis créez /etc/nginx/sites-available/grist.conf :

    server {
        listen 80;
        server_name grist.votredomaine.com;
        return 301 https://$host$request_uri;
    }
    
    server {
        listen 443 ssl;
        server_name grist.votredomaine.com;
    
        ssl_certificate /etc/letsencrypt/live/grist.votredomaine.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/grist.votredomaine.com/privkey.pem;
        ssl_protocols TLSv1.2 TLSv1.3;
    
        # En-têtes de sécurité
        add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
        add_header X-Frame-Options SAMEORIGIN always;
        add_header X-Content-Type-Options nosniff always;
        add_header Referrer-Policy strict-origin-when-cross-origin always;
    
        location / {
            proxy_pass http://127.0.0.1:8484;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_read_timeout 300s;
        }
    }

    Obtenez le certificat puis activez le vhost :

    certbot certonly --nginx -d grist.votredomaine.com
    ln -s /etc/nginx/sites-available/grist.conf /etc/nginx/sites-enabled/
    nginx -t && systemctl reload nginx

    Mettez ensuite à jour APP_HOME_URL dans le Compose et redémarrez le conteneur : docker compose up -d.

Formules Grist avancées : VLOOKUP et références croisées

Grist remplace les VLOOKUP fragiles de Google Sheets par des colonnes de référence et des fonctions de lookup typées. Dans une table Commandes avec une colonne Client de type Reference → Clients, accéder au nom du client s'écrit simplement $Client.Nom — pas de VLOOKUP.

Pour des agrégations, Grist propose lookupRecords() et lookupOne() :

# Dans la table Clients — montant total des commandes d'un client
SUM(Commandes.lookupRecords(Client=$id).Montant)

# Dans la table Commandes — récupérer le code postal du client
Clients.lookupOne(Nom=$Client.Nom).CodePostal

# Calcul de marge avec import Python standard
import datetime
delai = (datetime.date.today() - $DateCommande).days
$Montant * (1 - 0.02 * delai // 30)  # remise progressive par mois

Les formules Python s'exécutent dans un sandbox gVisor (variable GRIST_SANDBOX_FLAVOR=gvisor) : le processus Python n'a pas accès au système de fichiers ni au réseau, ce qui isole les calculs même dans un contexte multi-tenant.

Authentification SSO avec OIDC (facultatif)

Par défaut, GRIST_DEFAULT_EMAIL connecte automatiquement un seul utilisateur sans mot de passe — suffisant pour un usage solo ou une équipe de confiance sur réseau privé. Pour un déploiement multi-utilisateurs avec authentification réelle, Grist supporte OpenID Connect (OIDC) avec Authentik, Keycloak, Google Workspace ou tout provider compatible.

Ajoutez ces variables dans le Compose :

environment:
  GRIST_OIDC_IDP_ISSUER: "https://auth.votredomaine.com/application/o/grist/"
  GRIST_OIDC_IDP_CLIENT_ID: "votre-client-id"
  GRIST_OIDC_IDP_CLIENT_SECRET: "votre-client-secret"
  GRIST_OIDC_IDP_SCOPES: "openid email profile"
  GRIST_OIDC_SP_HOST: "https://grist.votredomaine.com"

Retirer GRIST_DEFAULT_EMAIL dès qu'OIDC est configuré — les deux variables sont incompatibles. Le premier utilisateur qui se connecte via OIDC devient administrateur du site.

Sauvegardes : volumes Docker et export

Les documents Grist résident dans le volume Docker grist_data, sous /persist/docs/. Deux stratégies complémentaires :

Snapshot du volume (sauvegarde complète, recommandé) : montez /persist/docs/ dans un job Backrest (disponible dans le catalogue ServOrbit). Backrest snapshote le dossier via restic et envoie les incréments chiffrés vers S3, Backblaze B2 ou SFTP — sans arrêter Grist.

# Sauvegarde manuelle d'urgence
docker run --rm \
  -v grist_data:/source:ro \
  -v /backups/grist:/dest \
  alpine tar czf /dest/grist-$(date +%Y%m%d).tar.gz -C /source .

Export par document : dans Grist, menu Document → Export → .grist (format natif SQLite) ou CSV/XLSX par table. L'export .grist est suffisant pour migrer un document vers une autre instance.

Grist conserve également un historique de snapshots par document (Document → History) — pratique pour restaurer une version précédente sans quitter l'interface, jusqu'à la limite de rétention configurée.

Dépannage courant

Le conteneur redémarre en boucle : vérifiez les logs (docker compose logs grist). La cause la plus fréquente est un GRIST_SESSION_SECRET vide ou trop court — il doit faire au moins 32 caractères hexadécimaux. Vérifiez aussi les permissions du volume : docker exec grist ls -la /persist.

Erreurs CORS sur l'API : si vous appelez /api/docs/… depuis un domaine différent, ajoutez GRIST_ALLOWED_HOSTS=grist.votredomaine.com et assurez-vous que APP_HOME_URL correspond exactement à l'URL publique (avec https://, sans slash final).

Lenteur sur grands datasets : Grist évalue toutes les formules à chaque modification. Sur des tables de plus de 100 000 lignes, limitez les lookupRecords() à des colonnes indexées et évitez les formules Python avec imports lourds dans des colonnes de trigger. La variable GRIST_MAX_ROWS_PER_TABLE (défaut non borné) permet de poser une limite préventive.

Problème d'accès après changement de domaine : mettez à jour APP_HOME_URL, effacez les cookies du navigateur et redémarrez le conteneur. Les sessions actives utilisent l'ancienne URL et deviennent invalides.

Grist vs NocoDB vs Baserow : choisir son tableur open-source

Faites défiler le tableau

CritèreGristNocoDBBaserow
StockageSQLite par document (fichier .grist)PostgreSQL ou MySQL (base partagée)PostgreSQL (base partagée)
FormulesPython côté serveur (sandbox)Pas de formules nativesFormules limitées, JS côté client
API RESTAutomatique sur chaque documentAutomatique sur chaque baseAutomatique
Auth SSOOIDC natifLDAP, OIDC (Enterprise)SSO payant (plans > Pro)
SauvegardesFichier .grist par documentDump PostgreSQLDump PostgreSQL
Cas d'usage idéalModélisation de données, formules complexesRemplacement Airtable orienté équipeInterface no-code pour équipes métier
Ressources (démarrage)1 Go RAM, Docker2 Go RAM, Docker + PostgreSQL2 Go RAM, Docker + PostgreSQL

Pour mettre à jour Grist, modifiez le tag d'image dans le Compose (gristlabs/grist:1.7.20 → version suivante), arrêtez le conteneur (docker compose down), puis redémarrez (docker compose up -d). Le volume grist_data persiste entre les versions. Consultez les release notes sur github.com/gristlabs/grist-core avant chaque mise à jour — certaines versions introduisent des migrations de schéma qui ne sont pas réversibles.

Votre Airtable, sur votre serveur

Déployez Grist sur un VPS ServOrbit et modélisez vos données sans abonnement, sans vendor lock-in — API REST, formules Python, collaboration temps réel.

Besoin d'aide ?

Parcourez notre centre d'aide et notre FAQ, ou contactez notre équipe — rappel, WhatsApp ou e-mail. Support en français, anglais et arabe.

Écrire sur WhatsApps'ouvre dans un nouvel onglet