Cómo hospedar una aplicación Node.js

Para hospedar una aplicación Node.js necesitas un servidor que ejecute la versión correcta de Node, tu código con su package.json y un comando de inicio. El puerto y los secretos deben venir de variables de entorno.

Actualizado el 6 min de lectura

Objetivos de aprendizaje

Después de leer este artículo podrás:

  • Preparar un proyecto Node.js para ejecutarse fuera de tu máquina
  • Escribir un servidor que lee el puerto del entorno y se cierra con seguridad
  • Verificar el deploy y corregir los errores más comunes
En esta página

Una aplicación Node.js funciona en tu máquina porque ahí están el Node correcto, las dependencias instaladas y las variables que configuraste. Hospedar es reproducir ese entorno en un servidor que está encendido todo el tiempo y al que otras personas pueden acceder.

El camino es el mismo para una API, un sitio con servidor o un bot: preparar el proyecto, subir el código, instalar las dependencias e iniciar el proceso. Esta guía usa una API con Express como ejemplo, pero los pasos valen para cualquier app Node.js. Para la visión general, mira qué es el deploy.

1Proyecto localpackage.jsonpackage-lock.jsonindex.jsEnvío al servidorsin node_modules ni .env2npm ci --omit=devdependencias del lockfile3Proceso nodeescucha el puerto de PORT4PORTvariable de entorno
Envía el proyecto con el lockfile, instala con npm ci e inicia node en el puerto que el entorno indica en PORT.

¿Qué necesitas antes de hospedar?#

  • Una versión de Node definida. Elige una versión LTS y usa la misma en local y en producción.
  • Un package.json completo, con las dependencias y un script de inicio.
  • Un package-lock.json incluido en el repositorio, para que la instalación sea idéntica en cualquier máquina.
  • Un lugar donde ejecutarla: un VPS, un contenedor o una plataforma de hospedaje. La diferencia entre ellos está en VPS vs plataforma en la nube.

¿Qué LTS elegir? Para un proyecto nuevo, usa la versión LTS activa de Node.js, siempre que tus dependencias la soporten, y declara la versión en el package.json. El calendario oficial muestra qué versión está en esa fase y hasta cuándo recibe correcciones cada una.

¿Cómo preparar el proyecto?#

Crea la estructura mínima:

bash
mkdir mi-api && cd mi-api
npm init -y
npm install express

Ajusta el package.json para módulos ES, con la versión de Node y los scripts:

json
{
  "name": "mi-api",
  "version": "1.0.0",
  "type": "module",
  "main": "index.js",
  "engines": { "node": ">=24" },
  "scripts": {
    "start": "node index.js",
    "dev": "node --watch --env-file-if-exists=.env index.js"
  },
  "dependencies": {
    "express": "^5.0.0"
  }
}

El campo engines documenta la versión esperada, y el script start es el comando que ejecutará el servidor. dev usa dos flags nativas de Node: --watch reinicia el proceso cuando cambia un archivo, y --env-file-if-exists carga las variables de un archivo .env (sin error si no existe), sin necesidad del paquete dotenv.

¿Cómo escribir un servidor listo para producción?#

Un servidor de pruebas se vuelve un servidor de producción cuando lee el puerto del entorno, mantiene los secretos fuera del código y maneja la señal de cierre.

js
// index.js
import express from "express";

const app = express();
const port = Number(process.env.PORT) || 3000;

app.get("/", (req, res) => {
  res.send("¡Hola, mundo!");
});

app.get("/health", (req, res) => {
  res.json({ status: "ok" });
});

const server = app.listen(port, "0.0.0.0", () => {
  console.log(`Servidor escuchando en el puerto ${port}`);
});

// Al recibir SIGTERM (reinicio o despliegue), deja de aceptar
// conexiones nuevas y termina las que están en curso.
process.on("SIGTERM", () => {
  server.close(() => process.exit(0));
});

Por qué importa cada parte:

  • process.env.PORT: la plataforma o el servidor decide el puerto y lo informa por variable. Fijar 3000 solo funciona en tu máquina. El || 3000 es el valor para desarrollo.
  • Ruta /health: te da a ti (y a monitores externos) una dirección ligera para comprobar que la app responde.
  • SIGTERM: es la señal que el sistema envía para pedir que el proceso termine. Cuando registras un listener, Node deja de salir al instante (su comportamiento por defecto), así que debes terminar el proceso explícitamente, como en el ejemplo.

Prueba en local con npm run dev y abre http://localhost:3000/health.

¿Cómo manejar las variables de entorno y los secretos?#

Los tokens, las contraseñas de base de datos y las claves de API van en variables de entorno, nunca en el repositorio. Léelas con process.env.NOMBRE. En local, usa un archivo .env y agrégalo al .gitignore; en producción, registra cada variable en el panel de tu proveedor o en el proceso del servidor. La guía qué son las variables de entorno detalla el patrón.

Falla pronto cuando falte una variable obligatoria. En lugar de un error oscuro más adelante, recibes un mensaje claro al arrancar:

js
if (!process.env.DATABASE_URL) {
  throw new Error("DATABASE_URL no está definida");
}

