Strapi es un CMS headless de código abierto escrito en Node.js: gestionas el contenido desde un panel de administración y lo sirves a cualquier frontend (web, app móvil) a través de una API REST o GraphQL. En este tutorial instalarás Strapi 5 en Ubuntu 24.04 con Node.js 22 y PostgreSQL, lo ejecutarás en modo producción como servicio systemd y lo publicarás detrás de Nginx con un certificado HTTPS de Let's Encrypt.

Requisitos previos

Para seguir esta guía necesitas:

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 2 GB de RAM. La compilación del panel de administración consume bastante memoria; con 1 GB necesitarás swap.
  • Un usuario no root con privilegios sudo.
  • Un dominio con un registro DNS A que apunte a la IP del servidor. En esta guía se usa tu_dominio.
  • UFW permitiendo SSH, si tienes el cortafuegos activado.

Paso 1: Instalar Node.js 22

Strapi 5 necesita una versión LTS activa de Node.js. Los repositorios de Ubuntu 24.04 traen Node.js 18, que ya no está soportado, así que instala Node.js 22 desde el repositorio oficial de NodeSource.

Descarga la clave del repositorio en /etc/apt/keyrings:

sudo apt update
sudo apt install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg

Añade el repositorio de Node.js 22 e instala el paquete:

echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
sudo apt update
sudo apt install nodejs

Comprueba las versiones:

node -v
npm -v
v22.x.x
10.x.x

Paso 2: Instalar PostgreSQL y crear la base de datos

Strapi funciona con SQLite por defecto, pero para producción conviene PostgreSQL. Instálalo desde los repositorios de Ubuntu (versión 16):

sudo apt install postgresql

Crea un usuario de base de datos llamado strapi. El comando te pedirá la contraseña dos veces; elige una contraseña segura y guárdala, la usarás en el paso siguiente:

sudo -u postgres createuser --pwprompt strapi

Crea la base de datos con strapi como propietario:

sudo -u postgres createdb -O strapi strapi

Comprueba que puedes conectarte con ese usuario por TCP, que es como lo hará Strapi:

psql -h 127.0.0.1 -U strapi -d strapi -c 'SELECT current_user;'
 current_user
--------------
 strapi
(1 row)

Paso 3: Crear el proyecto de Strapi

Crea un usuario de sistema sin privilegios para la aplicación y cambia a él:

sudo useradd -m -s /bin/bash strapi
sudo -iu strapi

Genera el proyecto con create-strapi. Las opciones indican que no quieres usar Strapi Cloud, que la base de datos es PostgreSQL y que no arranque el servidor al terminar. Sustituye tu_contraseña_db por la contraseña del paso 2:

npx create-strapi@latest cms --skip-cloud --use-npm --no-run \
  --dbclient=postgres --dbhost=127.0.0.1 --dbport=5432 \
  --dbname=strapi --dbusername=strapi --dbpassword=tu_contraseña_db

El asistente hará algunas preguntas que no cubren las opciones, como si quieres datos de ejemplo, TypeScript o inicializar un repositorio Git. Para esta guía, responde No a los datos de ejemplo; lo demás a tu gusto. Al terminar tendrás el proyecto en /home/strapi/cms, con las dependencias instaladas y un archivo .env con claves aleatorias (APP_KEYS, ADMIN_JWT_SECRET, JWT_SECRET, etc.) y los datos de conexión a la base de datos.

Comprueba el .env:

cd ~/cms
grep -E '^(HOST|PORT|DATABASE_CLIENT|DATABASE_NAME)=' .env
HOST=0.0.0.0
PORT=1337
DATABASE_CLIENT=postgres
DATABASE_NAME=strapi

Paso 4: Configurar Strapi para producción detrás de un proxy

Strapi solo tiene que escuchar en local, porque Nginx recibirá las peticiones públicas. Cambia HOST en el .env y añade la URL pública:

nano ~/cms/.env
HOST=127.0.0.1
PORT=1337
PUBLIC_URL=https://tu_dominio

Ahora indica a Strapi su URL pública y que confíe en las cabeceras del proxy. Edita el archivo de configuración del servidor (config/server.ts si elegiste TypeScript, config/server.js si elegiste JavaScript):

nano ~/cms/config/server.ts
export default ({ env }) => ({
  host: env('HOST', '0.0.0.0'),
  port: env.int('PORT', 1337),
  url: env('PUBLIC_URL', 'http://localhost:1337'),
  proxy: { koa: true },
  app: {
    keys: env.array('APP_KEYS'),
  },
});

En la versión JavaScript el contenido es el mismo, con module.exports = en lugar de export default.

Compila el panel de administración en modo producción. Tarda uno o dos minutos:

NODE_ENV=production npm run build

Cuando termine, vuelve a tu usuario con sudo:

exit

Paso 5: Ejecutar Strapi con systemd

Una unidad systemd arranca Strapi con el sistema, lo reinicia si falla y envía sus logs al journal. Crea el archivo:

