Su app funciona en localhost. Ahora quiere compartirla con un dominio real, sin tener que dejar su portátil encendido. Las piezas que faltan son un VPS, un proceso de aplicación gestionado, DNS y un proxy inverso con HTTPS.
Esta guía le muestra ese camino con una pequeña app Node.js. Claude Code puede ayudarle a inspeccionar su proyecto y a preparar la configuración para que usted la revise; los cambios en el servidor siguen siendo responsabilidad suya. El modelo no se instala en el VPS, y el acceso a Claude y su facturación son independientes del hosting.
Recorrido animado. El panel es una demostración ficticia, no una monitorización de infraestructura real.
Vea el resumen del despliegue
Ver el resumen del despliegue (vídeo en inglés).
Lo que probamos realmente: un panel real creado originalmente por Claude Code fue desplegado por nuestro operador en un VPS Linux desechable. Comprobamos el DNS público, el HTTPS de confianza, la redirección de HTTP a HTTPS, los procesos de la aplicación y del proxy sin privilegios de root, y el acceso tras reiniciar el servicio de la aplicación. No hubo una nueva ejecución de despliegue autónoma de Claude. El prompt sugerido que aparece más abajo es instructivo, no una respuesta grabada.
Antes de empezar
- Un VPS Linux con systemd y una cuenta SSH con acceso sudo. Use primero un VPS de pruebas; no sobrescriba los servicios ni la configuración web de un sitio existente.
- Un entorno de ejecución Node.js LTS compatible instalado para el servidor, y Caddy instalado como servicio del sistema. Instale estos requisitos previos antes de seguir los pasos del despliegue.
- Un dominio o subdominio cuyos registros DNS pueda editar.
- Una app pequeña y autónoma. Nuestro ejemplo sirve
index.htmlmedianteserver.mjs, sin dependencias de paquetes ni base de datos.
Las aplicaciones basadas en frameworks necesitan su propio comando de compilación, su comando de arranque, sus dependencias y un plan de almacenamiento. No dé por hecho que un proyecto con Next.js, Python o base de datos puede usar este ejemplo de dos archivos sin cambios.
1. Pida a Claude Code que inspeccione el proyecto antes de modificarlo
Empiece en el directorio de su proyecto local. Una buena primera petición es:
Inspect this project without changing files. Identify its runtime,
build and start commands, configuration and secrets. Propose a Linux
VPS deployment using a non-root systemd service and Caddy HTTPS.
Bind the app to 127.0.0.1:3000. Explain each proposed file and
verification step. Do not connect to a server or edit DNS.
Los flujos de trabajo documentados de Claude Code incluyen planificar antes de editar. Revise los cambios propuestos en lugar de aceptar a ciegas los comandos de despliegue. Mantenga las claves de API, las credenciales de producción y las claves SSH privadas fuera de los prompts y de los repositorios; el acceso de solo lectura no hace confidenciales los archivos sensibles.
2. Prepare y transfiera la aplicación
Para este ejemplo mínimo concreto, guarde su HTML como index.html y use el siguiente servidor. Escucha por defecto en loopback y no expone archivos arbitrarios del proyecto:
import {createServer} from 'node:http';
import {readFileSync} from 'node:fs';
import {fileURLToPath} from 'node:url';
const page=readFileSync(new URL('./index.html',import.meta.url));
export function createAppServer(){
return createServer((req,res)=>{
const pathname=new URL(req.url,'http://localhost').pathname;
if(pathname!=='/' && pathname!=='/index.html'){
res.writeHead(404,{'Content-Type':'text/plain; charset=utf-8'});res.end('Not found');return;
}
res.writeHead(200,{'Content-Type':'text/html; charset=utf-8','X-Content-Type-Options':'nosniff'});res.end(page);
});
}
if(process.argv[1]===fileURLToPath(import.meta.url)){
const host=process.env.HOST||'127.0.0.1';const port=Number(process.env.PORT||3000);
const server=createAppServer();server.listen(port,host,()=>console.log(`Demo app listening on ${host}:${port}`));
process.on('SIGTERM',()=>server.close(()=>process.exit(0)));
}
Ejecútelo en local con node server.mjs, abra http://127.0.0.1:3000 y deténgalo antes de continuar. En un VPS que ya use el puerto 3000, elija otro puerto privado y utilícelo de forma coherente en el servicio y en el proxy.
Desde el directorio de su proyecto local, sustituya deploy y VPS_IP por su cuenta SSH y la dirección real de su servidor:
ssh deploy@VPS_IP 'mkdir -p ~/my-app'
scp index.html server.mjs deploy@VPS_IP:~/my-app/
En el servidor, instale estos dos archivos no secretos en un directorio de aplicación legible:
sudo install -d -m 0755 /opt/my-app
sudo install -m 0644 ~/my-app/index.html ~/my-app/server.mjs /opt/my-app/
command -v node
Anote la ruta absoluta del ejecutable de Node. Debe ser accesible fuera de su shell de inicio de sesión y de su directorio personal. Una instalación de un gestor de versiones solo para el usuario bajo /home no es adecuada para el servicio reforzado que se describe a continuación. No copie nunca un .env de desarrollo, una clave privada ni un volcado de base de datos en un directorio web público.
3. Ejecute la app como un servicio systemd sin root
Use sudoedit /etc/systemd/system/my-app.service. Sustituya /ABSOLUTE/PATH/TO/node por la ruta real del ejecutable a nivel de servidor que acaba de comprobar:
[Unit]
Description=My Node web app
After=network.target
[Service]
Type=simple
DynamicUser=true
WorkingDirectory=/opt/my-app
ExecStart=/ABSOLUTE/PATH/TO/node /opt/my-app/server.mjs
Environment=NODE_ENV=production
Environment=HOST=127.0.0.1
Environment=PORT=3000
Restart=on-failure
RestartSec=2
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
[Install]
WantedBy=multi-user.target
Los controles de ejecución de systemd documentan la cuenta dinámica y los ajustes de refuerzo. Este perfil es adecuado para nuestra app de solo lectura; las apps que escriben subidas o bases de datos requieren un almacenamiento persistente y unos permisos configurados de forma deliberada.
sudo systemctl daemon-reload
sudo systemctl enable --now my-app.service
sudo systemctl status my-app.service --no-pager
curl -fsS http://127.0.0.1:3000/
Resuelva los errores de la aplicación antes de tocar el DNS. Un servicio habilitado está configurado para arrancar con el sistema, pero eso no demuestra que se haya completado una prueba de reinicio.
4. Apunte un subdominio al VPS
Cree un registro A para el nombre de host que elija, como app.example.com, que apunte a la dirección IPv4 pública de su VPS. Con Cloudflare, nuestra demostración con servidor directo usó solo DNS y un TTL automático. No copie la IP de nuestra demostración en los registros de su propio sitio.
Compruebe la respuesta pública con dig +short A app.example.com. Inspeccione también dig +short AAAA app.example.com: si existe un registro IPv6, debe llegar al mismo despliegue operativo por IPv6. El DNS solo asigna una dirección; ni inicia su app ni configura HTTPS.
5. Ponga Caddy delante de la app privada
Mantenga el servicio de Node en 127.0.0.1:3000. Haga accesibles los puertos web públicos 80 y 443 a través del firewall del proveedor y del firewall del host, sin quitar su acceso SSH. No exponga el puerto 3000 a internet.
Añada este bloque de sitio a la configuración de su servicio Caddy instalado, sustituyendo el nombre de host de ejemplo. Conserve los bloques de otros sitios que no estén relacionados:
app.example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:3000
}
La directiva reverse_proxy reenvía las solicitudes públicas a la aplicación privada. Con un nombre de host público válido y unas condiciones de validación de certificados que funcionen, el HTTPS automático de Caddy obtiene y renueva los certificados y redirige HTTP a HTTPS.
Para el servicio de sistema estándar de Caddy y su ruta de configuración habitual:
sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
sudo systemctl reload caddy
Nuestro VPS de grabación usó un servicio de Caddy dedicado y rutas de configuración aisladas en lugar del servicio compartido estándar que se muestra arriba. El principio de enrutamiento es el mismo; estas rutas y nombres de servicio pensados para el lector son adaptaciones, no una transcripción literal de la instalación.
6. Verifique el resultado público y el reinicio del servicio
curl -I http://app.example.com/
curl -I https://app.example.com/
sudo systemctl restart my-app.service
curl --retry 5 --retry-delay 1 --retry-connrefused -I https://app.example.com/
Compruebe que hay una redirección HTTP a HTTPS, una respuesta HTTPS correcta y un certificado válido en el navegador. No use curl -k para ocultar errores de confianza. Abra la página desde otra red, no solo desde dentro del VPS.
Si falla la comprobación del reinicio, hay que investigarlo: revise journalctl -u my-app.service -n 50 --no-pager y los logs del servicio de Caddy. Los errores de DNS, un IPv6 incorrecto, una app detenida, un puerto de upstream equivocado y una validación de certificados bloqueada son problemas distintos.
Verificamos el reinicio del servicio de la aplicación, no el reinicio del VPS ni una configuración de alta disponibilidad. Antes de alojar una aplicación de negocio real, planifique también las actualizaciones, las copias de seguridad, la monitorización, la gestión de secretos y los datos persistentes.
Del proyecto local a una dirección pública
El camino completo es sencillo: preparar el proyecto, transferirlo al VPS, mantenerlo en ejecución como un servicio gestionado sin root, apuntar el DNS al servidor y terminar el HTTPS público en el proxy inverso. Claude Code puede ayudar a preparar y explicar estas piezas, pero la revisión y las comprobaciones operativas siguen siendo importantes.
Descubra los planes de VPS Linux de SharpHeberg cuando esté listo para poner en línea un proyecto local. El hosting aporta el entorno de servidor, no una suscripción a Claude incluida ni un modelo de Claude alojado localmente.