Cómo desplegar una app de Next.js en AWS EC2 (Nginx + Ubuntu)

Appluex·15 de octubre de 2023·10 min de lectura
Desarrollo de softwareWeb
Desplegar una app de Next.js en AWS EC2 con Nginx y Ubuntu
Publicar Next.js en tu propia máquina EC2. Nginx, Ubuntu, PM2 y SSL, de principio a fin.

Vercel hace que desplegar Next.js sea casi trivial, y para muchos equipos esa es la decisión correcta. Pero si quieres control total sobre el servidor, tu propia red, tus propios costos, todo viviendo dentro de una sola cuenta de AWS, vale la pena saber desplegar en una instancia EC2 común. Este es el camino completo: Node, una llave de despliegue de GitHub, un build de producción mantenido vivo por PM2, Nginx como proxy inverso y un certificado SSL gratuito al final para que el tráfico viaje cifrado.

Un recordatorio rápido de por qué Next.js justifica el esfuerzo: te da renderizado del lado del servidor y generación de sitios estáticos de fábrica, así que obtienes HTML generado en el servidor, cargas más rápidas y una arquitectura amigable con los buscadores sin tener que armarla tú.

Esta guía asume que ya abriste una conexión SSH segura a tu instancia EC2. Si todavía no la configuras, nuestra guía de SSH con PuTTY lo cubre. Una vez conectado, sigue los pasos de abajo.

Paso 1: tu punto de partida

Esta es más o menos la pantalla que deberías estar viendo antes de empezar. Una terminal limpia en la instancia.

Sesión inicial de terminal en la instancia EC2
Una sesión SSH recién abierta en la instancia EC2.

Paso 2: actualiza los paquetes y agrega la llave de NodeSource

Primero, refresca el índice de paquetes del sistema:

sudo apt-get update
Ejecutar apt-get update en Ubuntu
Actualizando los paquetes del sistema.

Después descarga e importa la llave GPG de NodeSource para que apt confíe en el repositorio de Node:

sudo apt-get install -y ca-certificates curl gnupg
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg

Paso 3: agrega el repositorio de Node e instala Node.js

Crea un repositorio deb fijado a la versión de Node que quieras, que se define con NODE_MAJOR=#version. Elige una línea LTS activa: al momento de escribir esto son la 22 (con soporte hasta abril de 2027) o la 24(hasta abril de 2028). Las versiones 16, 18, 20 y 21 ya llegaron al fin de su vida útil y no reciben parches de seguridad, así que conviene evitarlas en un servidor que vas a dejar corriendo. Aquí usaremos la 22.

NODE_MAJOR=22   # instala una línea LTS activa
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list

Vuelve a actualizar apt para que tome el nuevo repositorio y luego instala Node:

sudo apt-get update          # refresca las listas de paquetes
sudo apt-get install nodejs -y   # instala la versión de Node definida arriba
Instalar Node.js desde el repositorio de NodeSource
Instalando Node.js.

Si alguna vez necesitas quitar Node.js por completo, esto deshace todo lo anterior:

sudo apt-get purge nodejs && \
rm -r /etc/apt/sources.list.d/nodesource.list && \
rm -r /etc/apt/keyrings/nodesource.gpg

Confirma la versión que acabas de instalar:

Verificar la versión instalada de Node.js
Verificando la versión de Node.js.

Paso 4: crea un par de llaves SSH

Vas a necesitar un par de llaves SSH para que la instancia se comunique de forma segura con tu repositorio de GitHub. En sistemas UNIX y similares, ssh-keygen hace el trabajo. La bandera -t elige el tipo de llave y -b define la longitud en bits. Aquí generamos una llave RSA de 2048 bits:

ssh-keygen -b 2048 -t rsa   # crea tu llave SSH
Generar un par de llaves SSH con ssh-keygen
Generando el par de llaves SSH.

Paso 5: copia la llave pública

Por defecto empiezas en tu directorio personal, así que solo tienes que entrar a .ssh para leer la llave pública:

cd .ssh           # entra al directorio .ssh
ls                # lista los archivos que hay dentro
cat id_rsa.pub    # imprime la llave pública y copia su contenido
Imprimir la llave SSH pública con cat
Leyendo la llave pública desde id_rsa.pub.

Paso 6: agrega la llave como llave de despliegue en GitHub

Abre tu repositorio de GitHub y entra a sus Settings.

Configuración del repositorio en GitHub
Abriendo la configuración del repositorio.

En el menú de la izquierda, baja hasta Deploy keys y haz clic en Add deploy key.

La sección Deploy keys en la configuración de GitHub
La sección de llaves de despliegue.

Ponle un nombre a la llave y pega el valor que copiaste en el campo Key. Deja Allow write access sin marcar. La instancia solo necesita leer del repositorio, nunca escribir en él.

Agregar la llave pública como llave de despliegue en GitHub
Pegando la llave pública como llave de despliegue de solo lectura.

Paso 7: copia el comando de clonación SSH del repositorio

De vuelta en la página del repositorio, toma la URL de clonación por SSH.

Copiar el comando de clonación SSH desde GitHub
Copiando el comando de clonación por SSH.

Paso 8: clona el repositorio en la instancia

