OffVPSVPS OFFSHORESoporte

Desplegar una aplicación

Dé a su primera API una publicación repetible.

Haz que una pequeña API funcione localmente, haz que su proceso sea recuperable, y solo entonces conecta su hostname y HTTPS. Mantén explícitos la ruta de release y el camino de vuelta.

Guía de campo de OffVPS · Revisado · 5 min de lectura

Prepara un entorno pequeño y controlado

Este procedimiento apunta a un sistema Linux con systemd, una cuenta de administrador, un runtime Node.js 24 LTS ya instalado, curl y el servicio de sistema empaquetado de Caddy. Confirma primero las versiones instaladas y las rutas de los paquetes; La página de releases de Node identifica las líneas de release compatibles. La instalación y el aprovisionamiento del proveedor son tareas separadas. Usa una máquina que controles y mantén disponible una sesión SSH probada y una ruta de recuperación. Los nombres first-api y /opt/first-api deben estar sin usar antes de crear este ejemplo.

Para HTTPS también necesitas un dominio que controles, registros A/AAAA correctos y permiso para exponer tráfico web. api.example.com a continuación es un ejemplo reservado: reemplázalo con tu propio hostname. Esta API deliberadamente no contiene base de datos, autenticación ni datos de clientes. Demuestra un proceso repetible, no un producto completo ni un despliegue OffVPS probado.

Verifica el runtime y crea un release

command -v node &&
readlink -f "$(command -v node)" &&
node --version

El resto del ejemplo asume que el ejecutable compartido verificado es /usr/bin/node. Si el tuyo difiere, reemplaza esa ruta en cada comprobación y en ExecStart. Un runtime dentro del home privado de tu usuario de inicio de sesión no está disponible automáticamente para un servicio de sistema. Crea la cuenta de servicio y un directorio de release propiedad de root, deteniéndote si se encuentra una cuenta o ruta existente inesperada.

sudo useradd --system --user-group --home-dir /opt/first-api \
  --shell /usr/sbin/nologin first-api &&
sudo install -d -o root -g root -m 0755 /opt/first-api/releases/001

Usando tu editor con acceso administrativo, guarda lo siguiente como /opt/first-api/releases/001/server.mjs, propiedad de root y legible por el usuario del servicio. El release contiene solo este archivo; no hay dependencias de paquetes ni secretos.

import http from 'node:http';

const port = Number(process.env.PORT || 3000);
if (!Number.isInteger(port) || port < 1024 || port > 65535) {
  throw new Error('PORT must be an integer from 1024 to 65535');
}
const server = http.createServer((req, res) => {
  res.setHeader('Content-Type', 'application/json; charset=utf-8');
  res.setHeader('Cache-Control', 'no-store');
  if (req.method !== 'GET') {
    res.writeHead(405, { Allow: 'GET' });
    res.end(JSON.stringify({ error: 'method_not_allowed' }));
    return;
  }
  if (req.url === '/healthz') {
    res.writeHead(200);
    res.end(JSON.stringify({ status: 'ok', release: '001' }));
  } else if (req.url === '/api/message') {
    res.writeHead(200);
    res.end(JSON.stringify({ message: 'A small app, running clearly.' }));
  } else {
    res.writeHead(404);
    res.end(JSON.stringify({ error: 'not_found' }));
  }
});
server.requestTimeout = 10000;
server.headersTimeout = 10000;
server.keepAliveTimeout = 5000;
server.listen(port, '127.0.0.1');
process.on('SIGTERM', () => {
  server.close(() => process.exit(0));
  setTimeout(() => process.exit(1), 10000).unref();
});

La dirección de loopback explícita mantiene el listener de la API en el propio servidor. Caddy será su punto de entrada público. La API HTTP de Node documenta el manejo de solicitudes, los timeouts y el apagado del servidor. Comprueba el archivo como la cuenta que realmente lo ejecutará:

sudo -u first-api /usr/bin/node --check /opt/first-api/releases/001/server.mjs

Dale al proceso una definición de servicio

Guarda /etc/systemd/system/first-api.service con este contenido:

