Pourquoi self-héberger Trigger.dev sur un VPS
Trigger.dev remplace les files d'attente maison (BullMQ, cron fragiles, Lambda qui timeout) par une plateforme unifiée : tâches durables, retries automatiques, gestion de la concurrence et tableau de bord en temps réel. La version cloud facture au nombre d'exécutions et limite la durée des jobs, ce qui devient vite contraignant pour des traitements ETL, des envois d'emails en masse ou des appels d'API lents.
En self-hosted sur un VPS, vous gardez vos workers chez vous : vos clés API tierces (OpenAI, Stripe, Resend) ne transitent jamais par un tiers, et vos jobs peuvent tourner 10 minutes ou plusieurs heures sans être coupés. La stack repose sur PostgreSQL, Redis et Docker, ce qui la rend reproductible et facile à sauvegarder. L'installation est totalement gratuite : aucune licence, aucun abonnement — seul le coût du VPS s'applique.
Ce que vous gagnez en auto-hébergeant
- Aucune limite sur la durée ou le nombre d'exécutions de vos tâches en arrière-plan.
- Vos secrets (clés API, tokens) restent dans votre infrastructure, jamais sur un SaaS tiers.
- Coût fixe prévisible : un seul VPS au lieu d'une facture qui grimpe avec le volume.
- Aucun saut réseau public si vos workers tournent à côté de votre base de données et de votre application.
- Contrôle total sur les versions, les variables d'environnement et la rétention des logs.
- Possibilité de déclencher des jobs depuis vos webhooks internes sans exposer de service externe.
- Mise à jour à votre rythme : vous choisissez quand passer à une nouvelle version de Trigger.dev.
Trigger.dev self-hosted vs cloud : ce qui change vraiment
Faites défiler le tableau
| Critère | Cloud (SaaS) | Self-hosted VPS |
|---|---|---|
| Durée maximale d'un job | Limitée (quelques minutes) | Sans limite de durée |
| Coût au volume | Croît avec les exécutions | Fixe (VPS mensuel) |
| Secrets / clés API | Transitent par le cloud | Restent dans votre infra |
| Mises à jour | Automatiques, imposées | À votre rythme |
| Accès réseau interne | Impossible sans tunnel | Direct, sans aller-retour par l'Internet public |
| Rétention des logs | Limitée par le plan | Contrôlée par vous |
| Installation initiale | Zéro configuration | ~30 minutes (ce guide) |
Prérequis matériels et logiciels
Trigger.dev est une stack relativement gourmande car elle combine l'application web, des workers, PostgreSQL et Redis. Prévoyez un VPS avec au minimum 4 Go de RAM et 2 vCPU pour un usage de production léger ; visez 8 Go de RAM et 4 vCPU si vous exécutez beaucoup de jobs concurrents ou des traitements lourds. Comptez 40 Go de disque SSD pour la base de données, les logs et les images Docker.
Côté logiciel : Docker Engine 24+ et le plugin Docker Compose v2 (vérifiez avec docker compose version), un nom de domaine pointant vers l'IP du VPS (par exemple trigger.mondomaine.com), et le port 443 ouvert pour le HTTPS. Deux secrets sont indispensables : MAGIC_LINK_SECRET (authentification sans mot de passe) et ENCRYPTION_KEY (32 caractères hexadécimaux, chiffrement des variables d'environnement de vos projets). Générez-les avant de commencer — ils ne se régénèrent pas sans casser les données existantes.
Installer Trigger.dev avec Docker en 6 étapes
Préparer le VPS et installer Docker
Connectez-vous en SSH avec un utilisateur sudo, mettez à jour le système (
apt update && apt upgrade -y) puis installez Docker en une commande :curl -fsSL https://get.docker.com | sh. Ajoutez votre utilisateur au groupe docker (usermod -aG docker $USER) pour éviter de toujours passer parsudo. Créez un dossier dédié :mkdir -p /opt/trigger && cd /opt/trigger. Vérifiez que Compose v2 est disponible avecdocker compose version— la sortie doit afficherv2.x.xau minimum.Récupérer la stack officielle de self-hosting
Clonez le dépôt officiel :
git clone https://github.com/triggerdotdev/trigger.dev /opt/trigger/src. Naviguez dans le sous-dossier de déploiement :cd /opt/trigger/src/docker. Ce répertoire contient ledocker-compose.ymlqui orchestre l'application web (webapp), les workers, PostgreSQL et Redis. Copiez le fichier d'exemple vers votre configuration :cp .env.example .env. Ne lancez rien avant d'avoir configuré le.env— la stack refusera de démarrer avec les valeurs par défaut.Générer les secrets et configurer le domaine
Ouvrez
.envdans votre éditeur et renseignez au minimum ces variables :-
ENCRYPTION_KEY:openssl rand -hex 16(16 octets = 32 chars hex)
-MAGIC_LINK_SECRET:openssl rand -hex 16(idem)
-LOGIN_ORIGIN:https://trigger.mondomaine.com
-APP_ORIGIN:https://trigger.mondomaine.com
-POSTGRES_PASSWORD: un mot de passe fort (openssl rand -base64 24)
-REDIS_PASSWORD: idemLaissez
DATABASE_URLetREDIS_URLtelles quelles si vous utilisez les services internes du Compose — elles référencent les noms de service Docker. Définissez aussiSESSION_SECRETavec unopenssl rand -hex 32.Lancer la stack et créer le premier compte
Démarrez l'ensemble en arrière-plan :
docker compose up -d. Suivez les migrations de base de données en temps réel :docker compose logs -f webapp. Attendez le messageListening on port 3030avant de continuer — les migrations peuvent prendre 30 à 60 secondes au premier démarrage. Une fois l'application démarrée, le magic link d'inscription s'affiche dans les logs : copiez-le et ouvrez-le dans votre navigateur pour créer le premier compte administrateur. Si vous avez raté le lien :docker compose logs webapp | grep magic.Exposer l'application via un reverse proxy HTTPS
Placez Caddy devant l'application pour gérer le TLS automatiquement. Créez
/etc/caddy/Caddyfileavec ce contenu minimal :trigger.mondomaine.com { reverse_proxy localhost:3030 }Redémarrez Caddy (
systemctl reload caddy) : Let's Encrypt délivre le certificat en quelques secondes. Avec nginx, créez un vhost qui proxifie vershttp://127.0.0.1:3030et activez un certificat viacertbot --nginx. Vérifiez ensuite quehttps://trigger.mondomaine.comrépond correctement avant de passer à l'étape suivante.Connecter votre premier projet TypeScript
Dans votre projet Node.js, installez le SDK :
npm install @trigger.dev/sdk. Authentifiez le CLI sur votre instance :npx trigger.dev@latest login --api-url https://trigger.mondomaine.com. Créez ensuite votre premier projet dans le dashboard, récupérez la clé secrète du projet (sk_...) et ajoutez-la dans votre.envlocal :TRIGGER_SECRET_KEY=sk_.... Initialisez la configuration avecnpx trigger.dev@latest initet déployez votre premier job avecnpx trigger.dev@latest deploy.
Dépannage : erreurs fréquentes et leurs solutions
Voici les cinq erreurs les plus courantes rencontrées lors de l'installation de Trigger.dev en self-hosted.
Error: ENCRYPTION_KEY must be 32 characters — Vous avez utilisé openssl rand -base64 16 au lieu de openssl rand -hex 16. La forme base64 produit des caractères hors du jeu hexadécimal. Régénérez avec -hex 16 (exactement 32 chars).
webapp exited with code 1 au démarrage — Vérifiez les logs complets avec docker compose logs webapp. Cause la plus fréquente : DATABASE_URL incorrecte ou PostgreSQL pas encore prêt. Attendez 10 secondes et relancez avec docker compose restart webapp.
Le magic link de création de compte ne s'affiche pas — L'application a peut-être démarré avant la fin des migrations. Relancez docker compose restart webapp et regardez les logs dès le démarrage. Si le lien reste absent, vérifiez que LOGIN_ORIGIN correspond exactement à votre domaine (sans slash final).
Failed to connect dans le CLI lors du login — Le reverse proxy n'est pas encore actif ou le DNS ne pointe pas encore vers votre VPS. Testez localement avec curl http://127.0.0.1:3030/healthcheck depuis le VPS : si ça répond, le problème est côté proxy ou DNS.
Le job se déploie mais ne s'exécute pas — Vérifiez que les workers sont bien démarrés : docker compose ps doit montrer le service worker en état running. Si le worker est arrêté, docker compose up -d worker le redémarre. Vérifiez aussi que TRIGGER_SECRET_KEY dans votre projet correspond à la clé du bon projet dans le dashboard.
Sécuriser et maintenir votre instance
Une instance Trigger.dev expose le dashboard et l'API sur le même domaine. Quelques précautions réduisent la surface d'attaque sans compliquer l'exploitation.
Firewall : bloquez tous les ports sauf 22 (SSH), 80 et 443 (avec ufw allow pour chacun). Le port 3030 ne doit jamais être accessible directement depuis l'extérieur — il est réservé au reverse proxy local.
Rotation des secrets : MAGIC_LINK_SECRET peut être rotaté sans casser les données. ENCRYPTION_KEY, en revanche, chiffre les variables d'environnement des projets — une rotation exige une migration de données. Notez-les dans un gestionnaire de secrets (Bitwarden, 1Password ou HashiCorp Vault).
Mises à jour : Trigger.dev publie ses releases sur GitHub. Pour mettre à jour, tirez la nouvelle version (git pull dans /opt/trigger/src), reconstruisez les images (docker compose pull) et redémarrez (docker compose up -d). Les migrations de base de données s'appliquent automatiquement au démarrage de webapp.
Sauvegardes : planifiez un dump PostgreSQL quotidien vers un stockage externe. Un cron minimal : 0 3 * * * docker exec trigger-postgres-1 pg_dump -U postgres trigger | gzip > /opt/backups/trigger-$(date +%F).sql.gz.
Scalabilité horizontale des workers
Isolez les workers de l'application web sur des conteneurs séparés et limitez leur concurrence via la variable WORKER_CONCURRENCY (défaut : 10). Pour les jobs très gourmands en CPU, ajoutez un second VPS dédié aux workers pointant vers le même PostgreSQL et Redis : vous scalez horizontalement sans toucher au dashboard. Les workers sont sans état — ils n'ont besoin que de DATABASE_URL, REDIS_URL et ENCRYPTION_KEY pour rejoindre la flotte. Sur un petit VPS, commencez avec WORKER_CONCURRENCY=3 pour éviter la saturation mémoire lors de pics de jobs.
Écrire et déployer votre premier job Trigger.dev
Un job Trigger.dev est une fonction TypeScript exportée depuis un fichier trigger/ de votre projet. Voici un exemple minimal d'envoi d'email différé :
import { task } from "@trigger.dev/sdk/v3";
export const sendWelcomeEmail = task({
id: "send-welcome-email",
run: async (payload: { userId: string; email: string }) => {
// votre logique d'envoi ici
await sendEmail(payload.email, "Bienvenue !");
return { sent: true };
},
});Déployez avec npx trigger.dev@latest deploy. Le dashboard affiche alors votre job sous l'onglet Tasks. Déclenchez une exécution test depuis le dashboard ou depuis votre code : await sendWelcomeEmail.trigger({ userId: "u1", email: "[email protected]" }). Les retries automatiques s'appliquent en cas d'erreur — configurables avec l'option retry sur le task().
Pour les jobs longs (import CSV, traitement d'images), utilisez wait.for() pour suspendre l'exécution et ne pas bloquer un worker pendant les pauses réseau : Trigger.dev reprend le job là où il s'était arrêté, même après un redémarrage du container.
Cas d'usage les mieux adaptés au self-hosting
- Traitement ETL : import, transformation et chargement de grandes volumétries de données sans timeout.
- Envois transactionnels en masse : campagnes d'emails ou de notifications avec throttling maîtrisé.
- Pipelines IA : appels enchaînés vers OpenAI, Anthropic ou Hugging Face avec gestion des retries sur rate-limit.
- Synchronisation d'intégrations tierces : Stripe webhooks, Shopify, HubSpot — sans dépendre d'un SaaS intermédiaire.
- Tâches planifiées critiques : remplacement de cron fragiles par des jobs avec historique d'exécution et alertes.
- Génération de rapports PDF ou exports lourds : jobs de plusieurs minutes sans limite de durée.
Trigger.dev est totalement gratuit en self-hosted : aucune licence commerciale n'est requise pour un usage privé ou professionnel sur votre propre infrastructure. La licence MIT couvre le code open-source. Seul le code propriétaire de la version Enterprise (SSO SAML, audit logs avancés) est exclu — pour la grande majorité des équipes, la version communautaire auto-hébergée suffit largement.