Skip to main content
All docs
V23.2

Deploy an ASP.NET Core Blazor Server Application to Linux with Nginx

  • 10 minutes to read

This topic describes how to set up and publish an XAF Blazor Server UI application to Ubuntu, Red Hat Enterprise (RHEL), and SUSE Linux Enterprise Server machines with Nginx reverse proxy.

The Solution Wizard uses Microsoft SQL Server as the default database provider option. This examples changes that default option to PostgreSQL (simply as a customization example). For more information on how to switch database providers, refer to the following topic: Switch EF Core Connection from SQL Server to a Different Database Provider.

Prerequisites

  • Access to Ubuntu. Use its standard user account with sudo privileges.
  • An XAF Blazor Server application. This topic is based on the MainDemo Blazor Server demo application that ships with XAF. You can find this demo in the following folder: %PUBLIC%\Documents\DevExpress Demos 23.2\Components\XAF\MainDemo.NET.EFCore\CS\MainDemo.Blazor.Server.

    In some cases, you may encounter build errors because file paths are too long. To solve this, we recommend that you copy the folder containing the demo application closer to the root of your drive.

    If you do not have XAF installed, download our Universal Subscription installer from the Download Manager or start a 30-day trial now.

    Read Tutorial: Create a New XAF Blazor App

Deployment Instructions

Install Required Libraries and Packages

  1. Install the following font libraries. These libraries allow the application to measure text to correctly render documents and export them to PDF.

    sudo apt-get install -y libc6 libicu-dev libfontconfig1
    
  2. Install the ttf-mscorefonts-installer package that adds Microsoft TrueType core fonts to your system. (Installed fonts include Arial, Times New Roman, Courier New, and others.)

    sudo apt-get install ttf-mscorefonts-installer
    
  3. Install the package below to render JPEG images in PDF files (you can install any other package that implements libjpeg API v6.2 or v8.0.).

    sudo apt install -y libjpeg-turbo8
    
  4. Make sure an appropriate drawing engine is available. If your project targets .NET 7+, add the following packages to your MainDemo.Blazor.Server project:

Set Up Forwarded Header Middleware

Since requests are forwarded through a reverse proxy, use Forwarded Headers Middleware. It updates Request.Scheme with the X-Forwarded-Proto header so that redirect URIs and other security policies work correctly.

Forwarded Headers Middleware should run before any other middleware. This order ensures that other middleware can consume header values for processing. To run Forwarded Headers Middleware after diagnostic and error handling middleware, see Forwarded Headers Middleware order.

To set up this middleware, call the UseForwardedHeaders method from your project’s Startup.cs file before calling any other middleware. Within this call, configure the middleware to forward X-Forwarded-For and X-Forwarded-Proto headers.

File: Startup.cs

//...
using Microsoft.AspNetCore.HttpOverrides;

namespace MainDemo.Blazor.Server;

public class Startup {
    //...
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
        //...
        app.UseForwardedHeaders(new ForwardedHeadersOptions {
            ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
        });
        //...
    }
}

Set Up the Database and Connection String

