[{"data":1,"prerenderedAt":201},["ShallowReactive",2],{"seo-verification":3,"blog-desplegar-payload-cms-en-un-vps-es":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-desplegar-payload-cms-en-un-vps-es",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"publishedAt":20,"updatedAt":21,"category":22,"categories":28,"featuredImage":30,"bgImage":31,"posterImage":32,"relatedSolution":30,"intro":33,"sections":34,"ctaTitle":145,"ctaBody":146,"ctaButton":147,"ctaUrl":148,"relatedPosts":149},55,"desplegar-payload-cms-en-un-vps",{"fr":12,"en":13,"ar":14,"es":10},"deployer-payload-cms-vps","deploying-a-payload-cms-application-on-a-vps","نشر-تطبيق-payload-cms-على-خادم-vps","Desplegar Payload CMS en un VPS: guía completa","Guía completa para auto-alojar Payload CMS 3 en un VPS: Docker Compose, auth, hooks, migraciones y resolución de errores reales en producción.",10,1,false,"2026-04-26T00:00:00+00:00","2026-09-07T11:26:10+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},4,"Desarrollo","developpement","bg-warning\u002F10 text-warning","dev",[29],{"id":23,"name":24,"slug":25,"color":26,"icon":27},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fdeployer-payload-cms-vps-poster.svg","Payload CMS 3 se instala directamente dentro de una aplicación Next.js, lo que lo convierte en uno de los CMS headless más próximos al código. Esta guía cubre el despliegue completo en un VPS: configuración de Docker Compose con healthchecks, gestión de usuarios y API keys, hooks de colección, flujo de trabajo de migraciones y resolución de los errores más frecuentes en producción.",[35,39,50,53,75,78,81,84,87,90,142],{"type":36,"title":37,"body":38},"h2","Por qué auto-alojar Payload CMS en un VPS","Payload CMS adopta un enfoque radicalmente code-first: el esquema de contenido se define en TypeScript en archivos de configuración versionados con Git, y desde la versión 3 Payload se instala directamente en una aplicación Next.js mediante el App Router. Esto significa que un VPS permite alojar el CMS y el front en un único proceso Node, compartiendo el mismo build y runtime. Se obtiene tipado de extremo a extremo, migraciones de esquema gestionadas en el código y ninguna dependencia de una GUI para modelar los contenidos. El auto-alojamiento es la opción natural: Payload está diseñado para desplegarse como cualquier app Next.js, y un VPS da el control total sobre la base de datos (MongoDB o PostgreSQL), las subidas y las variables de entorno, sin plataforma intermediaria.",{"type":40,"title":41,"items":42},"ul","Beneficios concretos",[43,44,45,46,47,48,49],"Esquema de contenido definido en TypeScript y versionado con Git: revisión de código e historial completos","CMS y front Next.js en un único runtime: un solo build, un solo proceso a desplegar","Tipado de extremo a extremo entre la config de Payload, la API y el front, sin generación manual","Elección de base de datos: MongoDB o PostgreSQL mediante adaptadores oficiales","Migraciones de esquema pilotadas por código (`payload migrate`), reproducibles entre entornos","Local API: acceso directo a los datos sin llamadas HTTP desde el código de servidor Next.js","Sin coste de licencia: Payload CMS 3 es open-source (MIT) — solo pagas el VPS",{"type":36,"title":51,"body":52},"Requisitos de hardware y software","Como Payload funciona sobre Next.js, el build es exigente: planifica un VPS con 2 vCPU y 4 GB de RAM para construir y ejecutar la aplicación con comodidad. Instala Node.js 20 LTS, Docker y Docker Compose. Para la base de datos, aprovisiona MongoDB 7 o PostgreSQL 16 según el adaptador elegido (`@payloadcms\u002Fdb-mongodb` o `@payloadcms\u002Fdb-postgres`). Apunta tu dominio a la IP del VPS. Reserva 15 GB de disco para `node_modules`, el build `.next`, las subidas y las copias de seguridad de la base de datos.",{"type":54,"title":55,"steps":56},"steps","Despliegue paso a paso",[57,60,63,66,69,72],{"title":58,"body":59},"Elegir y configurar el adaptador de base de datos","En `payload.config.ts`, declara el adaptador: `mongooseAdapter` para MongoDB o `postgresAdapter` para PostgreSQL, leyendo la URL de conexión desde `DATABASE_URI`. Esta elección es estructural: PostgreSQL exige migraciones, MongoDB es más flexible con el esquema.",{"title":61,"body":62},"Preparar las variables de entorno","Crea un archivo `.env.production` con como mínimo:\n\n```bash\nPAYLOAD_SECRET=tu-secreto-de-32-caracteres-minimo\nDATABASE_URI=postgresql:\u002F\u002Fuser:pass@postgres:5432\u002Fpayload\nNEXT_PUBLIC_SERVER_URL=https:\u002F\u002Fexample.com\nNODE_ENV=production\nPAYLOAD_CONFIG_PATH=src\u002Fpayload.config.ts\n```\n\n`PAYLOAD_SECRET` cifra los tokens JWT y las cookies de sesión — un valor corto o predecible debilita todo el sistema de autenticación. Genéralo con `openssl rand -hex 32`. `NEXT_PUBLIC_SERVER_URL` debe coincidir con la URL pública final: Payload lo usa para construir los enlaces de los medios y las URLs de correo.",{"title":64,"body":65},"Escribir Docker Compose con healthchecks","Un `docker-compose.yml` robusto incluye healthchecks para que la app solo arranque tras la disponibilidad de la base de datos:\n\n```bash\nservices:\n  app:\n    build: .\n    ports:\n      - \"127.0.0.1:3000:3000\"\n    env_file: .env.production\n    depends_on:\n      postgres:\n        condition: service_healthy\n    volumes:\n      - uploads:\u002Fapp\u002Fpublic\u002Fmedia\n\n  postgres:\n    image: postgres:16-alpine\n    environment:\n      POSTGRES_USER: payload\n      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}\n      POSTGRES_DB: payload\n    volumes:\n      - pgdata:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -U payload\"]\n      interval: 5s\n      timeout: 5s\n      retries: 10\n\nvolumes:\n  pgdata:\n  uploads:\n```\n\nSin el healthcheck y la condición `service_healthy`, la app arranca antes de que PostgreSQL acepte conexiones, el pool de conexiones falla y el contenedor reinicia en bucle.",{"title":67,"body":68},"Construir la imagen y ejecutar las migraciones","Construye y ejecuta las migraciones en este orden:\n\n```bash\ndocker compose build\ndocker compose up -d postgres\ndocker compose run --rm app npx payload migrate\ndocker compose up -d app\n```\n\nPara PostgreSQL, `npx payload migrate` aplica los archivos de migración generados en `src\u002Fmigrations\u002F`. Si no existen todavía, genéralos primero con `npx payload migrate:create`. Para MongoDB, el esquema se aplica automáticamente en el primer arranque.",{"title":70,"body":71},"Configurar Nginx como reverse proxy","Apunta un vhost a `http:\u002F\u002F127.0.0.1:3000` (puerto por defecto de Next.js). Aumenta `client_max_body_size` para las subidas de medios y añade las cabeceras `X-Forwarded-Proto` para que Payload genere URLs HTTPS correctas.",{"title":73,"body":74},"Asegurar con SSL y fijar la URL del servidor","Ejecuta `certbot --nginx -d example.com`, luego verifica que `serverURL: 'https:\u002F\u002Fexample.com'` esté definido en `payload.config.ts`. Esta URL rige los enlaces de los medios, los correos de restablecimiento de contraseña y el correcto funcionamiento del panel de admin detrás del proxy.",{"type":36,"title":76,"body":77},"Autenticación y gestión de accesos","Payload 3 gestiona los usuarios mediante la colección `users` definida en `payload.config.ts`. Activa la autenticación en la colección con `auth: true` — esto añade automáticamente los endpoints `\u002Fapi\u002Fusers\u002Flogin`, `\u002Fapi\u002Fusers\u002Flogout`, `\u002Fapi\u002Fusers\u002Fme` y `\u002Fapi\u002Fusers\u002Frefresh-token`.\n\nPara los roles, Payload no impone un modelo: define un campo `role` (tipo `select`) en la colección `users`, luego controla el acceso mediante funciones `access` a nivel de colección y operación:\n\n```bash\naccess: {\n  read: ({ req: { user } }) => user?.role === 'admin',\n  create: isAdmin,\n  update: isAdminOrSelf,\n  delete: isAdmin,\n}\n```\n\nPara el acceso programático (CI, integraciones de terceros), usa las **API keys**: activa `useAPIKey: true` en la configuración de autenticación de la colección. Cada usuario puede generar una clave desde el panel de admin. Pásala en la cabecera `Authorization: users API-Key tu-clave`. Las API keys se hashean en la base de datos — una clave perdida no se recupera, se regenera.",{"type":36,"title":79,"body":80},"Hooks de colección: reaccionar a eventos de contenido","Los **collection hooks** de Payload 3 permiten ejecutar código antes o después de cada operación CRUD. Reemplazan elegantemente a los webhooks cuando la lógica vive en el mismo runtime:\n\n```bash\nhooks: {\n  afterChange: [\n    async ({ doc, operation }) => {\n      if (operation === 'create') {\n        await notifySubscribers(doc)\n      }\n    },\n  ],\n  beforeDelete: [\n    async ({ id }) => {\n      await cleanupMedia(id)\n    },\n  ],\n}\n```\n\nLos hooks disponibles son `beforeOperation`, `beforeValidate`, `beforeChange`, `afterChange`, `beforeRead`, `afterRead`, `beforeDelete` y `afterDelete`. Un hook `afterChange` es ideal para invalidar una caché CDN, enviar una notificación o sincronizar con un servicio externo tras la publicación.\n\nPara **webhooks HTTP salientes** (notificar a un sitio estático, Slack o un pipeline de build), Payload no dispone de módulo dedicado pero los hooks `afterChange` bastan: un simple `fetch` al endpoint objetivo dentro del hook cubre el caso de uso.",{"type":36,"title":82,"body":83},"Actualizaciones y migraciones de esquema","Payload 3 sigue el versionado semántico. Las actualizaciones de patch (`3.x.y → 3.x.z`) son seguras sin migraciones. Las actualizaciones minor (`3.x → 3.y`) pueden añadir columnas o índices y requieren ejecutar `payload migrate`.\n\nFlujo de trabajo recomendado para cada actualización:\n\n```bash\n# 1. Actualizar la versión en package.json\nnpm install payload@3.88.0 @payloadcms\u002Fdb-postgres@3.88.0\n\n# 2. Generar archivo de migración si el esquema cambió\nnpx payload migrate:create\n\n# 3. Probar localmente sobre una copia de la base de datos\nnpx payload migrate\n\n# 4. Confirmar el archivo de migración con el bump de versión\ngit add src\u002Fmigrations\u002F package.json package-lock.json\ngit commit -m \"chore: payload 3.88.0\"\n\n# 5. En producción: detener app, aplicar migraciones, reiniciar\ndocker compose run --rm app npx payload migrate\ndocker compose up -d app\n```\n\nNunca edites manualmente los archivos generados en `src\u002Fmigrations\u002F`: Payload los verifica por hash. Un archivo modificado hace fallar `migrate` con `Error: Migration file has been modified since it was created`. Para deshacer una migración: `npx payload migrate:down`.",{"type":36,"title":85,"body":86},"Cuando `sharp` se niega a cargar","Es el obstáculo más común al desplegar en un VPS, y se presenta en tres formas distintas. La más desconcertante es `Unsupported CPU: prebuilt binaries for linux-x64 require v2 microarchitecture`: no viene de tu código ni de tus dependencias, sino del **procesador de la máquina**. Los binarios precompilados apuntan a un conjunto de instrucciones que las CPU más antiguas no exponen — es un criterio de selección de VPS, no un bug. La segunda, `Could not load the \"sharp\" module using the linux-x64 runtime`, casi siempre indica un `node_modules` construido en otra plataforma — reinstala en la máquina objetivo. La tercera es propia de Docker multi-stage: `sharp` instalado en la etapa de construcción pero ausente de la etapa de ejecución — instálalo explícitamente en la etapa final.",{"type":36,"title":88,"body":89},"Resolución de errores frecuentes en producción","**Build OOM — `Killed` o `JavaScript heap out of memory`.**\nOcurre durante `npm run build` cuando la RAM disponible es inferior a 3 GB. Node.js está limitado a ~1.8 GB por defecto. Establece `NODE_OPTIONS=--max-old-space-size=3072` en el entorno antes del build, o construye la imagen en una máquina más potente y súbela a un registry.\n\n**PostgreSQL — `Error: connect ECONNREFUSED 127.0.0.1:5432`.**\nLa app intenta conectarse antes de que PostgreSQL esté listo, o la URL de conexión apunta a `localhost` en lugar del nombre del servicio Docker (`postgres`). Verifica que `DATABASE_URI` use el nombre del servicio y que el healthcheck esté configurado (ver paso 3).\n\n**Admin inaccesible en prod — `\u002Fadmin` devuelve 404 o redirección en bucle.**\nDos causas principales: `serverURL` no definido o no coincidente con la URL real, o la cabecera `X-Forwarded-Proto: https` ausente del proxy Nginx. Añade `proxy_set_header X-Forwarded-Proto $scheme;` al vhost y verifica que `serverURL` coincida exactamente con el origen público, sin barra diagonal final.\n\n**CORS — `Access-Control-Allow-Origin` ausente en la API.**\nPayload lee `cors` desde `payload.config.ts`. En producción, lista explícitamente los orígenes autorizados: `cors: { origins: ['https:\u002F\u002Fexample.com'] }`. `cors: { origins: [serverURL] }` es la configuración mínima segura.\n\n**`Error: Migration file has been modified since it was created`.**\nUn archivo en `src\u002Fmigrations\u002F` fue editado manualmente. Restaura el original desde Git, genera un nuevo archivo de migración si el esquema cambió, y vuelve a aplicar en orden.",{"type":91,"title":92,"headers":93,"rows":97},"comparison","Payload CMS vs Strapi: ¿qué CMS headless auto-alojar?",[94,95,96],"Criterio","Payload CMS","Strapi",[98,102,106,110,114,118,122,126,130,134,138],[99,100,101],"Enfoque de configuración","Code-first en TypeScript, versionado con Git","GUI-first mediante Content-Type Builder",[103,104,105],"Integración front","Nativa en Next.js (un solo runtime)","Desacoplado, front y CMS separados",[107,108,109],"Bases soportadas","MongoDB y PostgreSQL","PostgreSQL, MySQL, SQLite",[111,112,113],"Tipado","TypeScript de extremo a extremo nativo","Tipos generados, integración menos ajustada",[115,116,117],"Acceso a datos en servidor","Local API sin llamadas HTTP","API REST\u002FGraphQL por HTTP",[119,120,121],"Modelado de contenido","En el código, por desarrolladores","En la interfaz, accesible a no-devs",[123,124,125],"RAM necesaria para el build","Alta (build Next.js)","Alta (build React admin)",[127,128,129],"Gestión de migraciones","Archivos versionados, `payload migrate`","Migraciones automáticas via Strapi CLI",[131,132,133],"API keys nativas","Sí, por colección con hash","Sí, mediante tokens de API",[135,136,137],"Hooks de colección","Nativos, tipados en TypeScript","Lifecycle hooks via module middleware",[139,140,141],"Ideal para","Equipos dev, proyectos Next.js tipados","Equipos mixtos, modelado visual",{"type":143,"body":144},"tip","Aprovecha la Local API de Payload en tus componentes de servidor Next.js: en lugar de llamar a tu propia API mediante `fetch`, importa `getPayload` y consulta la base de datos directamente (`payload.find({ collection: 'posts' })`). Eliminas un viaje HTTP y ganas tanto en latencia como en seguridad. Para los medios, conecta el plugin `@payloadcms\u002Fstorage-s3` para almacenar las subidas fuera del VPS, y automatiza un volcado diario de la base de datos mediante cron para garantizar copias de seguridad externas. Por último, activa el flujo de trabajo de **borrador\u002Fprevisualización** nativo de Payload para ofrecer a los editores un paso de previsualización antes de publicar, sin ningún plugin de terceros.","Despliegue Payload CMS en un stack unificado","El VPS Cloud de ServOrbit ofrece la potencia necesaria para el build Next.js de Payload y un entorno Docker listo para MongoDB o PostgreSQL, para alojar su CMS code-first y su front en la misma máquina.","Descubrir el VPS Cloud","\u002Fvps-cloud",[150,167,185],{"id":151,"slug":152,"slugs":153,"title":157,"excerpt":158,"readTime":23,"views":159,"isPinned":19,"publishedAt":160,"updatedAt":21,"category":161,"categories":162,"featuredImage":30,"bgImage":31,"posterImage":164,"relatedSolution":165},5,"desplegar-laravel-en-un-vps",{"fr":154,"en":155,"ar":156,"es":152},"deployer-laravel-vps","deploying-laravel-on-a-vps-a-production-guide","نشر-laravel-على-خادم-vps-دليل-الإنتاج","Desplegar Laravel en un VPS: guía de producción","Ponga Laravel en producción en un VPS: PHP, workers, caché, base de datos, reverse proxy y HTTPS bien configurados.",0,"2026-02-09T00:00:00+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[163],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fdeployer-laravel-vps-poster.svg",{"categorySlug":27,"appSlug":166},"laravel-stack",{"id":168,"slug":169,"slugs":170,"title":174,"excerpt":175,"readTime":176,"views":159,"isPinned":19,"publishedAt":177,"updatedAt":21,"category":178,"categories":179,"featuredImage":30,"bgImage":31,"posterImage":181,"relatedSolution":182},43,"desplegar-una-aplicacion-nodejs-en-un-vps",{"fr":171,"en":172,"ar":173,"es":169},"deployer-nodejs-vps","deploy-a-nodejs-application-on-a-vps","نشر-تطبيق-nodejs-على-vps","Desplegar una aplicación Node.js en un VPS","Despliegue una aplicación Node.js en producción en un VPS: PM2, reverse proxy Nginx, SSL Let's Encrypt y arranque automático con el sistema.",3,"2026-05-08T00:00:00+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[180],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fdeployer-nodejs-vps-poster.svg",{"categorySlug":183,"appSlug":184},"desarrollo","nodejs-stack",{"id":186,"slug":187,"slugs":188,"title":192,"excerpt":193,"readTime":176,"views":151,"isPinned":19,"publishedAt":194,"updatedAt":21,"category":195,"categories":196,"featuredImage":30,"bgImage":31,"posterImage":198,"relatedSolution":199},44,"desplegar-una-aplicacion-django-en-un-vps",{"fr":189,"en":190,"ar":191,"es":187},"deployer-django-vps","deploy-a-django-application-on-a-vps","نشر-تطبيق-django-على-vps","Desplegar una aplicación Django en un VPS","Despliegue Django en un VPS: Gunicorn, Nginx, PostgreSQL, Docker y SSL. Guía completa para desarrolladores Python que quieren autoalojar.","2026-05-07T00:00:00+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[197],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fdeployer-django-vps-poster.svg",{"categorySlug":183,"appSlug":200},"django",1789665016430]