Cómo instalar Postiz en Windows: configuración con Docker y solución de problemas
Tabla de Contenidos
Postiz es el enormemente popular programador de redes sociales de código abierto, una alternativa auto-alojada a Buffer o Hootsuite con más de 33.000 estrellas en GitHub. Esta guía repasa cómo instalarlo en Windows con Docker Desktop.
Si eres como yo y te pareció que la documentación oficial de Postiz es demasiado vaga, este paso a paso es para ti.
⚡ Lee esto primero: la advertencia sobre el alojamiento local
Una cosa que debes saber antes de empezar:
Postiz solo publica mientras tu máquina está encendida.
La programación la gestiona un worker en segundo plano dentro del stack de Docker. Si programas una publicación para el martes a las 9:00 y tu PC está suspendida, apagada o Docker no se está ejecutando, la publicación no sale. Se dispara tarde, cuando el stack vuelve a arrancar.
La configuración de suspensión de Windows suele ser la culpable. Una máquina que se suspende durante la noche perderá todo lo programado esa noche, así que revisa tu plan de energía si las publicaciones salen con horas de retraso.
Eso hace que una instalación local sea ideal para:
- Probar Postiz antes de comprometerte con un servidor
- Redactar y organizar contenido
- Desarrollo y pruebas
…y una mala opción para cumplir un calendario de publicación de forma fiable. Si necesitas que las publicaciones salgan a su hora, ponlo en un VPS o usa la versión alojada. Todo lo de abajo también se aplica a un servidor, solo desaparece el problema de “mantener la máquina encendida”.
Si la programación es un factor decisivo, puedes pagar por Postiz o alojarlo en un VPS. Un VPS barato es suficiente; he escrito sobre qué hosts valen realmente la pena aquí.
Lo que necesitas
- Windows 10 u 11
- Docker Desktop - https://docker.com/products/docker-desktop
- Git (para Git Bash) - https://git-scm.com/download/win
- ~15 GB de espacio libre en disco. El stack descarga unos 9,5 GB de imágenes, más espacio para volumes y sobrecarga. (Medido en una instalación; los tamaños de imagen cambian, así que tómalo como una aproximación.)
- 8 GB de RAM como mínimo; el stack consume unos 3-4 GB en reposo. Ten en cuenta que el backend WSL2 de Docker Desktop tiene su propio techo de memoria por encima de eso, así que 8 GB en total dejan muy poco margen. Con 16 GB se va mucho más cómodo.
Los tiempos que se indican asumen una conexión razonablemente rápida. La descarga de imágenes es lo que domina.
Paso 1 - Instalar e iniciar Docker Desktop
Instálalo y luego lánzalo y espera a que el icono de la ballena en la bandeja del sistema deje de animarse. El motor de Docker se ejecuta en una VM Linux que tarda entre 30 y 60 segundos en arrancar. Todos los comandos docker fallan hasta que esté levantado.
Activa Settings → General → Start Docker Desktop when you log in. Postiz no puede publicar nada si Docker no se está ejecutando, así que en una instalación local esta opción te está haciendo un trabajo real.
Verifica en Git Bash:
docker --version
Cualquier número de versión significa que la CLI es accesible y el motor está levantado. Si obtienes bash: docker: command not found, consulta la Trampa 1 más abajo.
Paso 2 - Obtener el archivo compose
Elige donde sea que guardes tus proyectos. En Git Bash, tu carpeta de usuario de Windows es ~, así que esto lo deja en C:\Users\<you>\projects\postiz:
mkdir -p ~/projects && cd ~/projects
git clone https://github.com/gitroomhq/postiz-docker-compose postiz
cd postiz
Esto es solo la configuración de Docker Compose; la aplicación en sí llega como imágenes precompiladas.
El nombre de la carpeta importa un poco: Compose antepone ese nombre a sus volumes y redes. Si clonas en postiz obtienes postiz_postgres-volume; si clonas en my-postiz será my-postiz_postgres-volume. Los comandos de abajo asumen postiz.
Paso 3 - Establecer un JWT secret real
docker-compose.yaml viene con un valor de marcador:
JWT_SECRET: 'random string that is unique to every install - just type random characters here!'
Esa cadena firma tus tokens de inicio de sesión, y es idéntica en cada copia del repositorio. Genera la tuya:
openssl rand -base64 32
Pega el resultado en lugar del marcador. Haz esto antes del primer arranque; cambiarlo después invalida las sesiones existentes.
Otros dos ajustes que conviene conocer, ambos cerca del inicio del archivo:
| Ajuste | Valor por defecto | Significado |
|---|---|---|
DISABLE_REGISTRATION | 'false' | El registro está abierto. Necesario para tu primera cuenta; ciérralo después (Paso 6). |
MAIN_URL | http://localhost:4007 | Cámbialo solo si no estás en localhost. |
Paso 4 - Arrancarlo
docker compose up -d
La primera ejecución descarga aproximadamente 9,5 GB repartidos en 8 imágenes, así que cuenta con 5-15 minutos. Los arranques siguientes tardan bastante menos de un minuto.
-d lo ejecuta en modo detached, en segundo plano.
Paso 5 - Espera a que esté realmente listo
docker compose ps
Quieres ver todos los servicios 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
Los servicios arrancan en orden de dependencia (Elasticsearch, luego Temporal, luego Postiz) y Postiz ejecuta las migraciones de base de datos en el primer arranque. Ver (health: starting) durante dos o tres minutos es normal.
Después abre:
Usa http://, no https://. Aquí nada termina TLS, así que la URL con https simplemente falla.
Regístrate: la primera cuenta es la de administrador.
Paso 6 - Cerrar el registro
Una vez que exista tu cuenta, impide que cualquier otra persona cree una. En docker-compose.yaml:
DISABLE_REGISTRATION: 'true'
Aplícalo:
docker compose up -d
Eso recrea únicamente el container que cambió. Tu cuenta y tus datos viven en volumes con nombre y quedan intactos, así que sigues con la sesión iniciada.
Paso 7 - Mantener los secretos fuera del control de versiones
Opcional, pero vale la pena si alguna vez vas a subir esta configuración a un repositorio o copiarla a un servidor.
Docker Compose lee automáticamente un archivo .env situado junto a docker-compose.yaml. Mueve los secretos allí:
.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 sintaxis :? hace que Compose falle con un mensaje claro si falta la variable, en lugar de arrancar en silencio con un secreto vacío.
Comprueba la sustitución antes de reiniciar:
docker compose config | grep JWT_SECRET
Si .env está bien configurado, esto imprime el valor resuelto. Si falta la variable, docker compose config sale con un error en lugar de imprimir nada, y el grep nunca llega a ejecutarse. Cualquiera de los dos resultados te dice lo que necesitas saber:
error while interpolating services.postiz.environment.JWT_SECRET:
required variable JWT_SECRET is missing a value: set JWT_SECRET in .env
Haz una copia de seguridad de .env en algún sitio fuera del repositorio. Está ignorado por git a propósito, lo que también significa que no se respalda. Si pierdes el JWT_SECRET, todas las sesiones se rompen.
Paso 8 - Conectar canales sociales
Postiz lee las credenciales de las plataformas desde el entorno del container, no desde nada de la interfaz web. No hay ningún campo en el panel para esto: se configuran en los archivos de configuración y se reinicia.
Usando el patrón .env del Paso 7, tomemos X como ejemplo:
.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:-}
El valor por defecto :- mantiene válidos los valores vacíos, de modo que las plataformas que no hayas configurado simplemente quedan no disponibles en lugar de romper el arranque.
Después ejecuta docker compose up -d y conecta el canal desde la interfaz.
La lista completa de plataformas soportadas
Estos son los nombres de variables que trae el archivo compose. Configura solo las que necesites; el patrón es idéntico en todos los casos: añade las variables a .env, cambia el archivo compose a ${VAR:-} y reinicia.
| Plataforma | 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 apunta por defecto a https://mastodon.social; cámbialo si estás en otra instancia. Fíjate en que BEEHIIVE_* se escribe con la “E” de más en el archivo compose; cópialo exactamente así.
Postiz soporta más plataformas que estas en su interfaz. Si la que quieres no aparece en la lista, consulta la referencia de configuración para ver sus nombres de variables y añádelas de la misma forma.
Cada plataforma necesita una app registrada en su portal de desarrolladores, y cada una necesita una URL de callback en la lista blanca, normalmente http://localhost:4007/integrations/social/<platform>. Revisa docker compose logs postiz para ver la URL exacta que espera Postiz si una conexión falla.
Empieza por una fácil
Las plataformas varían enormemente en el trabajo que exigen, y conviene saberlo antes de perder una tarde entera con la equivocada:
- Fáciles - Mastodon, Discord, Reddit, GitHub. Credenciales emitidas al instante, sin revisión.
- Moderadas - LinkedIn (necesita una página de empresa), Pinterest, Slack.
- Difíciles - Facebook, Instagram/Threads, TikTok, YouTube. Revisión de la app, restricciones de sandbox o verificación de empresa.
- De pago - X requiere un plan de API de pago para acceso de escritura. El plan gratuito es de solo lectura, así que publicar no funcionará.
Si solo quieres comprobar que el flujo funciona de principio a fin, Mastodon lleva unos dos minutos: Preferences → Development → New application en cualquier instancia de Mastodon.
Estos requisitos cambian con frecuencia; toma la agrupación anterior como punto de partida y consulta las condiciones actuales en cada portal.
Comandos del día a día
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
Los datos viven en volumes con nombre de Docker y sobreviven a stop, up -d, restart y down.
docker compose down -v elimina los volumes, lo que borra tu cuenta, tus publicaciones y tus archivos subidos. Es el único comando con el que hay que tener cuidado.
Solución de problemas y trampas habituales
Trampa 1: bash: docker: command not found
Docker Desktop está instalado, pero tu shell no lo encuentra.
Primero, comprueba que Docker Desktop se esté ejecutando de verdad. Ningún arreglo del PATH sirve de nada si el motor está caído.
Si sí se está ejecutando, comprueba si la CLI está en tu PATH:
echo "$PATH" | tr ':' '\n' | grep -i docker
Si no hay salida, es que falta el directorio. Localiza el binario:
ls "/c/Program Files/Docker/Docker/resources/bin/docker.exe"
ls "$LOCALAPPDATA/Programs/DockerDesktop/resources/bin/docker.exe"
Las versiones recientes de Docker Desktop pueden instalarse por usuario en %LOCALAPPDATA%\Programs\DockerDesktop en lugar de en C:\Program Files\Docker. Las guías que dan por hecho Program Files no te van a cuadrar.
Añade a ~/.bashrc la ruta que exista en tu caso. $HOME mantiene tu nombre de usuario fuera del archivo y conserva el formato de ruta estilo Unix, que es lo que PATH espera:
echo 'export PATH="$PATH:$HOME/AppData/Local/Programs/DockerDesktop/resources/bin"' >> ~/.bashrc
source ~/.bashrc
(Si la tuya es la instalación en Program Files, usa /c/Program Files/Docker/Docker/resources/bin en su lugar.)
Evita $LOCALAPPDATA aquí. Se expande a una ruta estilo Windows con barras invertidas y letra de unidad (C:\Users\you\AppData\Local), y una C: dentro de un PATH separado por dos puntos es buscarse problemas. En Git Bash, $HOME ya es /c/Users/you.
Un detalle que conviene conocer: incluso cuando el directorio sí está correctamente registrado en tu PATH de Windows, las terminales heredan su entorno de explorer.exe, que puede tener una copia obsoleta de antes de la instalación. Abrir una terminal nueva no siempre ayuda; reiniciar sí. Editar ~/.bashrc esquiva el problema por completo.
Ten en cuenta que la ubicación de tu proyecto en el disco es irrelevante aquí. PATH es una lista de directorios absolutos; desde dónde ejecutes el comando no importa.
Trampa 2: 502 Bad Gateway al iniciar sesión o registrarse
La página carga, pero al enviar el formulario devuelve el 502 de nginx.
Esto significa que el frontend está bien y el backend está muerto.
El container postiz no es un único proceso: ejecuta varios detrás de una sola instancia de nginx:
| Proceso | Puerto | Función |
|---|---|---|
| nginx | 5000 | puerta de entrada; enruta todo |
| frontend (Next.js) | 4200 | la interfaz que ves |
| backend (NestJS) | 3000 | la API; /api/* va por aquí |
| workers / cron | - | publicación y trabajos programados |
nginx sirve la interfaz tan tranquilo, esté vivo o no el backend. Así que la página se renderiza y luego cada llamada a la API (login, registro) devuelve 502.
Comprueba si el backend está escuchando:
docker exec postiz ss -tln | grep 3000
Si ss no está en la imagen, usa Node, que siempre está presente:
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)})"
- Una línea
LISTEN(obackend UP) → el backend está bien; busca en otra parte. - Sin salida (o
backend DOWN) → el backend está caído. Averigua por qué:
docker compose logs postiz | grep -i "backend failed" -A 5
Trampa 3: “Todos los servicios healthy” pero la aplicación no funciona
Esta es la trampa que hace que la Trampa 2 sea confusa.
docker compose ps puede informar de que todos los servicios están (healthy) mientras Postiz está roto de raíz. Mira qué comprueba realmente el health check del archivo compose: hace una petición a http://localhost:5000/, que es nginx. Y nginx sigue perfectamente sano sirviendo el frontend incluso cuando el backend que tiene detrás se ha caído.
Así que en este stack, (healthy) significa “el servidor web está levantado”, no “la aplicación funciona”. Cuando algo se comporte raro, la comprobación del puerto 3000 de arriba es una señal mucho mejor que la columna de estado.
Trampa 4: No elimines servicios para ahorrar RAM
El stack ejecuta 8 containers y la parte de Temporal parece excesiva para una instalación personal. Resiste la tentación de recortarla.
temporal-elasticsearch en particular parece opcional y no lo es. Elasticsearch es genuinamente opcional para Temporal en general: da soporte a la “visibilidad avanzada”, y Temporal recurre a PostgreSQL sin él. Pero Postiz registra search attributes personalizados de Temporal al arrancar, y el almacén de visibilidad de PostgreSQL limita los atributos de tipo Text a tres. Postiz necesita más.
Si eliminas Elasticsearch, el backend muere durante la inicialización con:
Unable to create search attributes: cannot have more than 3 search attribute of type Text.
…lo que a ti te aparece como un 502 al iniciar sesión, con todos los servicios reportando healthy. El archivo compose incluye estos servicios por algo.
Si ya lo eliminaste, restaura el servicio original de Elasticsearch y luego borra el estado de Temporal para que se reinicialice de forma consistente:
docker compose down
docker volume rm postiz_temporal-postgres-data
docker compose up -d
Eso elimina únicamente la base de datos de Temporal. Tu cuenta de Postiz y tus publicaciones viven en postiz_postgres-volume y no se ven afectadas.
Comprueba primero los nombres de tus volumes, ya que Compose les antepone el nombre del directorio, así que serán postiz_* solo si clonaste en una carpeta llamada postiz:
docker volume ls | grep temporal
Trampa 5: timeouts de /api/copilot/chat en los logs
Inofensivo. Es la función de asistente de IA fallando porque OPENAI_API_KEY está vacía. Ignóralo, a menos que quieras contenido de publicaciones generado por IA, en cuyo caso proporciona una clave.
¿Deberías auto-alojarlo localmente?
Postiz es la mejor alternativa auto-alojada a Buffer que existe. Alojarlo en local es una forma genuinamente buena de evaluar Postiz, aprender el stack y redactar contenido sin pagar nada. PERO no es una buena forma de cumplir un calendario de publicación.
Si la fiabilidad de la programación importa, el mismo archivo compose funciona en un VPS pequeño: cambiarías MAIN_URL, FRONTEND_URL y NEXT_PUBLIC_BACKEND_URL a tu dominio y pondrías delante un proxy inverso con TLS. Todo lo demás se traslada sin cambios.
Un VPS barato es suficiente; he escrito aparte sobre en qué hosts vale realmente la pena registrarse, junto con las trampas de precios de renovación que conviene evitar.
NO confíes en los sitios de reseñas. Las comisiones de afiliados dictan sus clasificaciones. Este también es un sitio de afiliados, pero soy honesto sobre lo que gano y clasifico por calidad en lugar de por pago. Incluso si eso significa que me paguen $0. Lee sobre mi enfoque y por qué dejé de mentir. Aquí están los datos en bruto para que puedas verificar todo.
VPNs | Hosting | Nube | Herramientas