[Unit]
Description=First API learning release
After=network.target
StartLimitIntervalSec=60
StartLimitBurst=5

[Service]
Type=simple
User=first-api
Group=first-api
WorkingDirectory=/opt/first-api/releases/001
ExecStart=/usr/bin/node /opt/first-api/releases/001/server.mjs
Environment=NODE_ENV=production
Environment=PORT=3000
Restart=on-failure
RestartSec=5
TimeoutStopSec=15
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true

[Install]
WantedBy=multi-user.target

La unidad usa un usuario dedicado, un ejecutable explícito y un directorio de trabajo versionado. La recuperación automática tiene límite de frecuencia; una parada intencional del servicio no dispara Restart=on-failure. Consulta systemd.service. Las restricciones del sistema de archivos encajan con esta API de solo lectura; una aplicación que escribe datos necesita almacenamiento escribible con alcance deliberado. Los secretos no pertenecen a estas líneas de entorno. Consulta ajustes de ejecución de systemd.

sudo systemd-analyze verify /etc/systemd/system/first-api.service &&
sudo systemctl daemon-reload &&
sudo systemctl start first-api.service &&
sudo systemctl status first-api.service --no-pager &&
curl --fail --show-error http://127.0.0.1:3000/healthz

El && las protecciones detienen la secuencia pegada cuando un comando falla. Resuelve las advertencias de validación antes de iniciar, y no continúes al siguiente bloque tras una comprobación fallida. Verificación de la unidad puede detectar problemas de sintaxis y de ejecutable, pero una comprobación exitosa no es prueba de una aplicación en funcionamiento. La respuesta de salud esperada es {"status":"ok","release":"001"}. Si falla, inspecciona sudo journalctl -u first-api.service -n 50 --no-pager antes de reiniciar repetidamente.

Añade la ruta HTTPS después de la comprobación local

Haz una copia de seguridad de la configuración existente de Caddy con un nombre de archivo sin usar. Añade este bloque a /etc/caddy/Caddyfile sin reemplazar sitios no relacionados:

api.example.com {
    reverse_proxy 127.0.0.1:3000
}

Para el flujo estándar de dominio público, el hostname debe resolverse al servidor, los puertos 80/443 deben llegar a Caddy, y el almacenamiento de certificados de Caddy debe permanecer escribible y persistente. Comprueba cada ruta A/AAAA publicada. Mantén intacto el acceso SSH y mantén privado el puerto 3000. Estos requisitos provienen de HTTPS automático de Caddy; la sintaxis del upstream está documentada en reverse_proxy.

sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile &&
sudo systemctl reload caddy.service

Recarga solo tras una validación exitosa; caddy validate comprueba la configuración adaptada. El flujo de trabajo del servicio empaquetado se describe en la guía de servicio de Caddy. Desde un cliente separado, solicita https://api.example.com/healthz usando tu hostname real. Espera una conexión TLS de confianza y la misma respuesta del release, sin omitir las comprobaciones de certificado. Luego verifica que /api/message devuelve su mensaje y una ruta desconocida devuelve 404.

Conserva un release conocido y un punto de parada seguro

Después de que ambas comprobaciones funcionen, habilita la API para futuros arranques con sudo systemctl enable first-api.service. Registra la versión del runtime, el archivo fuente, la unidad y la configuración de Caddy. Para el siguiente release, crea un nuevo directorio numerado, comprueba su sintaxis como el usuario del servicio, actualiza ambas rutas de unidad, recarga systemd y reinicia la API. Mantén el directorio anterior hasta que se acepte el nuevo release.

Para detener este ejemplo, usa sudo systemctl stop first-api.service. Caddy reportará un fallo de upstream mientras esa ruta siga configurada; elimina solo esta ruta y valida/recarga Caddy al retirarla. Haz rollback de un release de código fallido seleccionando las rutas de unidad anteriores y repitiendo las comprobaciones. Una migración posterior de base de datos necesita su propio plan de recuperación. Continúa con la ruta de solicitud de DNS a aplicación o encontrar por qué se detuvo una app.

Documentación utilizada

Referencias principales para esta página. Consulta la documentación de la versión instalada en tu propio entorno.