Puntos clave
- Pasar del desarrollo local al entorno de producción implica gestionar variables de entorno, la gestión de errores, la gestión de procesos y la seguridad de una forma que tu configuración local nunca te exigió.
- El alojamiento gestionado de Node.js se encarga de la mayor parte de la infraestructura subyacente, así que puedes centrarte en el código en lugar de tener que configurar PM2, Nginx y los certificados SSL a mano.
- Las opciones de implementación van desde configuraciones de VPS no gestionadas, en las que te encargas de todo tú mismo, hasta plataformas totalmente sin servidor, cada una con diferentes ventajas e inconvenientes en cuanto a control, coste y complejidad.
Una cosa es conseguir que una aplicación de Node.js funcione en tu portátil. Pero ponerla en funcionamiento en un servidor de verdad, de forma segura y sin que se cuelgue en cuanto la usen usuarios reales, es un reto totalmente distinto.
Hay que gestionar variables de entorno, pensar en la seguridad, mantener la aplicación en marcha con un gestor de procesos y, sobre todo, decidir dónde alojarla. La mayoría de los tutoriales se saltan todo eso.
En este blog, te explicaré paso a paso los aspectos prácticos que hay que tener en cuenta a la hora de implementar una aplicación de Node.js, te hablaré de los diferentes sitios donde puedes implementarla y, después, crearemos desde cero un pequeño acortador de URL y lo pondremos en marcha en Cloudways.
Lo que realmente importa a la hora de implementar una aplicación Node.js
Implementar no consiste simplemente en ejecutar «npm start» en otro ordenador. Hay algunos aspectos prácticos que son más importantes de lo que la gente suele pensar.
Lo primero son las variables de entorno. Cualquier dato confidencial o específico del entorno —como claves de API, puertos o URL base— no debería ir escrito directamente en el código fuente. A nivel local, todo eso se guarda en un archivo .env. En producción, la plataforma de alojamiento se encarga de almacenar esos valores y se los pasa a la app en tiempo de ejecución.
La gestión de errores es otro tema. Un solo error sin gestionar en el entorno de producción puede dejar fuera de servicio toda la aplicación para todos los usuarios o, lo que es peor, filtrar un seguimiento de pila sin procesar que revele detalles internos. Un middleware básico de gestión de errores lo detecta todo y, en su lugar, muestra a los usuarios un mensaje adecuado.
Luego está el tema de la seguridad. Las apps en vivo son analizadas por bots casi en cuanto se conectan. Un par de medidas sencillas pueden marcar una gran diferencia: Helmet para gestionar bien los encabezados HTTP, la limitación de tasas para que nadie pueda saturar una ruta y una validación básica de entradas para que la app no se fíe ciegamente de lo que escriba cualquier visitante al azar.
Y, por último, mantener la aplicación en funcionamiento. Cuando cierras el terminal, el proceso «node server.js» se cierra con él, lo cual, obviamente, no va a funcionar en un servidor en producción. PM2 es una forma de gestionarlo tú mismo, pero en una plataforma gestionada, esto ya está resuelto por ti.
Olvídate de configurar el servidor: implementa tu aplicación en Cloudways
El alojamiento gestionado de Node.js de Cloudways se encarga del servidor, la gestión de procesos y toda la infraestructura de implementación para que puedas centrarte en la propia aplicación.
Dónde implementar realmente tu aplicación Node.js
No existe un único lugar correcto para desplegar una aplicación Node.js; realmente depende de cuánto de los aspectos subyacentes estés dispuesto a gestionar tú mismo.
VPS sin gestión: dispones de un servidor «a pelo» y control total sobre todo lo que hay en él. Eso también significa que tienes que instalar Node tú mismo, poner en marcha PM2, configurar Nginx, ocuparte del SSL y gestionar las actualizaciones del sistema operativo. Flexibilidad total, pero también responsabilidad total.
Alojamiento gestionado de Node.js: el proveedor se encarga del servidor, de la gestión de procesos y de toda la infraestructura de implementación. Tú solo tienes que proporcionarle un repositorio de Git, elegir tu framework y la versión de Node, y la aplicación ya está en marcha. Así tienes muchas menos cosas de las que preocuparte.
Sin servidor: en lugar de un servidor permanente, tu código se ejecuta bajo demanda cada vez que llega una solicitud. Es ideal para aplicaciones con picos de tráfico y poco volumen, pero normalmente implica reescribir partes del código para adaptarlo a ese modelo.
Para este tutorial, voy a usar el alojamiento gestionado de Node.js de Cloudways, sobre todo porque me permite centrarme en la aplicación en sí, en lugar de perder tiempo configurando un servidor desde cero.
Cómo implementar una aplicación de Node.js: una guía práctica paso a paso
Para poner todo esto en práctica, voy a crear un pequeño acortador de URL, una app que toma una URL larga, te devuelve una corta y te redirige a la original cada vez que alguien haga clic en el enlace corto.
Cuando haya terminado, esta aplicación estará en funcionamiento en un servidor real, protegida con las medidas de seguridad básicas que he mencionado antes, y obtendrá su configuración a través de variables de entorno en lugar de código fijo.
Lo que voy a usar
- Node.js
- VS Code
- Express, EJS y SQLite
- Helmet, express-rate-limit y nanoid
Paso 1: Configurar el proyecto de Node.js
Antes de poder escribir código, necesito tener Node.js instalado en mi ordenador.
Estoy trabajando en el portátil de la oficina, que tiene restricciones informáticas, así que voy a descargar la versión binaria independiente de Node.js desde nodejs.org en lugar de ejecutar un instalador normal.

