---
title: "Cómo Desplegar Astro en un VPS con CloudPanel (Guía 2026)"
description: "Aprende a desplegar Astro en un VPS con CloudPanel: compila un sitio estático, añade SSL y el CDN de Cloudflare, automatiza los rebuilds y revierte despliegues con seguridad en tu servidor."
date: 2026-09-28
categories: ["web-development"]
tags: ["astro","cloudpanel","vps"]
---

import Button from "@components/widgets/Button.astro";
import Notice from "@components/widgets/Notice.astro";
import ListCheck from "@components/widgets/ListCheck.astro";
import Accordion from "@components/widgets/Accordion.astro";
import Tabs from "@components/widgets/Tabs.astro";
import Tab from "@components/widgets/Tab.astro";
import YouTubeEmbed from "@components/widgets/YouTubeEmbed.astro";
import { Picture, Image } from "astro:assets";
import img1 from "@assets/images/23/cloudpanel-change-root.jpeg";
import img2 from "@assets/images/23/cloudflare_dns.jpeg";
import deployPipeline from "@assets/images/23/astro-deploy-pipeline.svg";
import deployRollback from "@assets/images/23/astro-deploy-rollback.svg";

Desplegar Astro en un VPS con CloudPanel te da lo único que las plataformas gratuitas nunca ofrecen: control total. Tu build, tus cabeceras de caché, tu ruta de rollback y sin límites de minutos de build esperando para sorprenderte a final de mes. El hosting gratuito como [Cloudflare Pages](https://www.bitdoze.com/deploy-astrojs-cloudflare/), Vercel y Netlify funciona bien hasta que deja de hacerlo: límites de ancho de banda, límites de build o lock-in de plataforma.

CloudPanel es un panel de hosting gratuito que corre sobre Nginx. Gestiona la administración de sitios, el SSL y el aislamiento de usuarios a través de una interfaz web. Despliegas Astro como un **Static HTML site** y dejas que CloudPanel sirva los archivos compilados directamente desde Nginx. Ningún proceso de Node.js corre en runtime, lo que significa que un VPS pequeño y barato es suficiente. Y desde que Astro 7 salió en junio de 2026 con builds entre un 15 y un 61 % más rápidos ([nuestro benchmark de builds de Astro 7](https://www.bitdoze.com/astro-7-faster-builds/)), los rebuilds en esa máquina pequeña son más rápidos que antes.

<Button link="https://go.bitdoze.com/do" text="DigitalOcean 100 $ gratis" />
<Button link="https://go.bitdoze.com/vultr" text="Vultr 100 $ gratis" />
<Button link="https://go.bitdoze.com/hetzner" text="Hetzner 20 € gratis" />
<Button link="https://go.bitdoze.com/hostinger-vps" text="Hostinger VPS" />

_Los precios de los VPS subieron en todos los proveedores en 2026. Si estás replanteándote una máquina alquilada, mira [qué cambió y cuándo gana un mini PC](/vps-price-increases/)._

## Prerequisitos

Antes de empezar, esto es lo que ya tiene que existir:

<ListCheck>
<ul>
<li><strong>Un servidor VPS.</strong> 1 GB de RAM funciona con swap, 2 GB es cómodo. Orden de magnitud: de 5 a 10 $/mes. Cualquier proveedor vale. Yo compararía opciones con [DigitalOcean vs Vultr vs Hetzner](https://www.bitdoze.com/digitalocean-vs-vultr-vs-hetzner/) antes de comprar.</li>
<li><strong>CloudPanel instalado.</strong> Sigue la guía [Instalar CloudPanel y alojar apps Node.js](https://www.bitdoze.com/install-cloudpanel-host-nodejs/) si aún no lo tienes. Si todavía estás comparando paneles, mira los [mejores paneles de servidor self-hosted](https://www.bitdoze.com/best-self-hosted-panels/).</li>
<li><strong>Un nombre de dominio</strong> con DNS que puedas editar.</li>
<li><strong>Acceso SSH</strong> al servidor como root. Ya que estás, [refuerza el acceso SSH de tu servidor](https://www.bitdoze.com/secure-ssh-server-linux/). Un VPS público recibe escaneos a los pocos minutos de arrancar.</li>
</ul>
</ListCheck>

El stack que monta esta guía: VPS Linux + CloudPanel (Nginx) + NVM + Node 24 + Cloudflare delante. Todo lo demás es opcional.

<Notice type="info" title="Mantén CloudPanel actualizado">
El changelog de CloudPanel muestra un historial real de CVEs, incluidos fixes de escalada de privilegios entre 2022 y 2024. La versión actual es v2.5.4 (2026-07-01). Ejecuta la rutina de actualización del panel tras instalarlo y no lo dejes obsoleto. Estás exponiendo una UI de administración en una IP pública.
</Notice>

También puedes desplegar Astro en un VPS con [Coolify](https://www.bitdoze.com/coolify-install-heroku-alternative/) o [EasyPanel](/es/desplegar-astro-easypanel/) si prefieres otro panel. Para stacks pesados en Docker son buenas opciones. Para un sitio Astro puramente estático, CloudPanel es el camino más simple.

> Si quieres monitorizar CPU, memoria y espacio en disco de tu servidor, mira: [Cómo monitorizar recursos de servidor y Docker](https://www.bitdoze.com/sever-monitoring/)

## Videoguía

<YouTubeEmbed
  url="https://www.youtube.com/embed/kMtVBvO87pg"
  label="Cómo Desplegar Astro en un VPS con CloudPanel (Guía 2026)"
/>

<Notice type="warning" title="El vídeo muestra una UI anterior de CloudPanel">
La videoguía se grabó en 2023. El flujo es el mismo, pero la UI de CloudPanel v2.5.x difiere en algunos puntos. Los pasos escritos de abajo están al día para CloudPanel v2.5.4 (2026-07-01). Fíate de ellos si las capturas no coinciden con lo que ves.
</Notice>

## Desplegar Astro.js en un VPS con CloudPanel

Hay dos enfoques en CloudPanel: un **Static HTML site** (recomendado para salida puramente estática) o un **Node.js site**. El enfoque Static HTML es más simple y barato de correr: CloudPanel sirve los archivos compilados directamente desde Nginx sin ningún proceso Node.js en runtime. Esta es la secuencia completa: sitio, DNS, SSL, Node para compilar, proyecto, build y automatización.

### Paso 1: Añade un sitio Static HTML en CloudPanel

1. Inicia sesión en tu panel de administración de CloudPanel.
2. Ve a **Sites** > **Add Site** > **Create a Static HTML Site**. (La documentación actual de CloudPanel para todos los tipos de sitio está en [cloudpanel.io/docs/v2/frontend-area/add-site/](https://www.cloudpanel.io/docs/v2/frontend-area/add-site/).)
3. Introduce tu nombre de dominio y crea las credenciales del usuario del sitio.
4. Cuando el sitio esté creado, edítalo y cambia el **Root Directory** para que apunte al directorio `dist`:

```
htdocs/www.yourdomain.com/dist
```

Astro genera su build de producción en `dist/` por defecto (`outDir: './dist'`). CloudPanel tiene que servir desde ese directorio, no desde la raíz del proyecto.

<Notice type="warning" title="Sirve siempre desde dist/, nunca desde la raíz del proyecto">
Servir la raíz del proyecto expondría tu directorio `.git/`, `node_modules/`, los archivos fuente y el `.env` a través de Nginx. Apunta el root directory a `dist/` y nada más será accesible. Si obtienes un 403 o un 404 tras un build "correcto", este ajuste es lo primero que debes revisar.
</Notice>

<Picture formats={["webp"]} fallbackFormat="webp"
  src={img1}
  alt="Ajustes del sitio en CloudPanel con el Root Directory configurado a htdocs/www.yourdomain.com/dist para un build de Astro"
/>

CloudPanel también crea la redirección www↔non-www y fuerza HTTP→HTTPS por ti. No necesitas escribir esas reglas a mano.

### Paso 2: Apunta el DNS a tu VPS

Añade un registro A en tu proveedor de DNS apuntando tu dominio a la dirección IP del VPS. Si usas Cloudflare, añade el registro A allí y activa el proxy para las ventajas del CDN y la protección DDoS.

```bash
dig +short www.yourdomain.com
# With Cloudflare proxy on, you'll see Cloudflare IPs (that's expected)
# With proxy off (DNS only), you should see your VPS IP
```

<Picture formats={["webp"]} fallbackFormat="webp"
  src={img2}
  alt="Registro A de DNS en Cloudflare apuntando el dominio a la IP del VPS con el proxy de Cloudflare activado"
/>

<Notice type="warning" title="Configura el SSL/TLS de Cloudflare en Full (strict)">
Con un certificado Let's Encrypt en el origen (siguiente paso) y Cloudflare delante, pon el modo SSL/TLS en **Full (strict)** en el dashboard de Cloudflare. El modo **Flexible** por defecto hace que Cloudflare hable HTTP con tu origen mientras sirve HTTPS a los visitantes. Eso provoca bucles de redirección con las reglas de HTTPS forzado de Nginx, o errores 522/525. Full (strict) verifica el certificado del origen y el bucle desaparece.
</Notice>

### Paso 3: Genera un certificado SSL de Let's Encrypt

En CloudPanel, ve a la sección **SSL/TLS** de tu sitio y haz clic en **New Let's Encrypt Certificate**. CloudPanel gestiona la generación del certificado y la configuración de Nginx automáticamente.

Verifícalo antes de seguir:

```bash
curl -I https://www.yourdomain.com
# Expect: HTTP/2 200 (the CloudPanel placeholder page is fine at this stage)

curl -I http://www.yourdomain.com
# Expect: 301 redirect to https://www.yourdomain.com
```

Si `curl` te da un aviso de certificado, el certificado no se emitió. Comprueba que el registro DNS del Paso 2 ya resuelve antes de reintentar.

### Paso 4: Instala Node.js 24 LTS con NVM

Astro necesita Node.js para compilar el sitio. CloudPanel crea usuarios de Linux aislados por sitio, así que instala Node.js bajo el usuario del sitio, no como root. Entra por SSH a tu VPS y cambia al usuario del sitio:

```bash
ssh root@your-server-ip
sudo su - www.yourdomain.com
```

Instala NVM (Node Version Manager). La v0.40.8 es la versión actual a septiembre de 2026:

```bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.8/install.sh | bash
source ~/.bashrc
```

Instala Node.js 24, la línea LTS activa:

```bash
nvm install 24
nvm alias default 24
```

Verifica la instalación:

```bash
node -v
# Should output v24.x.x

nvm ls
# Should show v24.x.x as default
```

<Notice type="info" title="Astro 7 necesita Node >= 22.12.0">
La línea de versiones actual de Astro (7.x, 7.3.5 en npm a octubre de 2026) requiere `node >=22.12.0`. Node 24 lo cumple y tiene soporte hasta 2028-04-30. Node 22 aún funciona (LTS de mantenimiento hasta 2027-04-30). `nvm install 22` instala el 22.x más reciente, que supera el mínimo de 22.12.0, pero 24 te da un año extra de soporte. Instala 24 salvo que tengas una razón para no hacerlo.
</Notice>

### Paso 5: Clona y configura tu proyecto Astro

Elimina los archivos de ejemplo que CloudPanel crea por defecto y coloca tu proyecto. El nombre del directorio debe coincidir con lo que CloudPanel espera (`www.yourdomain.com`).

<Tabs>
<Tab name="Clonar un repo existente">
```bash
cd htdocs
rm -rf www.yourdomain.com
git clone git@github.com:your-username/your-astro-repo.git www.yourdomain.com
cd www.yourdomain.com
npm install
```

Para un repo privado, `git@github.com:...` necesita una clave SSH para el usuario del sitio. Crea una y añádela al repo como deploy key de solo lectura:

```bash
ssh-keygen -t ed25519 -C "deploy-www.yourdomain.com"
cat ~/.ssh/id_ed25519.pub
# Copy the output, then in GitHub: repo Settings > Deploy keys > Add deploy key
# Paste it and leave "Allow write access" unchecked
```

Si necesitas una guía más detallada de claves SSH con GitHub, mira [cómo vincular GitHub con una clave SSH](https://www.bitdoze.com/link-github-with-ssh-maco-linux/).
</Tab>
<Tab name="Crear un proyecto nuevo">
```bash
cd htdocs
rm -rf www.yourdomain.com
npm create astro@latest www.yourdomain.com
cd www.yourdomain.com
npm install
```

Esto sigue generando proyectos Astro 7 correctamente. Para un tema de blog ya hecho, mira [Bitdoze Astro Theme](https://github.com/bitdoze/bitdoze-astro-theme) o [AstroWind](https://github.com/onwidget/astrowind).
</Tab>
</Tabs>

<Notice type="warning" title="¿Permission denied (publickey)?">
Ese error en `git clone` o `git pull` significa que el usuario del sitio no tiene ninguna clave SSH registrada en GitHub. Los pasos de la deploy key de arriba son la solución. Prueba con `ssh -T git@github.com` como usuario del sitio. Deberías ver "Hi username/repo! You've successfully authenticated".
</Notice>

### Paso 6: Compila el sitio estático de Astro

Cuando tu sitio esté configurado y el contenido en su lugar, compila la versión de producción:

```bash
npm run build
```

Esto genera los archivos estáticos en `dist/`. Como el root directory de CloudPanel ya apunta a `dist/`, el sitio entra en producción en el momento en que termina el build.

Verifica:

```bash
curl -I https://www.yourdomain.com
# Expect: HTTP/2 200 and content-type: text/html

curl -I https://www.yourdomain.com/_astro/*.css
# Expect: 200. Astro emits hashed assets under /_astro/
```

<Notice type="warning" title="¿OOM-killed en un VPS de 1 GB?">
`npm install` y los builds de Astro pueden consumir más de 1 GB de RAM, y el OOM killer del kernel abortará el build encantado. Redimensiona a 2 GB o añade de 1 a 2 GB de swap primero:

```bash
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
```

Los builds más rápidos de Astro 7 (de un 15 a un 61 %, [nuestro benchmark de builds de Astro 7](https://www.bitdoze.com/astro-7-faster-builds/)) acortan la ventana pero no eliminan el mínimo de memoria. Para sitios pesados, mira [optimización de la velocidad de build de Astro](https://www.bitdoze.com/astro-ssg-build-optimization/).
</Notice>

<Notice type="info" title="¿Recompilando un sitio viejo de Astro 4/5/6 en Astro 7?">
El nuevo compilador Rust de Astro 7 ya no corrige el markup silenciosamente. Las etiquetas sin cerrar y los atributos sin terminar ahora son errores de build duros, y los espacios en blanco entre elementos inline se colapsan al estilo JSX (los saltos de línea ya no se renderizan como un espacio; usa `{ ' ' }` donde necesites uno). Si te encuentras un muro de errores de markup después de que `npm install` trajera Astro 7, ese es el motivo. Ruta de actualización: `npx @astrojs/upgrade`, y luego corrige las líneas reportadas.
</Notice>

### Paso 7: Automatiza rebuilds y despliegues

Hacer pull y build a mano cansa al tercer push de contenido. Todo el pipeline es pequeño:

<Image src={deployPipeline} alt="Pipeline de despliegue de Astro: un git push dispara un script de deploy que ejecuta git pull, npm ci y npm run build en dist, que el Nginx de CloudPanel sirve por HTTPS a través de Cloudflare al visitante" />

Escribe un script de deploy mínimo como usuario del sitio, en `~/deploy.sh`:

```bash
#!/usr/bin/env bash
set -euo pipefail

cd ~/htdocs/www.yourdomain.com
git pull
npm ci
npm run build
```

```bash
chmod +x ~/deploy.sh
```

Usa `npm ci` en lugar de `npm install`. Instala exactamente lo que hay en `package-lock.json`, así que un rebuild mañana se comporta como el rebuild de hoy. Ahora elige un disparador:

<Tabs>
<Tab name="Cron de CloudPanel">
En CloudPanel, ve a **Cron Jobs** del usuario del sitio y ejecuta el script en un horario, por ejemplo cada 5 minutos:

```bash
*/5 * * * * /home/www.yourdomain.com/deploy.sh >> /home/www.yourdomain.com/deploy.log 2>&1
```

Para evitar rebuilds innecesarios, añade un check de fetch al principio del script:

```bash
cd ~/htdocs/www.yourdomain.com
git fetch -q
[ "$(git rev-parse HEAD)" = "$(git rev-parse @{u})" ] && exit 0
```

El build solo corre cuando el remoto tiene commits nuevos. Tosco, fiable, sin puertos de entrada.
</Tab>
<Tab name="Webhook de GitHub">
Añade un webhook en GitHub (**Settings** > **Webhooks** del repo): URL de payload apuntando a un listener mínimo en el VPS que ejecuta `deploy.sh`, content type `application/json`, evento "Just the push event".

El listener puede ser un servicio Python de 20 líneas o un script de shell tras un endpoint desechable. Apúntalo a una ruta que no sea el vhost de tu sitio web y ponle un secreto compartido. Si no, cualquiera que encuentre la URL puede disparar builds en tu servidor. Las deploy keys son de solo lectura; el listener del webhook es la única superficie de entrada que añades.
</Tab>
<Tab name="DPLOY">
CloudPanel incluye una herramienta de despliegue nativa de Git, [DPLOY](https://www.cloudpanel.io/docs/v2/dploy/installation/). Haces push a una rama, DPLOY hace pull y ejecuta tu comando de build. Es la opción "nativa" si quieres que el panel sea dueño del flujo de deploy en lugar de cron más un script de shell.

Revisa la página de docs para los pasos de instalación actuales. La documentación sigue activa y corresponde a la v2.
</Tab>
</Tabs>

Uses el disparador que uses: la salida del build acaba en los logs de cron o en el journal del webhook. Cuando un deploy "silenciosamente" no ocurre, ese log es el primer sitio donde mirar. El script es también donde se engancha la salvaguarda de rollback (lo vemos a continuación).

<Button link="https://go.bitdoze.com/do" text="DigitalOcean 100 $ gratis" />
<Button link="https://go.bitdoze.com/vultr" text="Vultr 100 $ gratis" />
<Button link="https://go.bitdoze.com/hetzner" text="Hetzner 20 € gratis" />
<Button link="https://go.bitdoze.com/hostinger-vps" text="Hostinger VPS" />

## Revertir un deploy fallido

`npm run build` vacía `dist/` antes de escribir la salida nueva. Si el build falla a medias (error de markup, OOM kill, dependencia rota), puedes quedarte sirviendo nada. La solución es aburrida: guarda el build bueno anterior hasta que el nuevo esté probado.

<Image src={deployRollback} alt="Diagrama de intercambio para rollback: antes de un rebuild mueve dist a dist.last-known-good, ejecuta el build que vacía dist primero y puede fallar, y luego en caso de éxito elimina el backup o en caso de fallo devuelve el backup para que el sitio siga sirviendo" />

Extiende el script de deploy con el intercambio:

```bash
#!/usr/bin/env bash
set -euo pipefail

cd ~/htdocs/www.yourdomain.com
git pull
npm ci

# Keep the previous build until the new one is proven
if [ -d dist ]; then
  rm -rf dist.last-known-good
  mv dist dist.last-known-good
fi

if npm run build && [ -f dist/index.html ]; then
  rm -rf dist.last-known-good
else
  echo "Build failed or produced no index.html, restoring previous dist" >&2
  if [ -d dist.last-known-good ]; then
    rm -rf dist
    mv dist.last-known-good dist
  fi
  exit 1
fi
```

La comprobación `[ -f dist/index.html ]` atrapa el peor caso: un build que sale con código 0 pero emite un árbol de salida vacío o incorrecto. Si falla, el backup vuelve a su sitio y Nginx sigue sirviendo la versión anterior.

Esto es una estrategia de "última salida estática buena conocida", no despliegues versionados. Para un sitio estático en solitario es suficiente: el tiempo de restauración es un `mv`, y el único estado que importa (el repo git) está en otra parte.

## Verifica el despliegue y configura las cabeceras de caché

Ejecuta este checklist tras el primer deploy y tras cada rebuild. Lleva 30 segundos y atrapa la mayoría de lo que se rompe:

<ListCheck>
<ul>
<li><code>curl -I https://www.yourdomain.com</code> devuelve <strong>200</strong> con <code>content-type: text/html</code></li>
<li>Un asset hasheado carga: <code>curl -I https://www.yourdomain.com/_astro/*.css</code> devuelve 200</li>
<li><code>curl -I http://www.yourdomain.com</code> devuelve un 301 a HTTPS</li>
<li>www y non-www resuelven al mismo contenido (la redirección de CloudPanel)</li>
<li>Ver código fuente: la página referencia assets compilados bajo <code>/_astro/</code>, no archivos fuente en crudo</li>
</ul>
</ListCheck>

Luego el caché. Astro emite archivos hasheados bajo `/_astro/`, así que esos se pueden cachear fuerte: `Cache-Control: public, max-age=31536000, immutable`. El HTML debe tener una vida corta; si no, los visitantes siguen viendo el deploy anterior tras un rebuild.

La plantilla de vhost estático de CloudPanel gestiona el serving básico, pero si quieres cabeceras explícitas y predecibles, añádelas en el editor **Vhost** del sitio ([docs de vhost de CloudPanel](https://www.cloudpanel.io/docs/v2/frontend-area/vhost/)):

```nginx
location /_astro/ {
  add_header Cache-Control "public, max-age=31536000, immutable";
}
```

Deja el HTML con el manejo por defecto, o pon un `Cache-Control` corto (60 segundos es de sobra). En el lado de Cloudflare, pon Browser Cache TTL en "Respect Existing Headers" para que gane el origen. Así un rebuild se ve sin necesidad de purgar.

<Notice type="info" title="¿Los deploys no aparecen?">
Dos sospechosos habituales: un TTL de caché de HTML largo (solución arriba), o el edge de Cloudflare aún guardando la página vieja. Purga la caché de Cloudflare para las páginas HTML tras un deploy si lo necesitas en vivo inmediatamente. Los assets de `/_astro/` no necesitan purga. Sus nombres de archivo cambian cuando cambia su contenido.
</Notice>

Cuando el sitio esté en vivo, vigílalo: [monitoriza el uptime con Beszel y Uptime Kuma](https://www.bitdoze.com/beszel-uptime-kuma/) y [sigue los recursos del servidor](https://www.bitdoze.com/sever-monitoring/). Un sitio estático también puede caerse: expiración del certificado, disco lleno, mala configuración de Nginx en la próxima edición.

Si prefieres no poner Cloudflare delante, [Bunny.net](https://go.bitdoze.com/bunny) funciona como CDN edge drop-in sobre el mismo origen. Pull zone apuntando a tu VPS, la misma historia de caché inmutable de `/_astro/`.

## Solución de fallos comunes

<Notice type="warning" title="La mayoría se arreglan en 2 minutos">
Cada entrada de abajo es error → causa → solución. El recorrido completo de cada una está en el paso enlazado dentro.
</Notice>

<Accordion label="git clone o git pull falla con Permission denied (publickey)" group="faq" expanded="true">
El usuario del sitio no tiene ninguna clave SSH que GitHub conozca. Genera una (`ssh-keygen -t ed25519` como usuario del sitio), añade la clave pública al repo como **deploy key** de solo lectura y prueba con `ssh -T git@github.com`. Pasos completos en el Paso 5. Variante común: añadiste la clave a tu cuenta personal de GitHub pero el repo está en una organización que restringe las deploy keys. Usa una deploy key del repo en su lugar.
</Accordion>

<Accordion label="El build muere en un VPS pequeño (npm install o astro build)" group="faq" expanded="true">
Falta de memoria. En máquinas de 1 GB el OOM killer del kernel aborta el build sin ningún error útil de Astro. Solución: añade de 1 a 2 GB de swap (comandos en el Paso 6) o redimensiona el VPS a 2 GB. Comprueba `dmesg -T | grep -i oom` para confirmar que es OOM y no otra cosa. Los builds más rápidos de Astro 7 acortan la ventana pero no bajan el mínimo de memoria.
</Accordion>

<Accordion label="Bucle de redirección o errores 522/525 tras Cloudflare" group="faq">
El modo SSL/TLS de Cloudflare probablemente está en **Flexible**, así que Cloudflare habla HTTP con tu origen mientras Nginx fuerza HTTPS. Eso es una redirección infinita. Pon SSL/TLS en **Full (strict)** con el certificado Let's Encrypt en el origen. Detalles en el Paso 2.
</Accordion>

<Accordion label="Errores de build duros tras actualizar a Astro 7" group="faq">
El nuevo compilador Rust ya no corrige el markup silenciosamente: etiquetas sin cerrar y atributos sin terminar son errores de build, y los espacios en blanco inline se comportan al estilo JSX. Corrige las líneas reportadas, o ejecuta `npx @astrojs/upgrade` y trabaja la lista. Detalles en el Paso 6.
</Accordion>

<Accordion label="El sitio muestra 404 o 403 tras un build correcto" group="faq">
El root directory no está apuntando a `dist/`. En CloudPanel, edita el sitio y pon el Root Directory en `htdocs/www.yourdomain.com/dist`. Si el directorio es correcto y `dist/index.html` no existe, la salida del build fue a otra parte: revisa `outDir` en `astro.config.mjs`. Servir la raíz del proyecto en lugar de `dist/` "arregla" el 404 y expone `.git/`. No lo hagas.
</Accordion>

<Accordion label="El deploy corrió pero los visitantes siguen viendo el sitio viejo" group="faq">
Caché. O el navegador retiene un TTL de HTML largo, o el edge de Cloudflare aún no lo ha expirado. Acorta la caché de HTML (ver la sección de verificación), purga Cloudflare tras el deploy si debe ser instantáneo, y confirma con `curl -s https://www.yourdomain.com | grep -o '_astro/[^\"]*\.css'` que se están sirviendo nombres de assets hasheados nuevos.
</Accordion>

## Alternativa: usa el tipo de sitio Node.js de CloudPanel

<Tabs>
<Tab name="Static HTML site (recomendado)">
Sirve `dist/` directamente desde Nginx. Ningún proceso Node.js en runtime.

- Menor huella de memoria. Un VPS de 1 a 2 GB es de sobra
- Nada que mantener vivo (sin PM2, sin gestor de procesos)
- Rebuild con Node, serving con Nginx

El default correcto para cualquier sitio Astro puramente estático.
</Tab>
<Tab name="Node.js site">
CloudPanel gestiona la versión de Node.js vía NVM y hace proxy a un puerto de la app.

- Elige **Node.js version 24 LTS** (soportado desde CloudPanel v2.5.4, 2026-07-01; 22 sigue seleccionable)
- Configura un puerto de app (p. ej. 3000) y corre la app con un gestor de procesos
- Necesario solo si más adelante cambias a SSR con un adapter

La misma versión de Node que instalarías a mano en el flujo estático, solo que gestionada por el panel. El coste es un proceso Node permanente consumiendo RAM 24/7.
</Tab>
</Tabs>

Para un sitio puramente estático, usa el tipo Static HTML site. Siempre puedes recrear el sitio como Node.js más adelante si pasas a SSR. Es un cambio de 10 minutos. Si aún estás decidiendo el panel en general, la comparativa de [mejores paneles de servidor self-hosted](https://www.bitdoze.com/best-self-hosted-panels/) cubre los trade-offs.

## Conclusión

Desplegar Astro en un VPS con CloudPanel es el movimiento correcto cuando te quedas corto con los límites del hosting gratuito o quieres control sobre la máquina. El tipo de sitio Static HTML es su versión más simple: Nginx sirve `dist/`, ningún proceso Node corre en runtime, un VPS de 5 a 10 $/mes es suficiente, y los builds más rápidos de Astro 7 hacen que los rebuilds en esa máquina pequeña duelan menos. Añade el script de deploy, el intercambio de `dist.last-known-good` y el checklist de verificación, y tendrás una ruta de deploy en la que puedes confiar a la 1 de la madrugada.

Un repaso de seguridad: mantén CloudPanel actualizado. El panel corre con privilegios reales en tu servidor y el changelog muestra el historial de CVEs que demuestra que importa.

<Button link="https://go.bitdoze.com/do" text="DigitalOcean 100 $ gratis" />
<Button link="https://go.bitdoze.com/vultr" text="Vultr 100 $ gratis" />
<Button link="https://go.bitdoze.com/hetzner" text="Hetzner 20 € gratis" />
<Button link="https://go.bitdoze.com/hostinger-vps" text="Hostinger VPS" />

Si quieres un panel web que también funcione como reverse proxy para contenedores Docker, mira este curso:

<Button
  link="https://webdoze.net/courses/cloudpanel-setup/"
  text="Curso de configuración de CloudPanel"
/>

Adónde ir después:

- [Añade un formulario de contacto a tu sitio Astro](/es/anadir-formulario-contacto-astro/) (gratis, sin backend)
- [Crea un blog Astro gratis en Cloudflare](/es/crear-blog-astro-gratis/) si quieres la ruta sin servidor
- [Despliega Astro.js en Cloudflare Pages](https://www.bitdoze.com/deploy-astrojs-cloudflare/) (hosting gratis, zero-config)
- [Documentación de despliegue de Astro](https://docs.astro.build/en/guides/deploy/) (cubre todas las plataformas soportadas)