Node en Digital Ocean con PM2, NGINX, Dominio Personalizado y Certificado SSL
By Tomás Hernández, Posted on September 15, 2024 (1y ago)
Importante
No voy a mostrar los pasos de levantar el backend en DigitalOcean, sino el proceso de PM2 en adelante. Si querés ver como levantarlo, tenés que agregar claves SSH y clonar el repositorio del backend: mirá acá Fazt Deploy
Actualizar los paquetes de Ubuntu
sudo apt-get update
Y luego
sudo apt-get upgrade
Instalando Node vía NVM
NVM en un servidor es súper útil porque te permite tener varias versiones de Node instaladas y que cada proyecto pueda utilizar su propia versión. Para poder configurar esto basta con crear un archivo llamado .nvmrc en la raíz de tu proyecto, en la cual solamente tenés que indicar qué version querés utilizar.
Ej.: v20.17.0
Una vez que tenés eso, ahora sí instalemos NVM en nuestro servidor. Entrá a Install Nvm y copiá el comando de curl que sea estable, por ejemplo:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
Este mismo comando, pegalo en tu servidor de DigitalOcean y cuando termine, ejecutá el siguiente comando para que refleje la configuración de nvm
source ~/.bashrc
Ahora, instalá todas las versiones de node que tengas en tus proyectos que vas a levantar. En mi caso, es la 20.17.0 entonces
nvm install node@20.17.0
Esto va a permitir que cuando después ejecutes con pm2, la línea de nvm use utilice la versión que espera el proyecto (la definida en .nvmrc)
Instalando PM2
PM2 es un Process Manager de Node que nos permite ejecutar y gestionar varias aplicaciones en simultáneo.
Preparativos
En la raíz de tu proyecto del backend, create un archivo que se llame ecosystem.json . Este archivo es súper importante porque te va a permitir manejar las configuraciones que va a tomar PM2 al ejecutar. Te dejo un esqueleto común
{
"apps": [
{
"name": "nombreProyecto",
"script": "dist/index.js",
"env_production": {
"NODE_ENV": "production"
},
"env_dev": {
"NODE_ENV": "development"
},
"instances": 1,
"log-date-format": "DD-MM HH:mm:ss.SSS"
}
],
"deploy": {
"development": {
"pre-deploy-local": "yarn install --frozen-lockfile --link-duplicates && yarn build",
"post-deploy": "yarn install && pm2 startOrRestart ecosystem.json --env development",
"env": {
"NODE_ENV": "development"
}
}
}
Entonces para poder levantar el proyecto vas a ejecutar los siguientes comandos
yarn install --frozen-lockfile --link-duplicates && yarn build
yarn install && pm2 startOrRestart ecosystem.json --env development
Importante: Esto quiere decir que si subís un cambio al repositorio deberías: entrar al servidor, hacer un fetch desde git, hacer el pull y ejecutar exactamente los mismos comandos que arriba. Esto porque vamos a estar ejecutando un build y no la aplicación en modo desarrollo (nodemon).
Importante 2: Si usás yarn, npm, bun o cualquier otro package manager considerá cambiar los comandos correspondientes de install y build.
Configurando PM2 en el servidor
Ejecutá
sudo npm install -g pm2
Ahora dirigite a la ruta donde tenés tu proyecto en el servidor, en mi caso es projects/proyecto entonces hago
cd projects/proyecto
Ejecutá ahora los comandos de pre-deploy-local y post-deploy del archivo ecosystem.json
yarn install --frozen-lockfile --link-duplicates && yarn build
yarn install && pm2 startOrRestart ecosystem.json --env development
Luego, debería levantarse todo correctamente. Podés chequearlo haciendo
pm2 list -> pm2 logs tuProyecto
PM2 en Startup
Si se te cae el servidor, sería un bajón tener que levantar todo vos manualmente. Imaginate que estás durmiendo.
Por suerte, PM2 tiene una configuración para levantarse con el servidor en caso de restart.
Ejecutá
pm2 startup systemd
¡Listo!
Configuración de NGINX
Instalando NGINX
sudo apt install nginx
Configurando NGINX
Nos dirigimos a /etc/nginx/sites-available con cd /etc/nginx/sites-available. Habilitamos el Firewall y le permitimos a Nginx
- sudo ufw enable
- sudo ufw allow 'Nginx Full'
- sudo ufw delete allow 'Nginx HTTP'
Luego de haber hecho estas 3 cosas, si colocamos sudo ufw status debería verse algo parecido a esto
Output
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
Nginx Full ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
Nginx Full (v6) ALLOW Anywhere (v6)
Ahora necesitamos crear un archivo que le permitirá a NGINX ejecutar cuando entremos vía el dominio que queramos, por lo tanto ejecutamos.
Verificando si NGINX funciona
Ejecutá
sudo nano default
y pegá la siguiente configuración
server {
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
default_type 'text html; charset=utf-8';
proxy_pass http://localhost:puertoApp;
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;
include proxy_params;
}
}
De acá lo único que tenes que cambiar es 1 cosa:
- puertoApp: colocá literalmente el puerto donde levantás tu app de Node
Para cerrar nano, podés hacer CTRL + O, colocarle un nombre al archivo y luego CTRL + X.
Verificando Configuración de Nginx
Ejecutá
sudo nginx -t
Si está todo bien debería salirte algo como esto
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Ahora, necesitamos crear un link desde sites-available a sites-enabled para que NGINX pueda leer nuestra configuración.
sudo ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/
Por último, reiniciemos NGINX para que tome nuestros cambios
sudo systemctl reload nginx
Entonces ahora si entrás por el navegador a la IP del servidor de DigitalOcean donde tenés tu servidor debería estar sirviendo la API
http://tuIp
¡Importante el http! Después vamos a ver como levantar el https.
Configurando NGINX con nuestro dominio personalizado
En cd /etc/nginx/sites-available, ejecutá
sudo nano tuDominio
Un ejemplo porque puede traer confusión, si tu dominio es: https://www.tudominio.com.ar/ entonces tendrías que colocar
sudo nano tudominio.com.ar
Cuando hagas algo como esto, pegá la siguiente configuración (no te gastes mucho) excepto en la que te voy a decir
server {
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name tuDominio www.tuDominio;
location / {
default_type 'text html; charset=utf-8';
proxy_pass http://localhost:puertoApp;
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;
include proxy_params;
}
}
De acá lo único que tenes que cambiar son 3 cosas:
- tuDominio: colocá literalmente tu dominio
- www.tuDominio: el subdominio www de tuDominio
- puertoApp: colocá literalmente el puerto donde levantás tu app de Node
Para cerrar nano, podés hacer CTRL + O, colocarle un nombre al archivo y luego CTRL + X.
Verificando Configuración de Nginx
Ejecutá
sudo nginx -t
Si está todo bien debería salirte algo como esto
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Creamos el link de sites-available a sites-enabled
sudo ln -s /etc/nginx/sites-available/tuDominio /etc/nginx/sites-enabled/
Por último, reiniciemos NGINX para que tome nuestros cambios
sudo systemctl reload nginx
¿Qué hicimos hasta ahora?
Levantamos nuestra app de Node en DigitalOcean configuramos nuestro dominio en el VPS. Sigamos ahora configurando los DNS de nuestro dominio.
Configurando los DNS
Dirigite a la plataforma que gestiona tu dominio y buscá gestor de DNS, DNS, o como se llame en tu plataforma. Deberías de ver muchos registros del tipo A, MX, TXT, etc. Ojo, hago énfasis en que gestiona tu dominio porque no necesariamente manejás los DNS desde allí en muchos casos. Por ejemplo, yo tengo el dominio en DonDominio pero los DNS los manejo en Cloudflare.
Una vez que estás en el GESTOR de DNS, tenés que agregar 2 registros A.
A @ DROPLET_IP
A www DROPLET_IP
DROPLET le llamamos al VPS en DigitalOcean. Sería la IP que usamos para ingresar a través de Ubuntu.
Si está todo bien, deberías colocar el dominio en DNS Checker y deberías de ver la IP del servidor en DigitalOcean.
Considerá que tarda hasta aproximadamente 24hs en reflejar el cambio aunque normalmente en 10 minutos (como mucho) debería estar todo ok.
Levantando el certificado de HTTPS en nuestro backend
Instalá este paquete
sudo apt install certbot python3-certbot-nginx
Ejecutá
sudo ufw status
y revisá que te de el siguiente resultado
Output
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
Nginx HTTP ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
Nginx HTTP (v6) ALLOW Anywhere (v6)
Si el resultado que te dió es diferente, fijate en hacer lo de arriba de vuelta del ufw.
Ejecutá
sudo certbot --nginx -d tuDominio -d www.tuDominio
Seguí los pasos que te pide (son 2). Si todo sale bien te va a mostrar un mensaje como este
Deploying certificate
Successfully deployed certificate for tuDominio to /etc/nginx/sites-enabled/tuDominio
Successfully deployed certificate for www.tuDominio to /etc/nginx/sites-enabled/tuDominio
Congratulations! You have successfully enabled HTTPS on https://tuDominio and https://www.tuDominio
Hay un posible error muy común y es este:
Certbot failed to authenticate some domains (authenticator: nginx). The Certificate Authority reported these problems:
Domain: tuDominio
Type: unauthorized
Detail: IP: Invalid response from http://tuDominio/.well-known/acme-challenge/B3gsR-gkB54SfIgeQdy1Z7bWJiWq50NRw5RY3MC6_40: 404
Si IP es diferente a la IP de tu droplet de DigitalOcean, entonces es porque configuraste mal los registros DNS desde el gestor de dominio.
Si todo salió bien, entoncés probá entrando a https://www.tuDominio y deberías ver todo joya.