¿Cómo subir el código e iniciar el proceso?#

El flujo general, en cualquier hospedaje:

  1. Sube el código sin node_modules y sin el .env (usa un .gitignore).
  2. En el servidor, instala las dependencias exactas del lockfile: npm ci --omit=dev.
  3. Define las variables de entorno, incluido NODE_ENV=production.
  4. Inicia con npm start (o node index.js).
  5. Mantén el proceso vivo: en un VPS, con un supervisor como systemd o PM2; en contenedores y plataformas, con su política de reinicio. Mira también cómo empaquetar con Docker.

Si tu app es una API, repasa el checklist de producción para APIs antes de abrirla al público.

¿Cómo verificar que funcionó?#

Después del deploy, confirma en tres pasos:

  1. Logs: la línea "Servidor escuchando en el puerto" debe aparecer sin errores a continuación.
  2. Petición: llama a /health desde la dirección pública y espera {"status":"ok"}.
  3. Reinicio: reinicia la app y confirma que vuelve sola.

¿Cuáles son los errores más comunes?#

  • Error: listen EADDRINUSE: address already in use: otro proceso ya ocupa el puerto. Detén la instancia anterior o cambia el puerto.
  • Cannot find module 'express': las dependencias no se instalaron, o el paquete está en devDependencies y instalaste con --omit=dev. Muévelo a dependencies.
  • SyntaxError: Cannot use import statement outside a module: falta "type": "module" en el package.json, o usas import en un archivo tratado como CommonJS.
  • La app arranca, pero el sitio no abre: el servidor escucha en un puerto fijo en lugar de process.env.PORT, o solo en localhost. En contenedores, escucha en 0.0.0.0.
  • Funciona en local, falla en producción: versión de Node distinta, variable de entorno ausente o memoria insuficiente. Compara qué cambió entre los entornos.

Cómo hacerlo en Square Cloud#

El flujo de esta guía cambia poco en Square Cloud. Subes el proyecto como ZIP en el panel, con la CLI, desde GitHub (un push dispara el deploy) o con la extensión de VS Code, y el formulario de deploy detecta index.js como archivo principal. El archivo squarecloud.app es opcional: el panel lo genera en el deploy. Si prefieres el script del package.json, el campo de comando de inicio acepta cualquier comando de shell, como npm start o npm run migrate && npm start.

La instalación sigue dos reglas que explican errores de la lista anterior. La plataforma solo ejecuta npm install cuando no existe node_modules, así que sube el proyecto sin esa carpeta. Además, instala con NODE_ENV=production: lo que esté en devDependencies queda fuera, y el resultado es el Cannot find module. No hay paso de build; para compilar, usa un CI externo como GitHub Actions o pon el build en el comando de inicio. El runtime de TypeScript ejecuta tu código sin una compilación previa.

El puerto viene de la variable PORT, que define la plataforma, y el servidor escucha en 0.0.0.0, como en el ejemplo. Las variables de entorno se definen por aplicación en el panel (que importa un .env), ya existen durante la instalación y valen desde el siguiente reinicio, sin rebuild. El contenedor está siempre encendido, y el reinicio automático, activado por defecto en el deploy desde el panel, levanta de nuevo un proceso que terminó con error, así que PM2 sobra. Quedarse sin memoria detiene la aplicación hasta que aumentes su RAM. La salida de npm install y la línea "Servidor escuchando en el puerto" aparecen en los logs en tiempo real.

Cada aplicación web usa al menos 512 MB de la RAM del plan, y el Hobby empieza en R$12,49 al mes con 1 GB. Node.js es uno de varios runtimes, junto a Python, Go y Java: consulta los runtimes disponibles hoy en Runtimes y los planes en hosting de APIs.

Hosting de APIs

APIs en contenedores siempre encendidos, sin cold start

Más información

Preguntas frecuentes

¿Qué versión de Node.js debo usar en producción?

Usa la versión LTS activa de Node.js, que recibe correcciones de seguridad durante más tiempo. Consulta en el calendario oficial de Node.js cuál es antes de fijar la versión en tu proyecto.

¿Necesito PM2 para mantener la aplicación en línea?

Depende del entorno. En un servidor que administras tú, un supervisor de procesos reinicia la app si se cae. En plataformas que ya reinician el proceso por su cuenta, PM2 suele ser innecesario.

¿Debo subir la carpeta node_modules?

Normalmente no. Sube package.json y package-lock.json y deja que el entorno instale las dependencias con npm ci. La carpeta node_modules puede ser enorme y depende del sistema operativo.

¿Puedo hospedar una aplicación Node.js escrita en TypeScript?

Sí. Puedes compilar a JavaScript antes del deploy o usar una plataforma que ejecute TypeScript directamente. Ejecutar el código compilado suele ser la opción más predecible.

Siguiente artículoCómo hospedar FastAPI, Flask o DjangoPara hospedar FastAPI, Flask o Django, ejecuta la aplicación con un servidor de producción (Uvicorn o Gunicorn) en 0.0.0.0. Comandos, ajustes de Django y errores.