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

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.

Paso 2: actualiza los paquetes y agrega la llave de NodeSource
Primero, refresca el índice de paquetes del sistema:
sudo apt-get update
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.gpgPaso 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.listVuelve 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
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.gpgConfirma la versión que acabas de instalar:

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
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
Paso 6: agrega la llave como llave de despliegue en GitHub
Abre tu repositorio de GitHub y entra a sus Settings.

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

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.

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.

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"
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
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
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 -gPaso 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 3000Revisa el estado del proceso en cualquier momento:
pm2 list nextjs-app
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
Paso 14: instala Nginx
sudo apt install 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/defaultBusca 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;
}
Asegúrate de que el puerto deproxy_passcoincida con el puerto en el que tu app realmente escucha. Si arrancaste PM2 con-p 3000, apunta el proxy ahttp://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
Paso 16: apunta tu dominio a la instancia
Copia la dirección IP pública de tu instancia EC2.

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.

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

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
Recarga el navegador y tu tráfico ya viaja cifrado 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.