Una vez descargado, lo descomprimiré en mi carpeta de Descargas.

Abrir la carpeta del proyecto en la línea de comandos
Voy a crear una carpeta para este proyecto en mi escritorio y la llamaré simplemente «node-url-shortener».
Luego abriré la línea de comandos y me desplazaré a la carpeta de mi proyecto:
cd C:\Users\abdulrehman\Desktop\node-url-shortener
Cómo indicar al Símbolo del sistema dónde está el binario de Node
El Símbolo del sistema no sabe automáticamente dónde están los archivos de Node en mi ordenador, así que se lo voy a indicar manualmente con este comando:
set PATH=%PATH%;C:\Users\abdulrehman\Downloads\node-v24.18.0-win-x64\node-v24.18.0-win-x64

Para asegurarme de que funciona de verdad, voy a ejecutar:
node -v npm -v
Ambos comandos han mostrado los números de versión, así que ya puedo seguir adelante.
Crear el proyecto de Node.js
Primero voy a inicializar un proyecto sencillo de Node.js:
npm init -y

Después instalaré todo lo que este proyecto realmente necesita: Express para el enrutamiento, EJS para generar el HTML, better-sqlite3 para una pequeña base de datos basada en archivos, nanoid para generar códigos cortos, dotenv para cargar variables de entorno, y Helmet junto con express-rate-limit para las dos medidas de seguridad más básicas que cualquier aplicación en producción debería tener.
npm install express ejs better-sqlite3 nanoid dotenv helmet express-rate-limit
Paso 2: Crear el acortador de URL
Ahora voy a abrir la carpeta de mi proyecto en VS Code. Para ello, voy a ir a Archivo > Abrir carpeta y seleccionaré mi carpeta «node-url-shortener».

