The modernization of web application delivery relies heavily on the integration of Continuous Integration and Continuous Deployment (CI/CD) pipelines. Blazor, Microsoft's framework for building interactive web UIs with .NET, offers two distinct hosting models—Blazor Server and Blazor WebAssembly (WASM)—each requiring a fundamentally different deployment strategy. GitHub Actions serves as the primary automation engine in this ecosystem, allowing developers to transition from code commit to a live production environment without manual intervention. By leveraging YAML-based workflow definitions, developers can orchestrate the build, test, and deployment phases, ensuring that the application is consistently delivered to environments such as the Azure App Service for server-side logic or GitHub Pages for static client-side hosting.
Deployment Architectures for Blazor Server and WebAssembly
Understanding the architectural requirements of Blazor is critical before configuring any automation pipeline. The choice between Blazor Server and Blazor WebAssembly dictates whether the deployment target must be a fully managed server environment or a static file host.
Blazor Server on Azure App Service
Blazor Server applications maintain a persistent connection between the client and the server via SignalR. Because the application logic executes on the server, it requires a robust hosting environment capable of running the .NET runtime. Azure App Service provides a Platform as a Service (PaaS) solution that abstracts the underlying infrastructure, allowing the application to be hosted as a web app.
The deployment process for Blazor Server involves utilizing a publish profile. This profile is a configuration file containing the necessary credentials and endpoint settings required for GitHub Actions to push the compiled binaries to the Azure environment. The workflow typically consists of two primary jobs: a build job that compiles the .NET code and a deploy job that transfers the artifacts to the Azure Web App.
Blazor WebAssembly on GitHub Pages
Blazor WebAssembly is a client-side framework where the application, its dependencies, and the .NET runtime are downloaded to the browser. This renders the app as a collection of static files (HTML, CSS, and JS), making it an ideal candidate for GitHub Pages.
Unlike server-side apps, Blazor WASM apps deployed to GitHub Pages face a specific challenge regarding routing. Because GitHub Pages is a static site host, it does not inherently understand Blazor's internal routing. If a user refreshes a subpage, the server returns a 404 error because the actual file does not exist on the disk. To solve this, developers must implement a workaround involving a 404.html file and a script, such as gh-pages.js, to intercept requests and redirect them back to the Blazor application.
Technical Implementation of Blazor Server Deployment to Azure
Deploying a Blazor Server application requires a coordinated effort between the Azure Portal and the GitHub repository.
Azure Infrastructure Setup
The process begins in the Azure portal, where the developer must create an Azure Web App within the App Service. The configuration of this resource requires the following specific inputs:
- Resource Group: A logical container for the Azure resources. If one does not exist, it must be created.
- App Name: A globally unique identifier for the web application.
- Runtime Stack: The specific version of .NET required to execute the code. For example, .NET 6 is commonly used for server-side Blazor applications.
- Operating System: Linux is a standard choice for hosting .NET applications in Azure.
- Region: The physical location of the data center, such as East Asia.
Once the resource is provisioned, the user must download the publish profile. This file contains the secret keys and endpoints that allow GitHub Actions to authenticate with Azure.
GitHub Actions Workflow for Azure
To automate this process, the publish profile data must be stored as a GitHub Secret. This ensures that sensitive credentials are not committed to the version control system.
The workflow is triggered upon pushing changes to the GitHub repository. The execution flow is as follows:
- Build Job: The workflow uses a runner (typically
ubuntu-latest) to compile the .NET project. - Deploy Job: The workflow utilizes the secrets and the publish profile to push the compiled code to the Azure App Service.
Once the action completes, the user can verify the status in the Azure portal. The web app will show a status of "Running," and the application can be accessed via the provided URL. If the application needs to be taken offline, the "Stop" button in the portal is used.
Technical Implementation of Blazor WebAssembly Deployment to GitHub Pages
Deploying Blazor WASM to GitHub Pages is a more complex process due to the static nature of the hosting and the need for routing adjustments.
Project Initiation and Local Setup
The process starts with the creation of the project using the .NET CLI:
dotnet new blazorwasm -o Blazor.Wasm
In this command, Blazor.Wasm represents the project folder name. After creation, the project is committed and pushed to a GitHub repository.
Managing Routing and the 404 Challenge
A significant technical hurdle in GitHub Pages is the lack of support for server-side routing. To ensure the application behaves correctly when navigating subpages, a specific set of files must be introduced:
- gh-pages.js: A script that adjusts the routing for the application.
- 404.html: A fallback page that redirects the user to the main entry point.
One method to implement this is by adding the Bluehill.Blazor.GHPages NuGet package. This package automatically adds gh-pages.js and 404.html to the wwwroot directory during the publish process. If these files already exist, they must be deleted to avoid conflicts.
The index.html file must be modified to include the routing script before the Blazor framework script:
```html
```
The GitHub Actions Workflow for WASM
The workflow for Blazor WASM typically involves a series of steps to prepare the static content for the web.
Workflow Configuration Parameters
The YAML configuration requires specific environment variables and permissions:
- PUBLISH_DIR: The path to the published files, such as
src/Blazor.Wasm/bin/Release/net9.0/publish/wwwroot. - Permissions: The workflow requires
contents: read,pages: write, andid-token: writeto interact with the GitHub Pages API. - Concurrency: A concurrency group named
pagesis used to ensure that only one deployment happens at a time, preventing race conditions.
Step-by-Step Execution Flow
The deployment process follows these precise technical steps:
- Checkout: Uses
actions/[email protected]to pull the code. - SDK Setup: Uses
actions/[email protected]to install .NET 9.0. - Publish: The command
dotnet publish src/Blazor.Wasm -c Releaseis executed to create the production build. - Base Href Rewrite: The
SteveSandersonMS/[email protected]action is used to modify theindex.htmlfile. This is necessary because GitHub Pages apps are often hosted at a subpath (e.g.,/repository-name/), and the<base href="/" />must be updated to match the repository name. - Artifact Upload: The
actions/[email protected]action uploads thewwwrootcontent. - Deployment: The
actions/[email protected]action pushes the content to the live site.
Comparison of Deployment Methods
The following table summarizes the differences between deploying Blazor Server and Blazor WebAssembly via GitHub Actions.
| Feature | Blazor Server (Azure) | Blazor WebAssembly (GitHub Pages) |
|---|---|---|
| Target Environment | Azure App Service (PaaS) | GitHub Pages (Static Hosting) |
| Primary Requirement | Publish Profile / Secrets | Routing Scripts (gh-pages.js) |
| Runtime | .NET Runtime on Server | WebAssembly in Browser |
| Routing Logic | Handled by ASP.NET Core | Handled by Client-side JS / 404.html |
| Workflow Trigger | Push to main branch | Push to main branch |
| Complexity | Medium (Cloud Config) | High (Routing/Base Href fixes) |
Advanced Workflow Implementations and NuGet Integrations
For those seeking a more streamlined experience, specialized GitHub Actions exist to handle the complexities of Blazor WASM. The na1307/blazor-github-pages@v4 action simplifies the process by combining the restore, build, and publish steps while automatically modifying index.html and 404.html.
Using the Specialized Action
When using this action, the workflow is structured as follows:
- The
na1307/blazor-github-pages@v4action is called with theproject-pathprovided (e.g.,MyBlazorApp/MyBlazorApp.csproj). - The action prepares the files for GitHub Pages.
- The
actions/configure-pages@v5action sets up the environment. - The
actions/upload-pages-artifact@v4action uploads the content from the output path provided by the preparation step. - Finally,
actions/deploy-pages@v4makes the site live.
Critical Considerations for .NET Version Upgrades
Upgrading the .NET version can introduce breaking changes in the deployment pipeline. For example, transitioning from .NET 7 to .NET 8 introduced the "Blazor Web App" model, which merges server and client components.
A critical issue arises when converting a Blazor WebAssembly app to a Blazor Web App. In the newer model, the App.razor file in the server project takes precedence, and the index.html file in the client's wwwroot is deleted. Since GitHub Pages requires an index.html file to serve as the entry point, this conversion results in a 404 error. To resolve this, developers must ensure that the build process generates a static index.html file that the GitHub Pages environment can recognize.
Conclusion
The automation of Blazor deployments via GitHub Actions represents a sophisticated intersection of .NET development and DevOps practices. For Blazor Server, the focus is on secure credential management through publish profiles and the leveraging of Azure App Service's PaaS capabilities to maintain persistent SignalR connections. For Blazor WebAssembly, the challenge is primarily the adaptation of a single-page application (SPA) to a static hosting environment, requiring precise manipulations of the base href and the implementation of routing workarounds through 404.html and specialized JavaScript.
The effectiveness of these pipelines is measured by their ability to remove manual errors and accelerate the release cycle. Whether using the manual dotnet publish approach with base href rewrites or utilizing specialized actions like na1307/blazor-github-pages, the goal remains the same: a seamless transition from source code to a globally accessible URL. As the Blazor ecosystem evolves—particularly with the introduction of the unified Blazor Web App model in .NET 8 and beyond—developers must remain vigilant about the structural requirements of their hosting providers to avoid deployment failures.