To use PostgreSQL database provider, follow the steps below:

  1. Install the Npgsql.EntityFrameworkCore.PostgreSQL package to both MainDemo.Module and MainDemo.Blazor.Server projects. Make sure that this package’s version matches the version of the EntityFrameworkCore package you have installed.

  2. Change the application connection string.

    File: appsettings.json

    "ConnectionStrings": {
        "ConnectionString": "Host=localhost;Database=maindemo;Username=testuser;Password=Qwerty1_",
        ...
    },
    ...  
    
  3. If you use migrations, update the MainDemoDesignTimeDbContextFactory.CreateDbContext method in the following way:

    File: MainDemo.Module/BusinessObjects/MainDemoDbContext.cs

    public class MainDemoDesignTimeDbContextFactory : IDesignTimeDbContextFactory<MainDemoDbContext> {
    
        public MainDemoDbContext CreateDbContext(string[] args) {
            var optionsBuilder = new DbContextOptionsBuilder<MainDemoDbContext>();
            optionsBuilder.UseNpgsql(@"Host=localhost;Database=maindemo;Username=testuser;Password=Qwerty1_");
            optionsBuilder.UseChangeTrackingProxies();
            return new MainDemoDbContext(optionsBuilder.Options);
        }
    }
    
  4. Change the DbContextTypesInfoInitializerBase descendant to use PostgreSQL.

    File: MainDemo.Module/BusinessObjects/MainDemoDbContext.cs

    public class EFDemoDbContextInitializer : DbContextTypesInfoInitializerBase {
    
        protected override DbContext CreateDbContext() {
            var optionsBuilder = new DbContextOptionsBuilder<MainDemoDbContext>()
                .UseNpgsql(@";")
                .UseChangeTrackingProxies()
                .UseObjectSpaceLinkProxies();
            return new MainDemoDbContext(optionsBuilder.Options);
        }
    }
    
  5. Change the MainDemo.Blazor.Server/Startup.cs file as follows:

    namespace MainDemo.Blazor.Server;
    
    public class Startup {
        //...
        public void ConfigureServices(IServiceCollection services) {
            //...
            builder.ObjectSpaceProviders
                .AddSecuredEFCore(o => o.PreFetchReferenceProperties())
                .WithAuditedDbContext(contexts => {
                contexts.Configure<MainDemoDbContext, AuditingDbContext>(
                    (serviceProvider, businessObjectDbContextOptions) => {
                        string connectionString = GetConnectionString(Configuration);
                        ArgumentNullException.ThrowIfNull(connectionString);
                        // businessObjectDbContextOptions.UseSqlServer(connectionString);
                        businessObjectDbContextOptions.UseNpgsql(connectionString);
                        businessObjectDbContextOptions.UseLazyLoadingProxies();
                        businessObjectDbContextOptions.UseChangeTrackingProxies();
                        businessObjectDbContextOptions.UseObjectSpaceLinkProxies();
                    });
                }
        }
    }
    

    You may see an error message that says “Cannot write DateTime with Kind=Unspecified to PostgreSQL type ‘timestamp with time zone’“. If this happens, set DateTimeKind to UTC for each DateTime property or add the following line to the Program.Main() method.

    File: YourSolutionName.Blazor.Server\Program.cs

    namespace MainDemo.Blazor.Server;
    
    public class Program : IDesignTimeApplicationFactory {
        public static int Main(string[] args) {
            //...
            AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
            //...
        }
    }
    

    Refer the following topic for details: Date and Time Handling.

Publish and Copy the Application

  1. Make sure that the DevExpress NuGet source URL is registered in your source list. Use the following link to locate this URL: Your DevExpress NuGet Feed URL. We also recommend that you use the NuGet v3 source URL.

    The command below adds DevExpressNuget to your source list.

    dotnet nuget add source -n DevExpressNuget https://nuget.devexpress.com/{your-feed-authorization-key}/api/v3/index.json 
    
  2. Execute the dotnet publish command from the development environment to package an application into a directory that can be run on the server.

    dotnet publish --configuration Release
    
  3. Copy your application to the server using a tool that is integrated into the organization’s workflow (for example, SCP, SFTP). It’s common to store web applications in the var directory (for example, var/www/MainDemo.Blazor.Server).

  4. Check that the application works correctly. To do this, run the application from the command line.

    dotnet MainDemo.Blazor.Server.dll.
    

    Run the application locally under Linux. Open a browser and navigate to http://<serveraddress>:<port>. Check that the application works correctly.

    DevExpress XAF - Publishing Result

  5. If the application database exists and doesn’t require any updates, your XAF Blazor application is ready to use.

    If 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 needs to be updated (based on your latest changes to the data model and XAF modules). To resolve the error, start the application in database update mode.

    dotnet MainDemo.Blazor.Server.dll --updateDatabase --forceUpdate –silent
    

Configure Nginx

  1. Install Nginx. To do this, you can use the following instructions:

  2. As Nginx has just been installed, explicitly start it.

    sudo service nginx start
    
  3. Configure Nginx as a reverse proxy to forward HTTP requests to your Blazor server application.

    Open the following file: /etc/nginx/sites-available/default. For example, run the command below to open it in the nano text editor.

    sudo nano /etc/nginx/sites-available/default 
    

    Replace the file’s content with the following code.

    server {
        listen 80;
        server_name localhost;
        location / {
            return 301 https://$host$request_uri;
            proxy_pass http://localhost:5000;
        }
    }
    
    server {
        listen 443 ssl;
        server_name localhost;
        ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
        ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
        location / {
            proxy_pass http://localhost:5000;
            proxy_http_version 1.1;
            proxy_cache_bypass $http_upgrade;
            proxy_buffering off;
            proxy_read_timeout 100s;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Connection keep-alive;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-Host $server_name;
        }
    }
    
  4. In the configuration snippet above, Nginx accepts public HTTPS traffic on ports 80 and 443 and redirects unsecured HTTP traffic to an encrypted HTTPS connection. Nginx forwards matching requests to Kestrel at http://127.0.0.1.

  5. Specify the path to the certificate file and associated key. For example, you can generate a self-signed certificate file for testing purposes. For more information, refer to the following tutorial: How To Create a Self-Signed SSL Certificate for Nginx in Ubuntu 16.04.

  6. Run the following command to check that configuration file syntax is correct.

    sudo nginx –t
    
  7. Run the command below to apply the new configuration to Nginx.

    sudo nginx -s reload
    
  8. Navigate to the application directory and run the application.

    dotnet MainDemo.Blazor.Server.dll.
    
  9. Navigate to https://<your_host_name> in a browser to check that Nginx is working correctly with the application.

Monitor the Application

  1. Nginx cannot manage Kestrel application processes. You can use systemd to create a service file that starts and monitors the underlying web application.

  2. Create a service definition file.

    sudo nano /etc/systemd/system/kestrel-maindemo.service
    

    Service file example below starts and monitors the MainDemo.Blazor.Server application.

    [Unit]
    
    Description=XAF Blazor App running on Linux
    
    [Service]
    
    WorkingDirectory=/var/www/MainDemo.Blazor.Server
    ExecStart=/usr/bin/dotnet /var/www/MainDemo.Blazor.Server/MainDemo.Blazor.Server.dll
    Restart=always
    
    # Restart service after 10 seconds if the dotnet service crashes:
    
    RestartSec=10
    KillSignal=SIGINT
    SyslogIdentifier=dotnet-example
    User=www-data
    Environment=ASPNETCORE_ENVIRONMENT=Development
    Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
    
    [Install]
    
    WantedBy=multi-user.target
    
  3. The example above uses the /usr/bin/dotnet command to start the application. You can change this option if the dotnet runtime location is different, or if you have published your application with the --self-contained argument. For example:

    ExecStart=/var/www/MainDemo.Blazor.Server/MainDemo.Blazor.Server
    

    Make sure that the value of the WorkingDirectory option is correct. Otherwise, you may see the following error: “Could not find ‘xaf.focusViewItem’ (‘xaf’ was undefined)“.

    The User option specifies a user that manages the service. This user (www-data in this example) must exist and have proper ownership of application files.

  4. Save the file and enable the service.

    sudo systemctl enable kestrel-maindemo.service
    
  5. Start the service.

    sudo systemctl start kestrel-maindemo.service
    
  6. Verify that the service is running.

    sudo systemctl status kestrel-maindemo.service
    

    DevExpress XAF - The Service Is Running

Important Disclaimer

These deployment recommendations do not apply to all possible configurations and should not be considered comprehensive. We offer these instructions as a getting-started reference. Steps may vary depending on your operating system, installed software, and DevExpress versions. You, the developer, are responsible for the application, database, network, and other configurations based on your client, security, environment, and other requirements. We recommend that you review these settings with your database, network, and IT infrastructure administrators and consider their recommendations tailored to your case. We also recommend that you review Performance Optimization tips for deployed applications.

Troubleshooting

If you encounter problems, refer to the following topic: Deployment Troubleshooting Guide.

Additional Resources

Refer to the Microsoft documentation for more information about deploying ASP.NET Core and Blazor applications: