Si has seguido esta serie ya sabes cómo montar tu servidor, y no solo eso, ya sabes cómo montar tu aplicación para que se despliegue sola utilizando el VPS junto a Dokploy. Lo configuramos todo una vez y sin más configuraciones manuales tendremos despliegues limpios utilizando contenedores Docker.
En ese post vimos que tenemos dos opciones: o le damos a un botón para desplegar cada vez que hacemos un cambio, o usamos el webhook que se activa automáticamente al hacer un push en el branch. Ninguna de las dos son ideales por sí solas.
Hoy vamos a configurar un flujo de trabajo real con una pipeline de integración continua (CI) y despliegue continuo (CD) utilizando GitHub Actions.
Tabla de contenidos
1 - Preparaciones
Lo primero que tenemos que hacer antes de empezar es tener claro qué es lo que necesitamos.
1.1 - Preparación de la parte del servidor
De la parte del servidor, necesitamos (además de tener un VPS de Hostinger corriendo Dokploy) obtener nuestra URL del webhook.
Como vimos en el post anterior, una vez configurada la app, tenemos que copiar nuestra URL del webhook dentro de la pestaña de Deployment.

En mi caso es algo como:
http://76.13.59.111:3000/api/deploy/L_HlvvQTqamT4u_rVsg2M
OJO, no compartas esta URL con nadie, pues si la compartes cualquiera puede lanzar un nuevo despliegue de tu aplicación. NOTA: Donde ves la IP, puedes poner tu propio dominio.
1.2 - Preparación de la parte de la aplicación
Igual que vimos en el post anterior, necesitamos un Dockerfile funcional que construya la aplicación. Como estamos usando .NET 10, nos aseguramos de usar las imágenes correctas:
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
USER $APP_UID
WORKDIR /app
EXPOSE 8080
EXPOSE 8081
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["BlazorApp1/BlazorApp1.csproj", "BlazorApp1/"]
RUN dotnet restore "BlazorApp1/BlazorApp1.csproj"
COPY . .
WORKDIR "/src/BlazorApp1"
RUN dotnet build "./BlazorApp1.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./BlazorApp1.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "BlazorApp1.dll"]
Para hacer el proceso de Integración Continua mucho más realista, vamos a meter un test automático. En Blazor, el estándar de la industria es usar la librería bUnit. Vamos a probar que nuestro componente Counter.razor suma números correctamente al hacer clic en el botón de la interfaz web.
Dentro de nuestro proyecto de pruebas BlazorApp1.Tests, tendríamos un test como este:
public class CounterTests : BunitContext
{
[Fact]
public void WhenButtonIsClicked_ThenCounterShouldIncrement()
{
var cut = Render<Counter>();
cut.Find("button").Click();
cut.Find("p").MarkupMatches("<p role=\"status\">Current count: 1</p>");
}
}
Si en el futuro alguien rompe el contador (por ejemplo, restando en lugar de sumar), este test fallará y detendrá el despliegue, salvando nuestra aplicación en producción.
1.3 - Preparar GitHub
En nuestro caso utilizamos GitHub, pero puedes utilizar cualquier plataforma de control de versiones ya que todas funcionan de forma parecida.
Aquí lo que tenemos que hacer es incluir la URL del webhook que hemos visto antes dentro de los Secrets de GitHub. Para ello, dentro de tu repositorio ve a Settings -> Secrets and variables -> Actions y añade uno nuevo con el nombre DOKPLOY_WEBHOOK y la URL como valor.

Esto significa que tienes tu secreto guardado en GitHub y será utilizado por la pipeline, pero nadie puede verlo.
2 - Crear una pipeline para desplegar en Dokploy desde GitHub
Una vez tenemos la preparación, solo queda crear el archivo que va a indicar a GitHub exactamente qué hacer cuando subimos el código. Y lo haremos de la forma correcta: primero se ejecutan los tests de Blazor y, solo si todo sale bien, hacemos el deploy.
Este fichero se puede crear tanto en tu máquina como en GitHub directamente (en la ruta .github/workflows/deploy.yml) y este es el resultado final:
name: CI/CD Pipeline
on:
push:
branches:
- '**'
jobs:
test:
name: test-execution
runs-on: ubuntu-latest
steps:
- name: get code
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: 10.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
deploy:
name: Dockploy Deploy
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: ping Dockploy webhook
run: |
curl -X POST "${{ secrets.DOKPLOY_WEBHOOK }}" \
--header 'Content-type: application/json' \
--header 'X-Github-Event: Deploy-from-gihtub' \
--data '{"ref":"refs/heads/main"}' \
- El apartado
onsignifica cuándo vamos a ejecutar este fichero. En nuestro caso, en cada push, en cualquier branch (**). - Después vienen los
jobs. Tenemos dos.- El primero es para correr los tests de C# con .NET 10 y como vemos tiene varios pasos (descargar código, preparar .NET y correr los tests).
- El segundo es el de desplegar, que tiene algo más de miga:
- Primero tenemos la instrucción
needs: test. Esto significa que necesita que el job de tests termine (y sea exitoso) para empezar a ejecutarse. Si no indicas esto, ambos se ejecutarían a la vez. - Luego tenemos un i
f: github.ref == 'refs/heads/main'. Este if hace que el despliegue se ejecute únicamente en la rama principal.
- Primero tenemos la instrucción
De esta forma, todos los push a cualquier rama ejecutan los tests, pero a Dokploy solo llega lo que subimos a main cuando los tests pasan. ¡Ahora únicamente nos queda probarlo y ver que todo funciona de forma correcta!

Si revisas Dokploy, verás que tu VPS ha lanzado otro despliegue, teniendo el proceso totalmente automatizado.
Puedes pensar que desplegar con cada commit a main es muy arriesgado, pero la verdad es que es una práctica completamente normal en un montón de empresas punteras.
10% de descuento en tu VPS de HOSTINGER con el cupón de descuento NETMENTOR.