Comment installer Postiz sur Windows : configuration Docker + dépannage
Table des Matières
Postiz est le très populaire planificateur de publications open source pour les réseaux sociaux, une alternative auto-hébergée à Buffer ou Hootsuite qui dépasse les 33 000 étoiles sur GitHub. Ce guide détaille son installation sur Windows avec Docker Desktop.
Si vous êtes comme moi et que vous trouvez la documentation officielle de Postiz trop vague, alors ce pas à pas est fait pour vous.
⚡ À lire en premier : la limite de l’hébergement local
Une chose à savoir avant de commencer :
Postiz ne publie vos posts que pendant que votre machine tourne.
La planification est gérée par un worker en arrière-plan à l’intérieur de la stack Docker. Si vous programmez une publication pour 9h00 le mardi et que votre PC est en veille, éteint, ou que Docker ne tourne pas, la publication ne part pas. Elle partira en retard, au prochain démarrage de la stack.
Les réglages de veille de Windows sont le coupable habituel. Une machine qui se met en veille pendant la nuit ratera tout ce qui est programmé de nuit, donc vérifiez votre mode de gestion de l’alimentation si vos publications arrivent avec des heures de retard.
Cela fait d’une installation locale une excellente option pour :
- Essayer Postiz avant de s’engager sur un serveur
- Rédiger et organiser du contenu
- Le développement et les tests
…et une mauvaise option pour tenir un calendrier de publication de façon fiable. Si vous avez besoin que vos posts partent à l’heure, mettez Postiz sur un VPS ou utilisez la version hébergée. Tout ce qui suit s’applique aussi à un serveur, seul le problème du “garder la machine allumée” disparaît.
Si la planification est rédhibitoire, vous pouvez soit payer pour Postiz, soit l’héberger sur un VPS. Un VPS bon marché suffit ; j’ai détaillé quels hébergeurs valent vraiment le coup ici.
Ce qu’il vous faut
- Windows 10 ou 11
- Docker Desktop - https://docker.com/products/docker-desktop
- Git (pour Git Bash) - https://git-scm.com/download/win
- ~15 Go d’espace disque libre. La stack télécharge environ 9,5 Go d’images, plus la place pour les volumes et le reste. (Mesuré sur une installation, la taille des images évolue, considérez donc cela comme un ordre de grandeur.)
- 8 Go de RAM minimum ; la stack consomme environ 3-4 Go au repos. Notez que le backend WSL2 de Docker Desktop a sa propre limite mémoire par-dessus, donc 8 Go au total laissent très peu de marge. 16 Go est nettement plus confortable.
Les durées ci-dessous supposent une connexion raisonnablement rapide. C’est le téléchargement des images qui domine.
Étape 1 - Installer et démarrer Docker Desktop
Installez-le, puis lancez-le et attendez que l’icône de baleine dans la zone de notification arrête de s’animer. Le moteur Docker tourne dans une VM Linux qui met 30 à 60 secondes à démarrer. Toutes les commandes docker échouent tant qu’il n’est pas prêt.
Activez Settings → General → Start Docker Desktop when you log in. Postiz ne peut rien publier si Docker ne tourne pas, donc sur une installation locale cette option vous rend un vrai service.
Vérifiez dans Git Bash :
docker --version
N’importe quel numéro de version signifie que la CLI est joignable et que le moteur est démarré. Si vous obtenez bash: docker: command not found, voyez le Piège 1 plus bas.
Étape 2 - Récupérer le fichier compose
Choisissez l’endroit où vous rangez vos projets. Dans Git Bash, votre dossier utilisateur Windows est ~, donc ceci le place dans C:\Users\<vous>\projects\postiz :
mkdir -p ~/projects && cd ~/projects
git clone https://github.com/gitroomhq/postiz-docker-compose postiz
cd postiz
Il ne s’agit que de la configuration Docker Compose, l’application elle-même arrive sous forme d’images précompilées.
Le nom du dossier a son importance : Compose l’utilise comme préfixe pour ses volumes et ses réseaux. Clonez dans postiz et vous obtenez postiz_postgres-volume ; clonez dans my-postiz et ce sera my-postiz_postgres-volume. Les commandes ci-dessous supposent postiz.
Étape 3 - Définir un vrai secret JWT
docker-compose.yaml est livré avec une valeur d’exemple :
JWT_SECRET: 'random string that is unique to every install - just type random characters here!'
Cette chaîne signe vos jetons de connexion, et elle est identique dans toutes les copies du dépôt. Générez la vôtre :
openssl rand -base64 32
Collez le résultat à la place de la valeur d’exemple. Faites-le avant le premier lancement, la modifier plus tard invalide les sessions existantes.
Deux autres réglages à connaître, tous les deux près du début du fichier :
| Réglage | Valeur par défaut | Signification |
|---|---|---|
DISABLE_REGISTRATION | 'false' | Les inscriptions sont ouvertes. Nécessaire pour votre premier compte, fermez-les ensuite (Étape 6). |
MAIN_URL | http://localhost:4007 | À modifier uniquement si vous n’êtes pas sur localhost. |
Étape 4 - Lancer la stack
docker compose up -d
Le premier lancement télécharge environ 9,5 Go répartis sur 8 images, comptez 5 à 15 minutes. Les démarrages suivants prennent bien moins d’une minute.
-d la lance en mode détaché, en arrière-plan.
Étape 5 - Attendre qu’elle soit réellement prête
docker compose ps
Vous voulez voir tous les services affichés en (healthy) :
postiz Up (healthy)
postiz-postgres Up (healthy)
postiz-redis Up (healthy)
temporal Up (healthy)
temporal-postgresql Up (healthy)
temporal-elasticsearch Up (healthy)
temporal-ui Up (healthy)
temporal-admin-tools Up
Les services démarrent dans l’ordre des dépendances, Elasticsearch, puis Temporal, puis Postiz, et Postiz exécute les migrations de base de données au premier démarrage. Voir (health: starting) pendant deux ou trois minutes est normal.
Ouvrez ensuite :
Utilisez http://, pas https://. Rien ici ne termine le TLS, donc l’URL en https échoue tout simplement.
Inscrivez-vous, le premier compte est l’administrateur.
Étape 6 - Fermer les inscriptions
Une fois votre compte créé, empêchez quiconque d’en créer un autre. Dans docker-compose.yaml :
DISABLE_REGISTRATION: 'true'
Appliquez le changement :
docker compose up -d
Cela recrée uniquement le container modifié. Votre compte et vos données vivent dans des volumes nommés et ne sont pas touchés, vous restez connecté.
Étape 7 - Garder les secrets hors du contrôle de version
Optionnel, mais utile si vous comptez un jour committer cette configuration ou la copier sur un serveur.
Docker Compose lit automatiquement un fichier .env placé à côté de docker-compose.yaml. Déplacez-y les secrets :
.env
JWT_SECRET=your-generated-secret
POSTGRES_USER=postiz-user
POSTGRES_PASSWORD=postiz-password
POSTGRES_DB=postiz-db-local
docker-compose.yaml
JWT_SECRET: ${JWT_SECRET:?set JWT_SECRET in .env}
DATABASE_URL: 'postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postiz-postgres:5432/${POSTGRES_DB}'
.gitignore
.env
La syntaxe :? fait échouer Compose avec un message clair si la variable est absente, plutôt que de démarrer silencieusement avec un secret vide.
Vérifiez la substitution avant de redémarrer :
docker compose config | grep JWT_SECRET
Si .env est correctement configuré, cela affiche la valeur résolue. Si la variable est absente, docker compose config se termine sur une erreur au lieu d’afficher quoi que ce soit, le grep ne s’exécute jamais. Dans les deux cas vous obtenez l’information dont vous avez besoin :
error while interpolating services.postiz.environment.JWT_SECRET:
required variable JWT_SECRET is missing a value: set JWT_SECRET in .env
Sauvegardez .env quelque part en dehors du dépôt. Il est ignoré par git par conception, ce qui veut aussi dire qu’il n’est pas sauvegardé. Perdez le JWT_SECRET et toutes les sessions cassent.
Étape 8 - Connecter les réseaux sociaux
Postiz lit les identifiants des plateformes depuis l’environnement du container, pas depuis quoi que ce soit dans l’interface web. Il n’y a pas de champ dans le tableau de bord pour cela, vous les définissez dans la configuration puis vous redémarrez.
En reprenant le principe du .env de l’Étape 7, prenons X comme exemple :
.env
X_API_KEY=your-key-here
X_API_SECRET=your-secret-here
docker-compose.yaml
X_API_KEY: ${X_API_KEY:-}
X_API_SECRET: ${X_API_SECRET:-}
La valeur par défaut :- garde les valeurs vides valides, donc les plateformes que vous n’avez pas configurées restent indisponibles au lieu de casser le démarrage.
Faites ensuite docker compose up -d et connectez le canal dans l’interface.
La liste complète des plateformes supportées
Voici les noms de variables livrés avec le fichier compose. Ne branchez que celles dont vous avez besoin, le principe est identique pour chacune : ajoutez les variables au .env, passez le fichier compose en ${VAR:-}, redémarrez.
| Plateforme | Variables |
|---|---|
| X (Twitter) | X_API_KEY, X_API_SECRET |
FACEBOOK_APP_ID, FACEBOOK_APP_SECRET | |
| Instagram / Threads | THREADS_APP_ID, THREADS_APP_SECRET |
LINKEDIN_CLIENT_ID, LINKEDIN_CLIENT_SECRET | |
| TikTok | TIKTOK_CLIENT_ID, TIKTOK_CLIENT_SECRET |
| YouTube | YOUTUBE_CLIENT_ID, YOUTUBE_CLIENT_SECRET |
PINTEREST_CLIENT_ID, PINTEREST_CLIENT_SECRET | |
REDDIT_CLIENT_ID, REDDIT_CLIENT_SECRET | |
| Mastodon | MASTODON_CLIENT_ID, MASTODON_CLIENT_SECRET, MASTODON_URL |
| Discord | DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET, DISCORD_BOT_TOKEN_ID |
| Slack | SLACK_ID, SLACK_SECRET, SLACK_SIGNING_SECRET |
| Dribbble | DRIBBBLE_CLIENT_ID, DRIBBBLE_CLIENT_SECRET |
| GitHub | GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET |
| Beehiiv | BEEHIIVE_API_KEY, BEEHIIVE_PUBLICATION_ID |
MASTODON_URL vaut https://mastodon.social par défaut, changez-le si vous êtes sur une autre instance. Notez que BEEHIIVE_* s’écrit avec un “E” en plus dans le fichier compose, recopiez-le exactement.
Postiz supporte plus de plateformes que cela dans son interface. Si celle que vous voulez n’est pas listée, consultez la référence de configuration pour trouver ses noms de variables et ajoutez-les de la même façon.
Chaque plateforme nécessite une application enregistrée sur son portail développeur, et chacune nécessite une URL de callback autorisée, généralement http://localhost:4007/integrations/social/<platform>. Consultez docker compose logs postiz pour connaître l’URL exacte attendue par Postiz si une connexion échoue.
Commencez par une plateforme facile
Les plateformes diffèrent énormément par la quantité de travail qu’elles demandent, et cela vaut la peine de le savoir avant d’y passer un après-midi pour rien :
- Facile - Mastodon, Discord, Reddit, GitHub. Identifiants délivrés immédiatement, sans validation.
- Moyen - LinkedIn (nécessite une page entreprise), Pinterest, Slack.
- Difficile - Facebook, Instagram/Threads, TikTok, YouTube. Validation de l’application, restrictions de sandbox, ou vérification d’entreprise.
- Payant - X exige un niveau d’API payant pour l’accès en écriture. Le niveau gratuit est en lecture seule, donc la publication ne fonctionnera pas.
Si vous voulez simplement prouver que la chaîne fonctionne de bout en bout, Mastodon prend environ deux minutes : Préférences → Développement → Nouvelle application, dans n’importe quelle instance Mastodon.
Ces exigences changent fréquemment, considérez le classement ci-dessus comme un point de départ et vérifiez les conditions actuelles sur chaque portail.
Les commandes du quotidien
docker compose ps # status
docker compose logs -f postiz # follow logs, Ctrl+C to quit
docker compose stop # stop, keeps all data
docker compose up -d # start
docker compose restart postiz # restart just the app
Les données vivent dans des volumes nommés Docker et survivent à stop, up -d, restart et down.
docker compose down -v supprime les volumes, ce qui efface votre compte, vos publications et vos fichiers envoyés. C’est la seule commande avec laquelle il faut faire attention.
Dépannage et pièges courants
Piège 1 : bash: docker: command not found
Docker Desktop est installé, mais votre shell ne le trouve pas.
Vérifiez d’abord que Docker Desktop tourne réellement. Aucune correction de PATH n’y changera quoi que ce soit si le moteur est à l’arrêt.
S’il tourne, vérifiez si la CLI est dans votre PATH :
echo "$PATH" | tr ':' '\n' | grep -i docker
Aucune sortie signifie que le répertoire est absent. Trouvez le binaire :
ls "/c/Program Files/Docker/Docker/resources/bin/docker.exe"
ls "$LOCALAPPDATA/Programs/DockerDesktop/resources/bin/docker.exe"
Les versions récentes de Docker Desktop peuvent s’installer par utilisateur dans %LOCALAPPDATA%\Programs\DockerDesktop plutôt que dans C:\Program Files\Docker. Les guides qui supposent Program Files ne correspondront pas.
Ajoutez à ~/.bashrc celui des deux chemins qui existe. $HOME évite d’y écrire votre nom d’utilisateur et reste au format de chemin Unix, ce qui est ce que PATH attend :
echo 'export PATH="$PATH:$HOME/AppData/Local/Programs/DockerDesktop/resources/bin"' >> ~/.bashrc
source ~/.bashrc
(Si vous avez l’installation dans Program Files, utilisez /c/Program Files/Docker/Docker/resources/bin à la place.)
Évitez $LOCALAPPDATA ici. Il se développe en un chemin au format Windows avec des antislashs et une lettre de lecteur (C:\Users\you\AppData\Local), et un C: dans un PATH séparé par des deux-points, c’est chercher les ennuis. $HOME vaut déjà /c/Users/you dans Git Bash.
Une subtilité à connaître : même quand le répertoire est correctement enregistré dans votre PATH Windows, les terminaux héritent de leur environnement d’explorer.exe, qui peut en garder une copie obsolète datant d’avant l’installation. Ouvrir un nouveau terminal n’aide pas toujours, un redémarrage si. Modifier ~/.bashrc contourne tout le problème.
Notez que l’emplacement de votre projet sur le disque n’a aucune importance ici. PATH est une liste de répertoires absolus ; l’endroit d’où vous lancez la commande ne change rien.
Piège 2 : 502 Bad Gateway à la connexion ou à l’inscription
La page se charge, mais l’envoi du formulaire renvoie le 502 de nginx.
Cela veut dire que le frontend va bien et que le backend est mort.
Le container postiz n’est pas un seul processus, il en fait tourner plusieurs derrière une unique instance nginx :
| Processus | Port | Rôle |
|---|---|---|
| nginx | 5000 | porte d’entrée ; route tout le trafic |
| frontend (Next.js) | 4200 | l’interface que vous voyez |
| backend (NestJS) | 3000 | l’API, /api/* est proxifié ici |
| workers / cron | - | publication et tâches planifiées |
nginx sert l’interface sans broncher, que le backend soit vivant ou non. Donc la page s’affiche, puis chaque appel d’API, connexion, inscription, renvoie un 502.
Vérifiez si le backend écoute :
docker exec postiz ss -tln | grep 3000
Si ss n’est pas dans l’image, utilisez Node, qui est toujours présent :
docker exec postiz node -e "require('net').connect(3000,'127.0.0.1').on('connect',()=>{console.log('backend UP');process.exit(0)}).on('error',()=>{console.log('backend DOWN');process.exit(1)})"
- Une ligne
LISTEN(oubackend UP) → le backend va bien, cherchez ailleurs. - Aucune sortie (ou
backend DOWN) → le backend est à terre. Découvrez pourquoi :
docker compose logs postiz | grep -i "backend failed" -A 5
Piège 3 : “Tous les services sont healthy” mais l’application est cassée
C’est le piège qui rend le Piège 2 déroutant.
docker compose ps peut afficher chaque service en (healthy) alors que Postiz est fondamentalement cassé. Regardez ce que teste réellement le health check du fichier compose : il interroge http://localhost:5000/, c’est-à-dire nginx. Et nginx reste parfaitement healthy à servir le frontend même quand le backend derrière lui a planté.
Donc sur cette stack, (healthy) signifie “le serveur web tourne”, pas “l’application fonctionne”. Quand quelque chose déraille, le test du port 3000 ci-dessus est un signal bien plus fiable que la colonne de statut.
Piège 4 : ne supprimez pas de services pour économiser de la RAM
La stack fait tourner 8 containers et la partie Temporal a des airs de surdimensionnement pour une installation personnelle. Résistez à l’envie de l’élaguer.
temporal-elasticsearch en particulier a l’air optionnel et ne l’est pas. Elasticsearch est réellement optionnel pour Temporal en général, il alimente la “visibilité avancée”, et Temporal se rabat sur PostgreSQL sans lui. Mais Postiz enregistre des search attributes Temporal personnalisés au démarrage, et le store de visibilité PostgreSQL plafonne les attributs de type Text à trois. Postiz en a besoin de plus.
Supprimez Elasticsearch et le backend meurt pendant l’initialisation avec :
Unable to create search attributes: cannot have more than 3 search attribute of type Text.
…ce qui remonte jusqu’à vous sous la forme d’un 502 à la connexion, avec tous les services signalés comme healthy. Le fichier compose livre ces services pour une raison.
Si vous l’avez déjà supprimé, restaurez le service Elasticsearch d’origine, puis effacez l’état de Temporal pour qu’il se réinitialise proprement :
docker compose down
docker volume rm postiz_temporal-postgres-data
docker compose up -d
Cela supprime uniquement la base de données de Temporal. Votre compte Postiz et vos publications vivent dans postiz_postgres-volume et ne sont pas affectés.
Vérifiez d’abord le nom de vos volumes, Compose les préfixe avec le nom du répertoire, donc ils s’appellent postiz_* uniquement si vous avez cloné dans un dossier nommé postiz :
docker volume ls | grep temporal
Piège 5 : des timeouts /api/copilot/chat dans les logs
Sans gravité. C’est la fonctionnalité d’assistant IA qui échoue parce que OPENAI_API_KEY est vide. Ignorez-les, sauf si vous voulez du contenu de publication généré par IA, auquel cas fournissez une clé.
Faut-il vraiment auto-héberger en local ?
Postiz est la meilleure alternative auto-hébergée à Buffer qui existe. L’héberger en local est une très bonne façon d’évaluer Postiz, d’apprendre à connaître la stack et de rédiger du contenu sans rien payer. MAIS ce n’est pas une bonne façon de tenir un calendrier de publication.
Si la fiabilité de la planification compte, le même fichier compose tourne sur un petit VPS : vous changeriez MAIN_URL, FRONTEND_URL et NEXT_PUBLIC_BACKEND_URL pour votre domaine et vous mettriez un reverse proxy avec TLS devant. Tout le reste se transpose sans modification.
Un VPS bon marché suffit ; j’ai détaillé séparément quels hébergeurs valent vraiment la peine qu’on s’y inscrive, ainsi que les pièges de tarification au renouvellement à éviter.
NE faites PAS confiance aux sites d’avis. Les commissions d’affiliation dictent leurs classements. Ceci est aussi un site d’affiliation, mais je suis honnête sur ce que je gagne et je classe par qualité plutôt que par rémunération. Même si cela signifie que je suis payé 0 $. Lisez mon approche et pourquoi j’ai arrêté de raconter des conneries. Voici les données brutes pour que vous puissiez tout vérifier.
VPN | Hébergement | Cloud | Outils