Vale, ya tengo mi proyecto abierto en VS Code, así que ahora toca compilarlo de verdad.
Configuración de la base de datos
Lo primero que quiero hacer es configurar la base de datos para que los enlaces cortos se mantengan cuando se reinicie la aplicación. Para ello, en la raíz de la carpeta de mi proyecto, voy a crear una carpeta llamada «db» y, dentro de ella, un archivo llamado «database.js». Voy a pegar esto:
const path = require('path');
const Database = require('better-sqlite3');
const db = new Database(path.join(__dirname, 'urls.db'));
db.exec(`
CREATE TABLE IF NOT EXISTS urls (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT UNIQUE NOT NULL,
original_url TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
module.exports = db;

Esto crea un archivo de base de datos SQLite la primera vez que se ejecuta la app, con una tabla que incluye la URL original, su código abreviado y la fecha de creación.
Añadir variables de entorno
Ahora necesito un sitio donde guardar la configuración que no deba ir codificada directamente en el código. Para ello, en la raíz de mi proyecto, voy a crear un nuevo archivo llamado .env y voy a pegar lo siguiente:
BASE_URL=http://localhost:3000 NODE_ENV=development

BASE_URL es lo que se usa al generar cada enlace corto. Más adelante lo cambiaré por la URL de Cloudways en producción. NODE_ENV=development está bien por ahora, mientras estoy probando localmente, pero lo cambiaré a producción en el servidor de producción.
Ahora bien, como no quiero que nada de esto acabe en mi repositorio público de GitHub, voy a crear un archivo más en la raíz de mi proyecto llamado .gitignore y voy a pegar lo siguiente:
node_modules .env db/urls.db

Esto le dice a Git que se salte estos tres archivos cada vez que haga un push. «node_modules» porque solo son paquetes descargados. «.env» porque contiene mis valores de configuración reales. Y «db/urls.db» porque es el archivo de la base de datos local, que no debería incluirse con el código.
Configuración del servidor
Aquí es donde voy a desarrollar la aplicación propiamente dicha: toma la URL larga, genera un código corto para ella, almacena ambas en la base de datos y se encarga de la redirección cuando alguien hace clic en el enlace corto.
Voy a crear un archivo llamado server.js en la raíz de mi proyecto y voy a pegar esto en él:

require('dotenv').config();
const express = require('express');
const path = require('path');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const { nanoid } = require('nanoid');
const db = require('./db/database');
const app = express();
const PORT = process.env.PORT || 3000;
const BASE_URL = process.env.BASE_URL || `http://localhost:${PORT}`;
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(helmet());
app.use(express.urlencoded({ extended: true }));
// Limit the shorten route to 10 requests per minute per IP
const shortenLimiter = rateLimit({
windowMs: 60 * 1000,
max: 10,
message: 'Too many requests, please slow down.',
});
app.get('/', (req, res) => {
res.render('index', { shortUrl: null, error: null });
});
app.post('/shorten', shortenLimiter, (req, res) => {
const { url } = req.body;
try {
new URL(url);
} catch {
return res.render('index', { shortUrl: null, error: 'Please enter a valid URL.' });
}
const code = nanoid(7);
db.prepare('INSERT INTO urls (code, original_url) VALUES (?, ?)').run(code, url);
res.render('index', { shortUrl: `${BASE_URL}/${code}`, error: null });
});
app.get('/:code', (req, res) => {
const row = db.prepare('SELECT original_url FROM urls WHERE code = ?').get(req.params.code);
if (!row) {
return res.status(404).render('not-found');
}
res.redirect(row.original_url);
});
// Basic error handler so users never see a raw stack trace
app.use((err, req, res, next) => {
console.error(err);
res.status(500).send('Something went wrong on our end.');
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Helmet se aplica a toda la aplicación, la ruta acortada está envuelta en un limitador de velocidad y hay un manejador de errores en la parte inferior que garantiza que los usuarios nunca tengan que ver un rastreo de pila si algo falla.
Ese archivo server.js apunta a una plantilla llamada index que aún no existe, así que a continuación crearé la página que necesita.
Creación de la interfaz de usuario
Voy a crear una carpeta llamada views en la raíz de mi proyecto, y dentro de ella, un archivo llamado index.ejs . Voy a pegar esto:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Shorten a URL</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<main>
<div class="card">
<h1>Shorten a URL</h1>
<form action="/shorten" method="POST">
<input type="text" name="url" placeholder="Paste a long URL here" required>
<button type="submit">Shorten</button>
</form>
<% if (error) { %>
<p class="error"><%= error %></p>
<% } %>
<% if (shortUrl) { %>
<div class="result">
<p>Here's your short link:</p>
<a href="<%= shortUrl %>" target="_blank"><%= shortUrl %></a>
</div>
<% } %>
</div>
</main>
</body>
</html>

Dentro de la misma carpeta «views», voy a crear otro archivo llamado «not-found.ejs»; esto es lo que aparece cuando alguien introduce un código corto que no existe. Voy a pegar esto:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Link Not Found</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<main>
<div class="card">
<h1>Link Not Found</h1>
<p>That short link doesn't exist, or it may have expired.</p>
<a href="/">Go back</a>
</div>
</main>
</body>
</html>

Y, para terminar con este paso, voy a crear una carpeta llamada «public» en la raíz de mi proyecto y, dentro de ella, un archivo llamado «style.css». Voy a pegar esto ahí:
* {
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
background: linear-gradient(135deg, #667eea, #764ba2);
margin: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
main {
width: 100%;
max-width: 500px;
}
.card {
background: #ffffff;
border-radius: 12px;
padding: 40px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
}
h1 {
margin-top: 0;
color: #1f2937;
}
form {
display: flex;
gap: 10px;
margin-top: 20px;
}
input {
flex: 1;
padding: 12px 14px;
border: 1px solid #d1d5db;
border-radius: 8px;
font-size: 14px;
}
button {
background: #667eea;
color: #ffffff;
border: none;
padding: 12px 20px;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
}
button:hover {
background: #5568d3;
}
.error {
color: #dc2626;
margin-top: 15px;
}
.result {
margin-top: 25px;
padding: 15px;
background: #f3f4f6;
border-radius: 8px;
}
.result a {
color: #667eea;
word-break: break-all;
}

Esto le da a todo un aspecto limpio, como de tarjetas, nada extravagante, solo lo justo para que la app no parezca texto sin formato.
Probando lo que hemos creado
Vale, es hora de probarlo. Voy a añadir un script de inicio a mi package.json para no tener que estar escribiendo siempre el comando completo de Node:
"scripts": {
"start": "node server.js",
"test": "echo \"Error: no test specified\" && exit 1"
}

Luego, de vuelta en la línea de comandos:
npm start
Voy a comprobar que funciona abriendo el navegador y entrando en http://localhost:3000. Voy a pegar una URL larga, pulsaré «Acortar» y debería aparecer un enlace corto. Al hacer clic en él, me lleva directamente a la página original.
Todo va tal y como yo quiero.

Paso 3: Subir el proyecto a GitHub
Antes de implementarlo en ningún sitio, quiero subirlo primero a GitHub.
Me voy a pasar por GitHub y voy a crear un nuevo repositorio llamado node-url-shortener.
Después, de vuelta en la línea de comandos, voy a ejecutar estos comandos uno por uno para subir mi proyecto:
git init git add . git commit -m "First commit" git branch -M main git remote add origin https://github.com/abdulrehman293/node-url-shortener git push -u origin main
Ahora voy a volver a mi repositorio de GitHub y a actualizar la página. Y, como era de esperar, se han subido todos mis archivos, excepto el .env, node_modules y el archivo de la base de datos local.

Paso 4: Implementación en Cloudways
A estas alturas, mi proyecto ya funciona en el entorno local y también lo he subido a GitHub. Ahora lo voy a pasar del entorno local a un servidor en producción. Para ello, voy a usar el alojamiento gestionado de Node.js de Cloudways, diseñado precisamente para implementar proyectos como este directamente desde un repositorio de Git.
Desde el panel de control de Cloudways, voy a hacer clic en «Node.js» en el menú y, a continuación, en «Lanzar ahora».

A partir de aquí, voy a elegir el plan Starter, ya que este proyecto no necesita muchos recursos para funcionar.

Ahora, en la pantalla «Implementar tu aplicación web de Node.js», voy a hacer clic en «Conectar a través de Git» y, cuando me lo pida, voy a iniciar sesión en mi cuenta de GitHub.
Una vez que haya iniciado sesión, elegiré mi repositorio «node-url-shortener» de la lista y seguiré adelante.

Paso 5: Configuración y puesta en marcha
En esta siguiente parte le indico a Cloudways cómo ejecutar el proyecto.
Voy a elegir «Express» como configuración predeterminada del framework. En cuanto a la versión de Node, voy a elegir Node 24 (LTS). Voy a dejar el directorio raíz tal y como viene por defecto.

A continuación, haré clic en la opción «Cambiar» dentro de «Configuración de compilación y salida». Después, configuraré el «Gestor de paquetes» como «npm» y el «Archivo de entrada» como «server.js».

Ahora bien, como mi archivo .env no se ha subido a GitHub a propósito, tengo que añadir esos valores a mano. Voy a la sección «Variables de entorno» y le doy a «Añadir». Aquí puedo añadir mis variables.
Para la primera, voy a poner «Key» en NODE_ENV y «Value» en production.
Después haré clic en «+ Añadir más» y pondré la clave en «BASE_URL». Y para el valor, de momento, pondré un valor provisional como «http://placeholder.com», ya que todavía no tengo mi URL temporal de Cloudways. Volveré a actualizar esto después de la primera implementación.
Una vez que las dos estén introducidas, haré clic en «Guardar».


Una vez rellenado todo, pulsaré «Implementar ahora».
Ahora, Cloudways descargará el código de GitHub, instalará las dependencias e iniciará la aplicación usando el archivo de entrada que le he indicado.

El momento de la verdad
En cuanto la implementación aparezca como «correcta», copiaré la URL temporal de Cloudways desde la página «Resumen» de la app.

Volveré a «Variables de entorno», actualizaré BASE_URL con esa URL y volveré a implementar para que el cambio surta efecto.



Ahora, si abro mi URL temporal de Cloudways en el navegador, pego una URL larga y pulso «Acortar», obtengo un enlace corto que apunta a mi dominio de Cloudways en producción. Al hacer clic en él, me lleva directamente a la URL original. (También puedes configurar Node.js con PostgreSQL en Cloudways)

Ya está todo listo, y cualquier enlace corto que cree a partir de ahora seguirá funcionando mientras la app esté en marcha.
Además, échale un vistazo a nuestra comparación detallada entre Django y Node.js.
Conclusión
Con esto terminamos este repaso sobre cómo desplegar una aplicación de Node.js. En este blog, he explicado lo que realmente implica pasar del desarrollo local a un servidor en producción: las variables de entorno, la gestión de errores, la seguridad y cómo mantener la aplicación en funcionamiento; y luego he repasado los diferentes lugares donde puedes desplegarla.
También he creado un pequeño acortador de URL con una interfaz de usuario que funciona, he añadido medidas básicas de seguridad para producción y he subido el proyecto terminado a mi GitHub. No dudes en clonarlo y reutilizar el código.
Y si tú también quieres intentar implementar algo similar, nuestro alojamiento gestionado de Node.js se encarga precisamente de este tipo de flujo de trabajo: conecta tu repositorio, elige tu framework y ya estás en línea.
¿Listo para poner en marcha tu propio proyecto de Node.js?
Conecta tu repositorio de Git, elige tu framework y pon tu app en marcha en Cloudways en cuestión de minutos.
P. ¿Necesito un servidor especial para implementar una aplicación de Node.js?
La verdad es que no. Un simple VPS te vale si te sientes cómodo gestionando el servidor tú mismo, o puedes optar por una plataforma gestionada como Cloudways, donde se encargan por ti del servidor, la gestión de procesos y toda la infraestructura de implementación. El modelo «serverless» es una tercera opción, aunque suele adaptarse mejor a ciertos tipos de aplicaciones que a otros.
P. ¿Cuál es la diferencia entre ejecutar una aplicación de Node.js localmente y en producción?
A nivel local, eres el único usuario, los fallos no son un gran problema y la configuración está en un archivo de tu ordenador. En producción, hay usuarios reales utilizando la aplicación, un solo error no gestionado puede colapsar todo el sistema y la configuración tiene que estar fuera del código para que los distintos entornos puedan usar valores diferentes.
P. ¿Necesito PM2 para que mi aplicación de Node.js siga funcionando?
Solo si gestionas el servidor tú mismo. PM2 reinicia la aplicación si se cuelga y la mantiene en funcionamiento después de que cierres la terminal. En una plataforma gestionada, eso ya se encarga de forma automática.
P. ¿Es seguro desplegar una aplicación de Node.js directamente desde GitHub?
Sí, siempre y cuando la información confidencial se guarde en variables de entorno y no en el código. Tu archivo .env debería excluirse mediante .gitignore para que nunca acabe en el repositorio, y la plataforma de despliegue almacena esos mismos valores por separado y los pasa en tiempo de ejecución.
Start Growing with Cloudways Today.
Our Clients Love us because we never compromise on these
[email protected]
Abdul es un experto en tecnología, aficionado al café y al marketing creativo al que le encanta estar al día de las últimas actualizaciones de software y aparatos tecnológicos. También es un hábil escritor técnico capaz de explicar conceptos complejos de forma sencilla para un público amplio. Abdul disfruta compartiendo sus conocimientos sobre el sector de la Nube a través de manuales de usuario, documentación y entradas de blog.