De regreso en tu sesión de terminal, clona el repositorio con la URL SSH que acabas de copiar:

git clone "tu URL de clonación SSH de GitHub"
Clonar el repositorio de GitHub por SSH
Clonando el repositorio.

Paso 9: instala las dependencias

Entra al directorio del proyecto e instala todo:

ls                       # lista los directorios que acabas de clonar
cd "nombre de tu directorio" # entra a la raíz de la app
npm i                    # instala todas las dependencias
Instalar las dependencias del proyecto con npm
Instalando dependencias.

Paso 10: compila para producción

Genera un build de producción optimizado e instala sharp para que Next.js pueda encargarse de la optimización de imágenes automáticamente:

npm run build
npm install sharp   # permite que Next.js optimice imágenes automáticamente
Ejecutar el build de producción
Compilando la app para producción.

Paso 11: instala PM2

Necesitas algo que mantenga vivo el proceso de Next.js en segundo plano después de que cierres la terminal. PM2 es la herramienta estándar para eso: ejecuta, reinicia y supervisa el proceso por ti. Instálalo de forma global:

sudo npm install pm2 -g

Paso 12: corre Next.js bajo PM2

Arranca la app a través de PM2 para que sobreviva a las desconexiones de la terminal y puedas detenerla o reiniciarla cuando quieras:

pm2 start npm --name nextjs-app -- run start -- -p 3000

Revisa el estado del proceso en cualquier momento:

pm2 list nextjs-app
Revisar el proceso de Next.js corriendo en PM2
La app de Next.js corriendo bajo PM2.

Paso 13: configura el cortafuegos

Activa UFW y abre solo los puertos que de verdad necesitas: SSH, HTTP y HTTPS:

sudo ufw enable        # activa el cortafuegos
sudo ufw status        # revisa el estado del cortafuegos
sudo ufw allow ssh     # puerto 22
sudo ufw allow http    # puerto 80
sudo ufw allow https   # puerto 443
Configurar las reglas del cortafuegos UFW
Abriendo SSH, HTTP y HTTPS en UFW.

Paso 14: instala Nginx

sudo apt install nginx
Instalar Nginx en Ubuntu
Instalando Nginx.

Paso 15: configura Nginx como proxy inverso

El trabajo de Nginx aquí es sentarse delante de tu app como proxy inverso con caché. Abre la configuración de su sitio por defecto:

sudo nano /etc/nginx/sites-available/default

Busca la línea server_name y el bloque location /, y reemplázalos por lo siguiente (pon tu propio dominio):

server_name tudominio.com www.tudominio.com;

location / {
  proxy_pass http://localhost:5000; # el puerto en el que corre tu app
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection 'upgrade';
  proxy_set_header Host $host;
  proxy_cache_bypass $http_upgrade;
}
Editar la configuración del sitio por defecto de Nginx
La configuración de proxy inverso de Nginx.
Asegúrate de que el puerto de proxy_pass coincida con el puerto en el que tu app realmente escucha. Si arrancaste PM2 con -p 3000, apunta el proxy a http://localhost:3000.

Prueba la configuración y reinicia Nginx para cargarla:

sudo nginx -t              # prueba la configuración de Nginx
sudo service nginx restart # reinicia Nginx para aplicar los cambios
Probar y reiniciar Nginx
Validando la configuración y reiniciando Nginx.

Paso 16: apunta tu dominio a la instancia

Copia la dirección IP pública de tu instancia EC2.

La dirección IP pública de la instancia EC2
Copiando la IP pública de la instancia.

En tu proveedor de DNS (aquí, Route 53), pégala en el registro principal de tipo A. Si todavía no existe uno, crea el registro.

Apuntar un registro A de Route 53 a la instancia EC2
Apuntando el registro A a la instancia.

Visita tu dominio ahora y verás que se sirve por HTTP simple (puerto 80) y aparece marcado como no seguro.

El sitio cargando por HTTP sin cifrar
El sitio está en vivo, pero todavía sin asegurar.

Paso 17: agrega un certificado SSL gratuito con Certbot

Instala Certbot con su complemento para Nginx y luego solicita los certificados de Let's Encrypt para tu dominio. Certbot actualiza la configuración de Nginx y la recarga por ti:

sudo apt install certbot python3-certbot-nginx
sudo apt-get update   # actualiza paquetes
sudo certbot --nginx -d tudominio.com -d www.tudominio.com   # solicita los certificados SSL/TLS
Solicitar un certificado SSL con Certbot
Emitiendo el certificado con Certbot.

Recarga el navegador y tu tráfico ya viaja cifrado por HTTPS.

El sitio cargando de forma segura por HTTPS
El sitio ahora se sirve de forma segura por HTTPS.

Ese es todo el proceso: un build de producción de Next.js mantenido vivo por PM2, con Nginx al frente, un cortafuegos y un certificado SSL que se renueva solo. Son más piezas móviles que un despliegue de un clic, pero te dan control total sobre el servidor. Si prefieres delegarlo, cuéntanos sobre tu proyecto. O mira lo que hemos lanzado.

¿Estás pensando en construir esto?

Appluex diseña y lanza apps móviles y web en producción, incluidas funciones de inteligencia artificial. Hablemos.

Agenda una consulta →← Todos los artículos