sudo nano /etc/systemd/system/strapi.service
[Unit]
Description=Strapi CMS
After=network.target postgresql.service
Requires=postgresql.service

[Service]
Type=simple
User=strapi
Group=strapi
WorkingDirectory=/home/strapi/cms
Environment=NODE_ENV=production
ExecStart=/usr/bin/npm run start
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Arranca el servicio y actívalo al inicio:

sudo systemctl daemon-reload
sudo systemctl enable --now strapi
sudo systemctl status strapi --no-pager

Strapi expone un endpoint de salud que responde 204 No Content cuando está listo:

curl -I http://127.0.0.1:1337/_health
HTTP/1.1 204 No Content

Si no responde, revisa el log con sudo journalctl -u strapi -n 50.

Paso 6: Configurar Nginx y HTTPS

Instala Nginx y Certbot con su plugin para Nginx:

sudo apt install nginx certbot python3-certbot-nginx

Crea un bloque de servidor para Strapi:

sudo nano /etc/nginx/sites-available/strapi
server {
    listen 80;
    listen [::]:80;
    server_name tu_dominio;

    client_max_body_size 50M;

    location / {
        proxy_pass http://127.0.0.1:1337;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

client_max_body_size permite subir archivos de hasta 50 MB a la biblioteca de medios; Nginx limita a 1 MB por defecto. Activa el sitio, comprueba la sintaxis y recarga:

sudo ln -s /etc/nginx/sites-available/strapi /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Abre HTTP y HTTPS en el cortafuegos y solicita el certificado. Certbot modifica el bloque de servidor para usar HTTPS y redirigir HTTP:

sudo ufw allow 'Nginx Full'
sudo certbot --nginx -d tu_dominio

Comprueba que el sitio responde por HTTPS:

curl -I https://tu_dominio/_health
HTTP/2 204

Paso 7: Crear el administrador y probar la API

Abre https://tu_dominio/admin en el navegador. La primera vez Strapi muestra un formulario para crear el primer usuario administrador; hazlo cuanto antes, porque cualquiera que llegue a esa URL antes que tú podría registrarlo.

Para acceder a la API desde un frontend o un script, crea un token en Settings > API Tokens > Create new API Token, con tipo Read-only. Copia el token, porque solo se muestra una vez, y úsalo en la cabecera Authorization. Por ejemplo, si tu proyecto tiene un tipo de colección article:

curl -H "Authorization: Bearer tu_token_api" https://tu_dominio/api/articles
{"data":[...],"meta":{"pagination":{"page":1,"pageSize":25,"pageCount":1,"total":3}}}

Si prefieres que un tipo de contenido sea público sin token, concede los permisos find y findOne al rol Public en Settings > Users & Permissions plugin > Roles.

Paso 8: Copias de seguridad

Lo que necesitas guardar es la base de datos, los archivos subidos (/home/strapi/cms/public/uploads) y el .env. Haz un volcado de la base de datos con pg_dump como el usuario postgres:

sudo mkdir -p /var/backups/strapi
sudo -u postgres pg_dump -Fc strapi | sudo tee /var/backups/strapi/strapi-$(date +%F).dump > /dev/null
sudo tar -czf /var/backups/strapi/uploads-$(date +%F).tar.gz -C /home/strapi/cms public/uploads .env
ls -lh /var/backups/strapi

Programa esos comandos con cron o un temporizador systemd y copia el directorio fuera del servidor. Para restaurar la base de datos usa pg_restore -d strapi.

Solución de problemas

  • npm run build termina con "JavaScript heap out of memory" o el proceso muere. El servidor no tiene RAM suficiente para compilar el panel. Añade un archivo de swap de 2 GB o amplía la RAM del servidor y repite la compilación.
  • El servicio falla con errores de conexión a la base de datos. Revisa DATABASE_HOST, DATABASE_USERNAME y DATABASE_PASSWORD en el .env y repite la prueba con psql del paso 2.
  • El panel carga pero las URLs apuntan a localhost o a HTTP. Falta PUBLIC_URL en el .env o url en la configuración del servidor. Corrígelo y vuelve a ejecutar npm run build antes de reiniciar.
  • Nginx devuelve 502 Bad Gateway. Strapi no está escuchando en 127.0.0.1:1337. Comprueba sudo systemctl status strapi y el journal.
  • Error 413 al subir archivos. Aumenta client_max_body_size en el bloque de Nginx y recárgalo.

Conclusión

Ya tienes Strapi 5 en producción en Ubuntu 24.04, con PostgreSQL, gestionado por systemd y publicado por HTTPS detrás de Nginx. Como siguientes pasos, puedes definir tus tipos de contenido en local y desplegarlos al servidor, instalar el plugin de GraphQL si tu frontend lo prefiere, o mover la biblioteca de medios a un almacenamiento compatible con S3 mediante un proveedor de subida.