{"id":28893606,"url":"https://github.com/devexpress/xaf-blazor-kubernetes-example","last_synced_at":"2025-10-09T22:06:52.013Z","repository":{"id":58779664,"uuid":"525766971","full_name":"DevExpress/XAF-Blazor-Kubernetes-example","owner":"DevExpress","description":"This example shows how to deploy XAF Blazor application to the Kubernetes cluster and to enable horizontal autoscaling","archived":false,"fork":false,"pushed_at":"2023-12-31T08:10:35.000Z","size":514,"stargazers_count":15,"open_issues_count":0,"forks_count":3,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-06-21T03:08:08.081Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DevExpress.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-08-17T11:48:49.000Z","updated_at":"2025-02-11T10:05:24.000Z","dependencies_parsed_at":"2022-09-08T12:50:21.729Z","dependency_job_id":"d22e4e03-7c83-4263-aa80-68b3bdecbd95","html_url":"https://github.com/DevExpress/XAF-Blazor-Kubernetes-example","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/DevExpress/XAF-Blazor-Kubernetes-example","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DevExpress%2FXAF-Blazor-Kubernetes-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DevExpress%2FXAF-Blazor-Kubernetes-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DevExpress%2FXAF-Blazor-Kubernetes-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DevExpress%2FXAF-Blazor-Kubernetes-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DevExpress","download_url":"https://codeload.github.com/DevExpress/XAF-Blazor-Kubernetes-example/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DevExpress%2FXAF-Blazor-Kubernetes-example/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279002116,"owners_count":26083307,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-21T03:08:05.467Z","updated_at":"2025-10-09T22:06:52.007Z","avatar_url":"https://github.com/DevExpress.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Deploy and scale an XAF Blazor Server app: use Azure Kubernetes Service to serve hundreds of users\n\nFollow the instruction in this example to deploy an XAF Blazor application to a Kubernetes cluster with horizontal autoscaling. We tested the application in two types of clusters: locally-run [K3s](https://k3s.io/) and [Azure Kubernetes Service (AKS)](https://azure.microsoft.com/en-us/services/kubernetes-service/). The maximum pod replica number (20) allowed around 300 concurrent users. An AKS cluster needs two nodes (B4ms machines: 4 Cores, 16 GB RAM) to operate with such a number of pod replicas and the same load.\n\nThis repository contains the following useful resources: \n\n* a `Dockerfile` that helps you publish an app to a Linux container, and a version for a Windows container (`Dockerfile.win`)\n* *.yaml files that help you deploy an app to a Kubernetes cluster with a Microsoft SQL Server database engine container, Horizontal Pod Autoscaler, and Ingress \n\nThe following diagram illustrates the cluster’s architecture:\n\n![Cluster diagram](/images/cluster-diagram.png)\n\n## Get Started\n\n### 1. Install Docker Engine or Docker Desktop\n\nVisit [docker.com](https://www.docker.com/) for downloads and additional information.\n\n### 2. Clone this repository\n\n\u003e **Note**\n\u003e Remove the `app.UseHttpsRedirection();` call from the _Startup.cs_ file if you need to run the application behind a Nginx reverse proxy (e.g., with an Nginx container or an Ingress Nginx controller in a Kubernetes cluster).\n\n### 3. Build a Docker image \n\nAdd the the [DevExpress NuGet source URL](https://community.devexpress.com/blogs/news/archive/2023/09/19/nuget-v3-support-and-enhanced-localization-across-winforms-wpf-asp-net-platforms-early-access-preview-v23-2.aspx) to the environment variable:\n\n```\n\u003eexport DX_NUGET=https://nuget.devexpress.com/{your-feed-authorization-key}/api/v3/index.json\n```\n\nUse [BuildKit](https://docs.docker.com/develop/develop-images/build_enhancements/#new-docker-build-secret-information) to build a Docker image. The `--secret` flag helps you safely pass a NuGet source URL from the variable:\n\n\n```\nDOCKER_BUILDKIT=1 docker build -t your_docker_hub_id/xaf-container-example --secret id=dxnuget,env=DX_NUGET .\n```\n\nThe following command runs a container with the image you built:\n\n```\ndocker run --network=\"host\" -e CONNECTION_STRING=MSSQLConnectionString your_docker_hub_id/xaf-container-example:latest\n```\n\n\u003e **Note**: \n\u003e The example in this repository requires that you pass a `CONNECTION_STRING` environment variable. This variable specifies the connection string name (defined in the _appsetting.json_ file) to be used in the container.\n\nIf the application's database is live and doesn't require updates, then your XAF Blazor application is ready for use at `http://localhost/`. \n\nIf the database isn't ready, you will see a database version mismatch error in the console. Such an error indicates that the database either doesn't exist yet or requires an update (based on your latest changes to data model and XAF modules). To resolve the error, force a database update. Launch another application instance in the running container. \n\nRun the following command to find the container's ID:\n\n```\ndocker ps\n```\n\nOnce you obtain the container ID, execute the following command to force the update:\n\n```\ndocker exec your_container_id dotnet XAFContainerExample.Blazor.Server.dll --updateDatabase --forceUpdate --silent\n```\n\n### 4. Store the image in the Docker Hub \n\nLog in with your Docker credentials: \n\n```\ndocker login\n```\n\nPush the image to your Docker Hub:\n\n```\ndocker push your_docker_hub_id/xaf-container-example:latest\n```\n\n**Optional**: You can pull already-built docker images from your own Docker hub. To accomplish this, change image names in the following files: `app-depl.yaml` and `docker-compose.yml`. \n\n### 5. (Optional) Use Docker Compose to run a multi-container application \n\nYou can use [Docker Compose](https://docs.docker.com/compose/) to run a multi-container application. The `docker-compose.yml` file describes how the application and Microsoft SQL Server containers interact. Run the following command to launch:\n\n```\ndocker-compose up\n```\n\nThe application is available at `http://localhost/`.\n\n### 6. Run a terminal \n\nOpen a terminal on the machine that runs Kubernetes. You can use any Kubernetes version: Azure Kubernetes Service (AKS), Google Kubernetes Engine (GKE), locally installed lightweight [K3s](https://k3s.io/), [minikube](https://minikube.sigs.k8s.io/docs/), or others. As mentioned above, we tested this example with AKS and locally-installed K3s.\n\n### 7. Create a storage for the database \n\nApply a [Persistent Volume Claim](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) definition to create a storage for the database:\n\n```\nkubectl apply -f ./K8S/local-pvc.yaml\n```\n\n### 8. Deploy the database\n\nStore the database password into a secret:\n\n```\nkubectl create secret generic mssql --from-literal=SA_PASSWORD=\"Qwerty1_\"\n```\n\nApply the manifest: create a Microsoft SQL Server deployment with its ClusterIP Service.\n\n```\nkubectl apply -f ./K8S/mssql-app-depl.yaml\n```\n\n### 9. Deploy the application\n\nCreate an application deployment with its ClusterIP Service. \n\nOpen the [app-depl.yaml](/K8S/app-depl.yaml) file and change the `devexpress` Docker Hub id to yours (or leave it as is to pull the image from the DevExpress repository). Apply the deployment manifest:\n\n```\nkubectl apply -f ./K8S/app-depl.yaml\n```\n\n**Note**: To fill the database with initial data, you can use the following technique. First, find a pod with the running application:\n\n```\nkubectl get pods\n```\n\nThe command output may look like this:\n\n```\nNAME                         READY   STATUS    RESTARTS     AGE\napp-depl-f487bdcfd-mxnrz     1/1     Running   0            75m\nmssql-depl-c47fdc8c7-5x5m7   1/1     Running   1 (2s ago)   5s\n```\n\nIn the example above, the application pod's name is `app-depl-f487bdcfd-mxnrz`. Use this name to run another application instance in database update mode:\n\n```\nkubectl exec -it %pod_name% -- dotnet XAFContainerExample.Blazor.Server.dll --updateDatabase --forceUpdate --silent\n```\n\n### 10. Configure Ingress \n\nIn this step, you will accomplish the following: \n\n* Configure [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) to make the application accessible from outside the cluster \n* Set up [Sticky Sessions](https://docs.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/server?view=aspnetcore-6.0#kubernetes)\n\nBefore you proceed, install the Ingress NGINX Controller if you haven't done so already. For example, visit the following URL for K3s setup instructions: https://docs.rancherdesktop.io/how-to-guides/setup-NGINX-Ingress-Controller/. \n\n\u003e **Note**\n\u003e This example's ingress definition includes configuration for HTTPS support. If you do not need this functionality, remove the `tls` section from the _ingress-srv.yaml_ file and skip the creation of the tls secret. Otherwise, make sure you have a certificate file (_\\*.crt_) and key file (_\\*.key_). For testing purposes, you can create a self-signed certificate:\n\n```\nopenssl genrsa -out ca.key 2048\n```\n\n```\nopenssl req -x509 \\\n  -new -nodes  \\\n  -days 365 \\\n  -key ca.key \\\n  -out ca.crt \\\n  -subj \"/CN=yourdomain.com\"\n```\n\nCreate a TLS secret:\n\n```\nkubectl create secret tls certificate-secret \\\n--key ca.key \\\n--cert ca.crt\n```\n\nApply the Ingress definition:\n\n```\nkubectl apply -f ./K8S/ingress-srv.yaml\n```\n\nWait for a couple of minutes and check that the application is accessible from outside the cluster:\n\n```\nkubectl get ingress\n```\n\nThe output may look like this:\n\n```\nNAME          CLASS   HOSTS   ADDRESS       PORTS   AGE\ningress-srv   nginx   *       \u003cyour-ip\u003e     80      5d21h\n```\n\nTry to open the starting page in the browser. Use the following URL: `http://\u003cyour-ip\u003e/`.\n\n### 11. Enable Horizontal Scaling \n\nThe application is now running in a single [Pod](https://kubernetes.io/docs/concepts/workloads/pods/). To scale the app horizontally, you can use [Horizontal Pod Autoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/):\n\n```\nkubectl apply -f ./K8S/app-hpa.yaml\n```\n\nYou can now run the following command to see active pods and their CPU load:\n\n```\nkubectl get hpa\n```\n\n```\nuser@ubuntu-k8s:~/Work/xaf-blazor-app-load-testing-example$ kubectl get hpa\nNAME      REFERENCE             TARGETS   MINPODS   MAXPODS   REPLICAS   AGE\napp-hpa   Deployment/app-depl   13%/50%   1         15        7          54m\n```\n\n## Implementation Details\n\n### Build a Docker image for an XAF Blazor application \n\nThis solution contains a `Dockerfile` example based on [microsoft-dotnet](https://hub.docker.com/_/microsoft-dotnet) images.\n\n```\nFROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base\nRUN apt-get update\nRUN apt-get install -y libc6 libicu-dev libfontconfig1\nWORKDIR /app\nEXPOSE 80\nEXPOSE 443\n\nFROM mcr.microsoft.com/dotnet/sdk:8.0 AS build\nWORKDIR /src\nRUN --mount=type=secret,id=dxnuget dotnet nuget add source $(cat /run/secrets/dxnuget) -n devexpress-nuget\nCOPY [\"XAFContainerExample.Blazor.Server/XAFContainerExample.Blazor.Server.csproj\", \"XAFContainerExample.Blazor.Server/\"]\nCOPY [\"XAFContainerExample.Module/XAFContainerExample.Module.csproj\", \"XAFContainerExample.Module/\"]\nRUN dotnet restore \"XAFContainerExample.Blazor.Server/XAFContainerExample.Blazor.Server.csproj\"\nCOPY . .\nWORKDIR \"/src/XAFContainerExample.Blazor.Server\"\nRUN dotnet build \"XAFContainerExample.Blazor.Server.csproj\" -c Release -o /app/build\n\nFROM build AS publish\nRUN dotnet publish \"XAFContainerExample.Blazor.Server.csproj\" -c Release -o /app/publish\n\nFROM base AS final\nWORKDIR /app\nCOPY --from=publish /app/publish .\nENTRYPOINT [\"dotnet\", \"XAFContainerExample.Blazor.Server.dll\"]\n```\n\nYou can also generate such a file in Visual Studio. Right-click the project (**YourApp.Blazor.Server**) and select **Add | Docker Support**. Note that you need to move the created `Dockerfile` up to the root solution folder.\n\n![Docker support](/images/docker-support.png)\n\nTo restore NuGet packages correctly, pass the DevExpress NuGet source URL as a secret (see [BuildKit](https://docs.docker.com/develop/develop-images/build_enhancements/#new-docker-build-secret-information) documentation). \n\nRefer to [Docker reference](https://docs.docker.com/engine/reference/builder/) for additional information on command syntax.\n\n### Run an XAF Blazor application in a Kubernetes cluster\n\nThis section describes all the specifications stored in the `K8S` folder. These specifications are sufficient to deploy and run an XAF Blazor application with load balancing and autoscaling.\n\n#### 1. Database deployment\n\nDatabase deployment requires a storage. \n\nThe [PersistentVolume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) subsystem implements API that abstracts details of storage allocation from storage consumption. A **PersistentVolumeClaim** (PVC) is a request for storage. The sample below is a specification for a simple PVC ([local-pvc.yaml](/K8S/local-pvc.yaml)), sufficient for a locally-run cluster, such as [K3s](https://k3s.io/):\n\n```\napiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n  name: mssql-claim\nspec:\n  accessModes:\n    - ReadWriteOnce\n  resources:\n    requests:\n      storage: 1Gi\n```\n\nSee [mssql-app-depl.yaml](/K8S/mssql-app-depl.yaml) for database engine [deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#writing-a-deployment-spec) specifications. The deployment procedure accomplishes two tasks: \n\n- Runs Microsoft SQL Server in a container\n- Runs ClusterIP service to [expose an app](https://kubernetes.io/docs/tutorials/kubernetes-basics/expose/expose-intro/) on an internal IP in the cluster. (The database server is only reachable inside the cluster.)\n\n#### 2. Application deployment\n\nSee [app-depl.yaml](/K8S/app-depl.yaml) for application deployment parameters, including a ClusterIP service:\n\n```\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: app-depl\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: xafcontainerexample\n  template:\n    metadata:\n      labels:\n        app: xafcontainerexample\n    spec:\n      containers:\n      - name: xafcontainerexample\n        image: devexpress/xaf-container-example:latest\n        env:\n          - name: CONNECTION_STRING\n            value: K8sMSSQLConnectionString\n          - name: ASPNETCORE_URLS\n            value: http://+:80\n        resources:\n          requests:\n            cpu: 400m\n            memory: 500Mi\n          limits:\n            cpu: 800m\n            memory: 1Gi\n```\n\nThe file specifies the pre-built image, additional environment variables (such as CONNECTION_STRING), and hardware resources that the cluster should reserve for this container.\n\n#### 3. Ingress\n\nIngress exposes HTTP and HTTPS routes from outside the cluster to services within the cluster. Traffic routing is controlled by rules defined on the Ingress resource. Visit the following webpage from Kubernetes documentation to learn more: [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/).  \n\nBlazor Server applications use long-living WebSocket to communicate between browser and server. This means that you need to enable [Sticky Sessions](https://docs.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/server?view=aspnetcore-6.0#kubernetes) to maintain a connection to a Pod during the entire application run. \n\nThe [Ingress definition](/K8S/ingress-srv.yaml) example in this repository works with Kubernetes version 1.19+. The cluster must run an [Ingress controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/).\n\n#### 4. Horizontal Pod Autoscaler\n\nThe following manifest ([app-hpa.yaml](/K8S/app-hpa.yaml)) defines a HorizontalPodAutoscaler (HPA) that adjusts the number of running pod replicas according to the specified metrics.\n\n```\napiVersion: autoscaling/v2\nkind: HorizontalPodAutoscaler\nmetadata:\n name: app-hpa\nspec:\n  scaleTargetRef:\n    apiVersion: apps/v1\n    kind: Deployment\n    name: app-depl\n  minReplicas: 1\n  maxReplicas: 20\n  metrics:\n  - type: Resource\n    resource:\n      name: cpu\n      target:\n        type: Utilization\n        averageUtilization: 50\n```\n\nThis example can scale pod replicas from 1 (`minReplicas`) up to 20 (`maxReplicas`) based on CPU utilization. Refer the [HPA documentation](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#algorithm-details) to learn more.\n\n### Use Docker Compose to run a multi-container application\n\nIf you don't want to scale the app automatically and set up Kubernetes, consider **Docker Compose**. \n\nThe simplest example of the `docker-compose.yml` file includes definitions for two containers. The first container relates to the `xafcontainerexample` image mentioned above. The second one runs a Microsoft SQL Server and allows access to it from the first container. \n\n```\nversion: \"3.9\"\nservices:\n    web:\n        image: \"devexpress/xaf-container-example:latest\"\n        ports:\n          - \"80:80\"\n        environment:\n          - CONNECTION_STRING=DockerComposeMSSQLConnectionString\n            \n    db:\n        image: \"mcr.microsoft.com/mssql/server\"\n        environment:\n            SA_PASSWORD: \"Qwerty1_\"\n            ACCEPT_EULA: \"Y\"\n        expose:\n          - \"1433\"\n```\n\nYou can access the application on port 80. However, we apply the 'expose' configuration option to the SQL Server container. This option makes the server accessible only within the Docker network.\n\nThe application can use the following connection string to access the database:\n\n```\nPooling=false;Data Source=db;Initial Catalog=XAFContainerExample;User Id=SA;Password=\u003cyour_strong_password\u003e\n```\n\nIf you want to run a container with HTTPS support, update the `docker-compose.yml` file as follows (remember to specify your certificate password instead of the placeholder \"certificate_password\"):\n\n```\nversion: \"3.9\"\nservices:\n    web:\n        image: \"devexpress/xaf-container-example:latest\"\n        ports:\n          - \"80:80\"\n          - \"443:443\"\n        environment:\n          - ASPNETCORE_ENVIRONMENT=Development\n          - ASPNETCORE_URLS=https://+:443;http://+:80\n          - ASPNETCORE_Kestrel__Certificates__Default__Password=certificate_password\n          - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx\n          - CONNECTION_STRING=DockerComposeMSSQLConnectionString\n        volumes:\n          - ~/.aspnet/https:/https:ro\n            \n    db:\n        image: \"mcr.microsoft.com/mssql/server\"\n        environment:\n            SA_PASSWORD: \"Qwerty1_\"\n            ACCEPT_EULA: \"Y\"\n        expose:\n          - \"1433\"\n```\nFor more information about how to set up development certificates in this case, see the [Microsoft documentation](https://learn.microsoft.com/en-us/aspnet/core/security/docker-compose-https?view=aspnetcore-7.0#macos-or-linux).\n\nThere is also another way to support HTTPS with Docker Compose. You can add a container with the Nginx reverse proxy - the Dockerfile for Nginx container is available here (`Dockerfile.Nginx`):\n\n```\nFROM nginx:latest\n\nCOPY nginx.conf /etc/nginx/nginx.conf\nCOPY nginx-selfsigned.crt /etc/ssl/certs/nginx-selfsigned.crt\nCOPY nginx-selfsigned.key /etc/ssl/private/nginx-selfsigned.key\n```\n\nDuring the image build, we copy the Nginx configuration file `nginx.conf` and the self-signed key and certificate pair into the container. Follow this article to learn how to create a self-signed certificate for testing purposes: [How To Create a Self-Signed SSL Certificate for Nginx in Ubuntu 16.04](https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-nginx-in-ubuntu-16-04).\n\nThe Nginx configuration file includes settings for the following operations:\n\n* Listen to port 443 over SSL \n* Forward requests to the application server \n* Redirect requests from port 80 to 443\n\nThe updated `docker-compose.nginx.yml` file has an additional definition for the Nginx container:\n\n```\nversion: \"3.9\"\nservices:\n    app:\n        image: \"devexpress/xaf-container-example:latest\"\n        pull_policy: missing\n        expose:\n          - \"80\"\n        environment:\n          - ASPNETCORE_URLS=http://+:80\n          - CONNECTION_STRING=DockerComposeMSSQLConnectionString\n    db:\n        image: \"mcr.microsoft.com/mssql/server\"\n        environment:\n            SA_PASSWORD: \"Qwerty1_\"\n            ACCEPT_EULA: \"Y\"\n        expose:\n          - \"1433\"\n    nginx:\n        build:\n          dockerfile: Dockerfile.Nginx\n        depends_on:\n          - app\n        ports:\n          - \"80:80\"\n          - \"443:443\"\n```\n\nUse the following command to run containers :\n```\ndocker compose -f docker-compose.nginx.yml up\n```\n\nUse the following URL to ensure the application is available in the browser: `https://localhost`. The browser should automatically redirect from `http` to `https`.\n\nAdditional information:\n- [Docker Compose specification](https://docs.docker.com/compose/compose-file/)\n\n## FAQ, troubleshooting, and limitations\n\n### 1. Will my own XAF Blazor app work with 100, 200, 300, or more concurrent users with similar performance, provided the hardware/software are the same?\n\nWe cannot provide a universal calculator for web server hardware/software requirements, nor can we comment on overall performance without tests. The complexity of your business model and implemented behaviors are significant factors in throughput/performance. Ultimately, performance will depend on development decisions, application type, environment, and even tested use-case scenarios. A few examples of factors that affect application performance are the number of persistent classes and their fields, Controller design, Application Model customizations, availability of memory intensive operations to end-users (frequent import/export of large data amounts, or complex report generation). For more information, review [XAF ASP.NET WebForms or Blazor Server UI for SaaS with 1000 users](https://supportcenter.devexpress.com/ticket/details/t585727/xaf-asp-net-webforms-or-blazor-server-ui-for-saas-with-1000-users) and [XAF ASP.NET Web Forms application deployment and load testing considerations](https://supportcenter.devexpress.com/ticket/details/s36497/xaf-asp-net-web-forms-application-deployment-and-load-testing-considerations).\n\nIn brief, every application is unique. Even with horizontal scaling, we recommend that you carefully test your XAF Web apps under conditions close to your production environment. Measure performance over time. Emulate the user load. The following GitHub example may prove useful - [XAF Blazor load testing on Linux and MySql using Puppeteer and GitHub Actions](https://github.com/DevExpress/xaf-blazor-app-load-testing-example).\n\n### 2. Can I consult DevExpress about configuration or deployment issues with Docker, Kubernetes, Windows-based or Linux-based servers, hosting providers, and other 3rd party technologies?\n\nDevExpress cannot assist you on this, because deployment is not specific to DevExpress code (XAF Blazor). You can do anything Microsoft ASP.NET Core Blazor Server can handle (refer to [Microsoft Docs](https://docs.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/server) | [Deployment Recommendations for XAF Blazor UI](https://docs.devexpress.com/eXpressAppFramework/403362/deployment/deployment-recommendations-blazor)).\n\nDevExpress does not assist in administering web servers or hosting environments for customers. We do not consult on various server and operating system configurations as part of our support services. For more information, please review the \"Prerequisites\" and \"Technical Support Scope\" sections at https://www.devexpress.com/products/net/application_framework/xaf-considerations-for-newcomers.xml. \n\nIf you experience issues, we recommend that you first make sure that your deployment scenario works without XAF. Try a pure Blazor Server app (with the same database and XPO or EF Core for data access). Once you resolve issues with that application, an XAF Blazor app should work as expected.\n\n### 3. How do I avoid a \"Connection Error\" message in the web browser for some users when the HPA scales down replicas?\n\nThis problem is caused by Sticky Sessions. The browser communicates with only one particular server all the time a page is open. When a pod replica is terminated, the connection is lost. You can implement a workaround - refresh the browser if the app loses server connection. You can find an example in the following file: [_Host.cshtml](/XAFContainerExample.Blazor.Server/Pages/_Host.cshtml). \n\nA Pod can also finish processes gracefully upon termination. You can set the [terminationGracePeriodSeconds](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#lifecycle) option to specify a delay after the container receives the termination signal and before it forcibly halts the process. The default delay is 30 seconds. You may also consider [Container Lifecycle Hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/) (such as `preStop`) to manage application instance state before pod termination.\n\n### 4. How do I get Ingress working on a K3s Kubernetes distribution? (The application web page cannot be reached outside the cluster)\n\nCheck the `ingress-nginx-controller` service:\n\n```\nkubectl get svc -n ingress-nginx\n```\n\nThe output may look like this:\n\n```\nNAME                                 TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                      AGE\ningress-nginx-controller-admission   ClusterIP      10.43.168.32   \u003cnone\u003e          443/TCP                      14m\ningress-nginx-controller             LoadBalancer   10.43.132.9    \u003cpending\u003e       80:31075/TCP,443:32734/TCP   14m\n```\n\nSee if the `ingress-nginx-controller` service always displays 'pending' under **External IP**. If that is the case, you probably experience the following issue: https://github.com/rancher/k3os/issues/208. Try a workaround from the following comment: https://github.com/rancher/k3os/issues/208#issuecomment-599087377\n\n```\nkubectl patch svc ingress-nginx-controller -n ingress-nginx -p '{\"spec\": {\"type\": \"LoadBalancer\", \"externalIPs\":[\"your-external-ip\"]}}'\n```\n\n### 5. How do I build a Docker image for a Windows container? (Docker BuildKit supports only Linux containers)\n\nIf you need to build an image for a Windows container, use the following workaround to avoid passing DevExpress NuGet source insecurely. \n\nBuild the application on the local machine and put the app into an image.\n\n```\ndotnet publish ./XAFContainerExample.Blazor.Server/XAFContainerExample.Blazor.Server.csproj -c Release -o ./app\n```\n\nChange the container type in the running Docker instance. Right-click the System Tray's Docker icon and choose **Switch to Windows containers...** To build an image with a custom Dockerfile name, use the `-f ` flag:\n\n```\ndocker build -f Dockerfile.win -t \u003cyour_docker_hub_id\u003e/xaf-container-example:win .\n```\n\nYou cannot run the container on Windows in the same manner as described in the \"Getting Started\" section. The `--network=\"host\"` mode is only supported on Docker for Linux. Use the [host.docker.internal](https://docs.docker.com/desktop/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host) as the hostname instead of `localhost`.\n\n```\ndocker run -p 80:80 -e CONNECTION_STRING=DockerMSSQLConnectionString your_docker_hub_id/xaf-container-example:latest\n``\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevexpress%2Fxaf-blazor-kubernetes-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevexpress%2Fxaf-blazor-kubernetes-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevexpress%2Fxaf-blazor-kubernetes-example/lists"}