[{"data":1,"prerenderedAt":177},["ShallowReactive",2],{"seo-verification":3,"blog-desplegar-aplicacion-strapi-en-un-vps-es":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-desplegar-aplicacion-strapi-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":121,"ctaBody":122,"ctaButton":123,"ctaUrl":124,"relatedPosts":125},54,"desplegar-aplicacion-strapi-en-un-vps",{"fr":12,"en":13,"ar":14,"es":10},"deployer-strapi-vps","deploying-a-strapi-application-on-a-vps","نشر-تطبيق-strapi-على-خادم-vps","Desplegar Strapi en un VPS: la guía completa","Despliegue Strapi en un VPS con Docker, PostgreSQL y Nginx: Dockerfile multietapa, secretos, S3, copias pg_dump y solución de errores OOM.",12,1,false,"2026-04-27T00: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-strapi-vps-poster.svg","Strapi es el CMS headless de código abierto de referencia en Node.js. Autoalojarlo en un VPS le da la propiedad total de sus contenidos, de su base de datos y de sus archivos subidos — sin cuota de suscripción, sin dependencia de Strapi Cloud y sin plataforma de terceros entre sus equipos y sus datos. Esta guía cubre la totalidad del despliegue: generación del proyecto, Dockerfile multietapa, docker-compose con PostgreSQL, secretos de producción, Nginx, HTTPS, almacenamiento S3, copias de seguridad automatizadas y actualizaciones sin roturas.",[35,39,51,54,70,88,91,109,112,115,118],{"type":36,"title":37,"body":38},"h2","Strapi Cloud o autoalojamiento: lo que pierde, lo que gana","Strapi Cloud es práctico para empezar, pero impone restricciones que se vuelven rápidamente bloqueantes en producción real. El plan Free limita los tipos de contenido y los usuarios administradores; los planes de pago facturan según el número de peticiones a la API y de archivos subidos. Sobre todo, sus datos residen en la infraestructura de Strapi Inc. — algo incompatible con los requisitos de localización de datos de muchos clientes (RGPD estricto, contratos sectoriales, datos sensibles). Al autoalojar en un VPS, recupera la propiedad completa de la base PostgreSQL, la elección del almacenamiento (disco local o bucket compatible con S3), la libertad de instalar cualquier plugin de la comunidad y volúmenes de contenido que ya no dependen de una tabla de precios. La contrapartida es operativa — usted gestiona las actualizaciones, las copias de seguridad y la monitorización. Esta guía le muestra exactamente cómo hacerlo.",{"type":40,"title":41,"items":42},"ul","Ventajas de autoalojar Strapi en un VPS",[43,44,45,46,47,48,49,50],"**Propiedad de los datos** — su base PostgreSQL y sus archivos subidos permanecen en su VPS, bajo su control exclusivo","**Ningún cupo de plan** — el número de entradas, de tipos de contenido y de cuentas de administrador ya no lo fija una suscripción, sino los recursos de su VPS","**Plugins de la comunidad** — cualquier plugin npm se instala libremente, sin validación ni lista blanca de la plataforma","**API en su dominio** — REST y GraphQL expuestos en `cms.mondomaine.com`, sin intermediario ni límite de tasa impuesto","**Pila unificada** — Strapi y un frontend Nuxt o Next.js conviven en el mismo VPS, detrás de un único reverse proxy","**Localización de los datos** — esencial para el cumplimiento del RGPD y los requisitos contractuales de sus clientes","**Coste previsible** — el VPS tiene una tarifa fija mensual, independiente del volumen de contenido o del tráfico de la API","**Control de las migraciones** — usted elige cuándo y cómo aplicar las actualizaciones de Strapi",{"type":36,"title":52,"body":53},"Requisitos previos: hardware, software y dominio","Strapi consume más recursos que la mayoría de los frameworks porque el **build del admin React consume por sí solo más de 2 GB de RAM**. Un VPS de 1 GB de RAM fallará sistemáticamente por OOM durante la fase de build — no lo infradimensione. Objetivos mínimos: **2 vCPU, 4 GB de RAM**. En producción estable, una vez terminado el build, la aplicación funciona con holgura con 512 MB a 1 GB. En el plano del software: Docker 24+ y Docker Compose v2 (`docker compose`, no `docker-compose`), Git para recuperar su código en el servidor, y Certbot para el TLS. En cuanto a la base de datos: se recomienda PostgreSQL 16 en producción — SQLite es funcional en desarrollo, pero demasiado limitado bajo carga concurrente e incompatible con algunos plugins. En cuanto a la red: apunte `cms.mondomaine.com` a la IP de su VPS antes de empezar, y prevea 20 GB de disco como mínimo (node_modules, archivos subidos, volcados de copia de seguridad).",{"type":55,"title":56,"steps":57},"steps","Preparar el proyecto antes de tocar el servidor",[58,61,64,67],{"title":59,"body":60},"Generar el proyecto Strapi","En su equipo, genere el proyecto con el comando oficial `npx create-strapi-app@latest mon-cms --dbclient=postgres`. Elija TypeScript si su equipo lo domina. Inicialice el repositorio Git, súbalo a su forja y luego clónelo en el VPS en `\u002Fsrv\u002Fmon-cms`. Todo lo que sigue se prepara en ese repositorio y no en el servidor: el VPS solo debe recibir código ya versionado.",{"title":62,"body":63},"Conectar la configuración de base de datos a PostgreSQL","En `config\u002Fdatabase.js` — o `config\u002Fdatabase.ts` en TypeScript —, la función exportada recibe `env` y devuelve un objeto `connection`. Declare `client: 'postgres'` y, a continuación, seis claves leídas desde el entorno en el subobjeto `connection`: `host: env('DATABASE_HOST', '127.0.0.1')`, `port: env.int('DATABASE_PORT', 5432)`, `database: env('DATABASE_NAME', 'strapi')`, `user: env('DATABASE_USERNAME', 'strapi')`, `password: env('DATABASE_PASSWORD', '')` y `ssl: env.bool('DATABASE_SSL', false)`. El segundo argumento de `env()` no es más que un valor de reserva para el desarrollo: ningún valor real debe figurar en este archivo, que acaba en Git.",{"title":65,"body":66},"Generar los cinco secretos de producción","Strapi se niega a arrancar en producción sin `APP_KEYS`, `API_TOKEN_SALT`, `ADMIN_JWT_SECRET`, `JWT_SECRET` y `TRANSFER_TOKEN_SALT`. Genere cada uno con `openssl rand -base64 32`; `APP_KEYS` espera varias, separadas por comas, así que produzca al menos dos. Escríbalos en `\u002Fsrv\u002Fmon-cms\u002F.env` en el VPS — nunca en Git — junto con `DATABASE_HOST`, `DATABASE_NAME`, `DATABASE_USERNAME`, `DATABASE_PASSWORD`, `NODE_ENV=production` y `URL=https:\u002F\u002Fcms.mondomaine.com`. Una clave que falte se traduce en un rechazo de arranque, no en una advertencia.",{"title":68,"body":69},"Escribir el Dockerfile multietapa","Dos etapas bastan para mantener ligera la imagen de producción. La etapa `builder` parte de `node:20-alpine`, fija `WORKDIR \u002Fapp`, copia `package*.json`, lanza `npm ci`, copia el resto del código y luego compila el admin con `NODE_ENV=production npm run build`. La etapa `runner` parte de la misma imagen base y solo copia de `builder` tres cosas: la carpeta de build, `node_modules` y `package.json`. Termine con `EXPOSE 1337` y un `CMD` que llame a `npm run start` — es el script de arranque que Strapi instala, no intente lanzar un archivo de servidor a mano. Si el build muere por OOM, ponga `NODE_OPTIONS=--max-old-space-size=4096` antes de `npm run build`.",{"type":55,"title":71,"steps":72},"Desplegar la pila en el VPS",[73,76,79,82,85],{"title":74,"body":75},"Describir los servicios en docker-compose.yml","El archivo declara dos servicios y un volumen con nombre. El servicio `postgres` utiliza la imagen `postgres:16-alpine`, recibe `POSTGRES_DB`, `POSTGRES_USER` y `POSTGRES_PASSWORD` en su bloque `environment`, monta el volumen `pgdata` en `\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata` y pasa a `restart: unless-stopped`. El servicio `strapi` se construye desde el Dockerfile local con `build: .`, lee sus variables mediante `env_file: .env`, declara `depends_on: postgres`, monta `.\u002Fpublic\u002Fuploads` en `\u002Fapp\u002Fpublic\u002Fuploads` y publica su puerto con `ports: 127.0.0.1:1337:1337`. Esta vinculación al bucle local es el punto importante: Nginx se convierte en el único punto de entrada público, el puerto 1337 nunca se expone al exterior. Un servicio `redis` sigue siendo opcional, para la caché de sesiones o las colas de procesamiento.",{"title":77,"body":78},"Construir la imagen y arrancar la pila","Lance `docker compose build` y luego `docker compose up -d`. Siga el arranque con `docker compose logs -f strapi`. Strapi aplica sus migraciones de esquema en el primer arranque en cuanto `NODE_ENV=production` está puesto. Cuente de dos a cinco minutos: la construcción del admin React es con diferencia la etapa más lenta. Espere la línea `Strapi started successfully` antes de continuar.",{"title":80,"body":81},"Poner Nginx como reverse proxy","Cree el vhost `\u002Fetc\u002Fnginx\u002Fsites-available\u002Fcms.mondomaine.com`. Escucha en `listen 80` sobre `server_name cms.mondomaine.com`, lleva un `client_max_body_size 50M` — imprescindible para las subidas de medios — y un único bloque `location \u002F` que hace `proxy_pass http:\u002F\u002F127.0.0.1:1337`. Añádale las cuatro cabeceras que Strapi espera detrás de un proxy: `Host`, `X-Real-IP`, `X-Forwarded-For` y `X-Forwarded-Proto`, cada una puesta por una directiva `proxy_set_header`. Active el vhost con un enlace simbólico hacia `sites-enabled`, y luego valide y recargue con `nginx -t && systemctl reload nginx`.",{"title":83,"body":84},"Activar HTTPS y fijar la variable URL","Obtenga el certificado con `certbot --nginx -d cms.mondomaine.com`: Certbot reescribe el vhost para redirigir HTTP hacia HTTPS. Compruebe después que `URL=https:\u002F\u002Fcms.mondomaine.com` figura correctamente en el `.env`, sin barra final. Es esta variable la que Strapi utiliza para construir los enlaces de los medios y las redirecciones del admin; sin ella, los archivos subidos salen con direcciones falsas y el panel de administración se comporta mal detrás del proxy. Reinicie el contenedor tras cualquier modificación del `.env`: las variables se leen al arrancar.",{"title":86,"body":87},"Comprobar que la instancia responde de verdad","Abra `https:\u002F\u002Fcms.mondomaine.com\u002Fadmin` y cree la primera cuenta de administrador — Strapi la pide en el primer acceso y no volverá a pedirla. Controle después tres puntos: `docker compose ps` muestra los dos contenedores en estado `running`, la API pública responde en `\u002Fapi`, y el envío de un archivo de prueba desde la mediateca funciona. Un `502 Bad Gateway` en esta fase indica casi siempre un contenedor `strapi` detenido o todavía en construcción: relea `docker compose logs strapi` antes de tocar Nginx.",{"type":36,"title":89,"body":90},"Sacar los medios del disco: el provider S3","Por defecto, Strapi guarda los archivos subidos en `public\u002Fuploads`, en el disco del VPS. El montaje `.\u002Fpublic\u002Fuploads` del docker-compose hace que sobrevivan a una recreación de contenedor, pero crecen con la mediateca, entran en cada copia de seguridad y desaparecen con el servidor. El provider oficial resuelve los tres problemas: `npm install @strapi\u002Fprovider-upload-aws-s3`. Declárelo después en `config\u002Fplugins.js`, bajo la clave `upload` y luego `config`: `provider: 'aws-s3'` y un objeto `providerOptions` que lee cuatro valores del entorno — `accessKeyId: env('AWS_ACCESS_KEY_ID')`, `secretAccessKey: env('AWS_ACCESS_SECRET')`, `region: env('AWS_REGION')` y `params: { Bucket: env('AWS_BUCKET') }`. Cualquier almacenamiento compatible con S3 sirve: Scaleway Object Storage, Wasabi, Cloudflare R2. Dos trampas merecen conocerse antes del cambio. Los medios ya subidos no se migran solos — cambie el provider antes de la puesta en línea, o copie a mano el contenido de `public\u002Fuploads` al bucket. Y las URLs de los medios cambian de dominio: si su frontend las almacena en caché o las reescribe, verifíquelo tras el cambio.",{"type":55,"title":92,"steps":93},"Automatizar la copia de seguridad de la base",[94,97,100,103,106],{"title":95,"body":96},"Crear la carpeta de destino","En el VPS, `mkdir -p \u002Fsrv\u002Fbackups\u002Fstrapi`. Mantenga esta carpeta fuera del repositorio Git y fuera de cualquier ruta montada en un contenedor: un volcado nunca debe acabar en una imagen ni en una release.",{"title":98,"body":99},"Localizar el nombre real del contenedor PostgreSQL","`docker compose ps` da el nombre exacto, de la forma `mon-cms-postgres-1`. Deriva del nombre de la carpeta del proyecto: no lo copie de una guía, léalo en su máquina. Un script de copia de seguridad que apunta a un contenedor inexistente falla en silencio una vez colocado en el cron.",{"title":101,"body":102},"Escribir el comando de volcado","Basta con una sola línea: `docker exec mon-cms-postgres-1 pg_dump -U strapi strapi | gzip > \u002Fsrv\u002Fbackups\u002Fstrapi\u002Fstrapi-$(date +%Y%m%d).sql.gz`. El `pg_dump` se ejecuta en el contenedor; la compresión y la escritura se hacen en el host. Láncelo una primera vez a mano y compruebe el tamaño del archivo producido: un volcado de unos pocos bytes indica un error de autenticación tragado por la tubería.",{"title":104,"body":105},"Purgar los volcados demasiado antiguos","Añada a continuación `find \u002Fsrv\u002Fbackups\u002Fstrapi -name '*.sql.gz' -mtime +7 -delete`. Sin esta línea, el disco se llena en unas semanas: es la avería más banal de una copia de seguridad diaria — la base cae porque la copia ha saturado el volumen.",{"title":107,"body":108},"Planificar y luego sacar los volcados del servidor","Ponga los dos comandos en un script `\u002Fetc\u002Fcron.daily\u002Fstrapi-backup`, con `#!\u002Fbin\u002Fbash` en la primera línea y un `chmod +x` para hacerlo ejecutable. Añádale un `rsync` hacia un almacenamiento externo, o un envío al mismo bucket S3 que los medios. Una copia de seguridad que se queda en la máquina que respalda no protege de nada; pruebe una restauración completa al menos una vez antes de depender de ella.",{"type":36,"title":110,"body":111},"Actualizar Strapi sin romper las migraciones","Una actualización se pilota desde el repositorio, no en el servidor: cambie la versión en `package.json`, suba los cambios y luego, en el VPS, encadene `git pull` y `docker compose build && docker compose up -d`. Strapi detecta y aplica las migraciones de esquema al arrancar mientras `NODE_ENV=production` esté puesto; confírmelo con `docker compose logs strapi | grep -i migrat`. Tres precauciones merecen el rodeo. Tome un `pg_dump` justo antes de la subida de versión, y no solo el de la noche anterior — una migración fallida se repara con una restauración, nunca con un segundo intento. Lea las notas de versión de los plugins que tenga instalados: una subida mayor de Strapi rompe más a menudo un plugin de la comunidad que el propio núcleo. Por último, si una migración falla, no relance la pila en bucle: cada reinicio repite la misma migración sobre una base ya modificada a medias. Detenga los contenedores, lea el registro completo, restaure el volcado si es necesario y luego corrija.",{"type":36,"title":113,"body":114},"Solución de problemas: los errores más frecuentes","**OOM durante el build (`Killed` o `JavaScript heap out of memory`)**: el build del admin React supera la memoria disponible. Dos remedios — añadir `NODE_OPTIONS=--max-old-space-size=4096` en la etapa `builder` del Dockerfile, o aumentar temporalmente el swap del VPS con `fallocate -l 2G \u002Fswapfile`, `chmod 600 \u002Fswapfile`, `mkswap \u002Fswapfile` y luego `swapon \u002Fswapfile`. Si el VPS sigue bloqueado, construya la imagen en una máquina más potente y súbala a un registro.\n\n**`Cannot find module @strapi\u002Fplugin-*`**: nunca comparta la carpeta `node_modules` entre un entorno de desarrollo local y la imagen de producción mediante un volumen Docker. El `node_modules` local está compilado para su sistema operativo, no para el Alpine Linux del contenedor. Elimine todo volumen `node_modules` del docker-compose y deje que el `npm ci` del Dockerfile se encargue.\n\n**`URL mismatch` en el admin, o medios con ruta relativa**: la variable `URL` del `.env` debe corresponder exactamente a la dirección pública HTTPS de su Strapi, sin barra final. Cualquier divergencia rompe los enlaces de los medios subidos y provoca errores CORS en el panel de administración.\n\n**`413 Request Entity Too Large`**: la directiva `client_max_body_size` de Nginx es demasiado baja. Súbala a `50M` como mínimo, o a `100M` si sube vídeos, y luego recargue Nginx.\n\n**`password authentication failed for user` al arrancar**: la contraseña del `.env` cambió después de la creación del volumen `pgdata`. PostgreSQL solo relee su contraseña al inicializar el volumen; alinee el `.env` con la contraseña existente, o parta de un volumen nuevo tras haber respaldado la base.",{"type":116,"body":117},"tip","Tres reglas no negociables para una instancia Strapi en producción: **S3 para los archivos subidos** (`@strapi\u002Fprovider-upload-aws-s3`) — sus medios sobreviven a cualquier recreación de contenedor y no engordan el disco del VPS; **`pg_dump` diario automatizado** copiado fuera del servidor — una copia de seguridad solo local desaparece con el VPS; **`NODE_ENV=production` imperativamente** — el modo desarrollo recompila el admin en caliente, expone el Content-Type Builder y desactiva las optimizaciones de caché, algo que hay que proscribir en producción.",{"type":36,"title":119,"body":120},"Strapi y un frontend Nuxt o Next.js en el mismo VPS","Es perfectamente posible hacer convivir Strapi y un frontend en el mismo VPS, siempre que haya suficiente RAM — cuente 8 GB para los dos, ya que ambos builds pueden dispararse al mismo tiempo. La arquitectura más simple utiliza Nginx como despachador: las peticiones hacia `cms.mondomaine.com` se proxian hacia el puerto `1337` (Strapi), y las dirigidas a `mondomaine.com` hacia el puerto `3000` (Nuxt o Next.js). Strapi expone su API REST en `\u002Fapi` — el frontend la consume directamente en la red Docker interna, sin pasar por Nginx, lo que reduce la latencia. Si su frontend genera páginas estáticas (`nuxt generate`, o `next build` en exportación estática), Nginx puede servir los archivos desde el disco y proxiar solo las rutas dinámicas. Esta arquitectura monolítica es ideal para un proyecto de tamaño medio — un solo servidor, un solo certificado TLS, un solo punto de supervisión. Si prefiere separar el frontend, nuestras guías de despliegue de Nuxt y Node.js retoman el mismo método en un segundo VPS Cloud dimensionado para el build.","Aloje su CMS Strapi con total autonomía","El VPS Cloud de ServOrbit aporta la RAM necesaria para el build del admin de Strapi y un entorno Docker + PostgreSQL listo para usar, para conservar la plena propiedad de sus contenidos y de sus datos.","Descubrir el VPS Cloud","\u002Fvps-cloud",[126,143,161],{"id":127,"slug":128,"slugs":129,"title":133,"excerpt":134,"readTime":23,"views":135,"isPinned":19,"publishedAt":136,"updatedAt":21,"category":137,"categories":138,"featuredImage":30,"bgImage":31,"posterImage":140,"relatedSolution":141},5,"desplegar-laravel-en-un-vps",{"fr":130,"en":131,"ar":132,"es":128},"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},[139],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fdeployer-laravel-vps-poster.svg",{"categorySlug":27,"appSlug":142},"laravel-stack",{"id":144,"slug":145,"slugs":146,"title":150,"excerpt":151,"readTime":152,"views":135,"isPinned":19,"publishedAt":153,"updatedAt":21,"category":154,"categories":155,"featuredImage":30,"bgImage":31,"posterImage":157,"relatedSolution":158},43,"desplegar-una-aplicacion-nodejs-en-un-vps",{"fr":147,"en":148,"ar":149,"es":145},"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},[156],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fdeployer-nodejs-vps-poster.svg",{"categorySlug":159,"appSlug":160},"desarrollo","nodejs-stack",{"id":162,"slug":163,"slugs":164,"title":168,"excerpt":169,"readTime":152,"views":127,"isPinned":19,"publishedAt":170,"updatedAt":21,"category":171,"categories":172,"featuredImage":30,"bgImage":31,"posterImage":174,"relatedSolution":175},44,"desplegar-una-aplicacion-django-en-un-vps",{"fr":165,"en":166,"ar":167,"es":163},"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},[173],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fdeployer-django-vps-poster.svg",{"categorySlug":159,"appSlug":176},"django",1789665015069]