NGINX.COM
Web Server Load Balancing with NGINX Plus

About two years ago Microsoft® announced .NET Core, a framework that allows you to develop and run .NET applications natively on Linux and Mac systems. ASP.NET Core includes Kestrel, an internal web server library.

As indicated in the documentation for Kestrel on the Microsoft website and the GitHub repository, you typically run Kestrel behind a production web server such as IIS or NGINX. In this tutorial, we’ll describe how to implement Kestrel behind NGINX and NGINX Plus.

In this tutorial you’ll learn how to:

When the installation and configuration are complete:

  • .NET Core and Kestrel:

    • Run the dynamic application code
    • Listen on a local IP address and respond to HTTP requests
  • NGINX or NGINX Plus, acting as a reverse proxy:

    • Accepts HTTP/2 traffic over IPv6 and IPv4
    • Provides SSL offload for the .NET application
    • Provides static file serving
    • Provides access logs
    • Adds caching
  • NGINX Plus:

    • Provides live activity monitoring and metrics
    • Ensures the app is working by means of active health checks

The .NET Core application deployment architecture is similar to the deployment architecture of Node.js or Go applications. NGINX provides .NET apps with traffic management features that simplify production deployment and scalability of the apps. You can run multiple .NET applications on the same or different machines, and NGINX or NGINX Plus performs load balancing and intelligent traffic routing between them.

Instructions

The following instructions explain how to quickly build a “Hello World” app using .NET Core, run it on Linux, and deploy it behind an NGINX or NGINX Plus reverse proxy with advanced traffic‑management functionality.

  1. Install .NET Core, NGINX, and NGINX Plus
  2. Run the “Hello World” App
  3. Run the Kestrel HTTP Server
  4. Configure NGINX or NGINX Plus to Reverse Proxy the .NET Application
  5. Configure NGINX Plus Live Activity Monitoring and Active Health Checks

Install .NET Core, NGINX, and NGINX Plus

  1. Install .NET Core using the instructions on the Microsoft website.

    In our example we’re using Ubuntu 16.04. The following commands were correct at the time of writing, but are subject to change because Kestrel is still under development. Consult the .NET Core documentation as necessary.

    $ sudo apt-get install apt-transport-https
    $ sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ xenial main" > /etc/apt/sources.list.d/dotnetdev.list'
    $ sudo apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893
    $ sudo apt-get update
    $ sudo apt-get install dotnet-dev-1.0.0-preview2-003131
  2. Install NGINX:

    $ sudo apt-get install nginx
  3. Install NGINX Plus if you want live activity monitoring, active health checks, or both. See the instructions in the NGINX Plus Admin Guide.

  4. Run the “Hello World” App

    Install and initialize a “Hello World” app in the parent directory of your choice:

    $ cd parent-dir-for-apps
    $ mkdir app
    $ cd app
    $ dotnet restore

    To check that the app is working, run the dotnet run command.

    Run the Kestrel HTTP Server

    1. Edit the ./project.json file to add Kestrel as a dependency to the project.

      {
        "version": "1.0.0-*",
        "buildOptions": {
          "debugType": "portable",
          "emitEntryPoint": true
        },
        "dependencies": {},
        "frameworks": {
          "netcoreapp1.0": {
            "dependencies": {
              "Microsoft.NETCore.App": {
                "type": "platform",
                "version": "1.0.1"
              },
              "Microsoft.AspNetCore.Server.Kestrel": "1.0.0"
            },
            "imports": "dnxcore50"
          }
        }
      }
    2. Copy this code for a simple app to a new file called Program.cs. It returns the current date and time, running Kestrel on port 5000 on localhost.

      using System;
      using Microsoft.AspNetCore.Builder;
      using Microsoft.AspNetCore.Hosting;
      using Microsoft.AspNetCore.Http;
      
      namespace ConsoleApplication
      {
          public class Program
          {
              public static void Main(string[] args)
              {
                  var host = new WebHostBuilder()
                      .UseKestrel()
                      .Configure(app =>
                      {
                          app.Run(async (context) => await context.Response.WriteAsync("Current date: " + DateTime.Now + "n"));
                      })
                      .Build();
      
                  host.Run();
              }
          }
      }
    3. Run the dotnet run command to start the .NET Core server:

      $ dotnet run
      Project app (.NETCoreApp,Version=v1.0) will be compiled because inputs were modified
      Compiling app for .NETCoreApp,Version=v1.0
      
      Compilation succeeded.
          0 Warning(s)
          0 Error(s)
      
      Time elapsed 00:00:01.9047678
      
      Hello World!
      Hosting environment: Production
      Content root path: /app/bin/Debug/netcoreapp1.0
      Now listening on: http://localhost:5000
      Application started. Press Ctrl+C to shut down.
    4. Run the curl command to test connectivity and HTTP:

      $ curl -v localhost:5000
      * Rebuilt URL to: localhost:5000/
      *   Trying ::1...
      * Connected to localhost (::1) port 5000 (#0)
      > GET / HTTP/1.1
      > Host: localhost:5000
      > User-Agent: curl/7.47.0
      > Accept: */*
      >
      < HTTP/1.1 200 OK
      < Date: Tue, 14 Feb 2017 19:50:59 GMT
      < Transfer-Encoding: chunked
      < Server: Kestrel
      <
      Current date: 02/14/17 12:50:59 PM
      * Connection #0 to host localhost left intact

    At this point .NET Core is running on Linux and serving dynamic data using Kestrel as the HTTP server.

    Configure NGINX or NGINX Plus to Reverse Proxy the .NET Application

    With NGINX or NGINX Plus as a reverse proxy for the .NET application, you can easily configure security with SSL/TLS, HTTP/2 support, and many other features for fast application delivery on the same machine where the .NET Core application is running. The following instructions assume that NGINX and NGINX Plus are already installed on your system; if not, see Install .NET Core, NGINX, and NGINX Plus.

    1. Install an SSL certificate. There are several ways to obtain one:

      • Buy it from a well‑known certificate authority (CA)
      • Have your corporate IT group or CA generate it
      • Export it from an existing IIS server
      • Use a free CA like Let’s Encrypt
      • Generate a self‑signed certificate directly

      For the purposes of quickly spinning up a sample .NET Core app with SSL, we’re generating a self‑signed certificate and associated key with this openssl command. We’re installing the certificate and key in the standard location for NGINX, /etc/nginx, but you can choose a different location.

      $ openssl req -x509 -subj /CN=localhost -days 365 -set_serial 2 -newkey rsa:4096 -keyout /etc/nginx/cert.key -nodes -out /etc/nginx/cert.pem
      Generating a 4096 bit RSA private key
      .........++
      ............................++
      writing new private key to '/etc/nginx/cert.key'
      -----
    2. Configure reverse proxy in the default NGINX and NGINX Plus configuration file for HTTP virtual servers.

      The main NGINX and NGINX Plus configuration file is /etc/nginx/nginx.conf. However, several NGINX distributions (as well as NGINX Plus) follow the convention that you do not place much actual configuration in the main file, but instead create smaller, function‑specific files in a subdirectory of /etc/nginx:

      • For NGINX Open Source builds provided by nginx.org, and for NGINX Plus, the directory is /etc/nginx/conf.d, and the default file for HTTP virtual servers is default.conf.
      • For NGINX Open Source builds distributed with Ubuntu, the directory is /etc/nginx/sites-enabled, and the default file for HTTP virtual servers is default.

      The content of the function‑specific files in these directories is then read into the main (nginx.conf) file with an include directive, for example:

      include /etc/nginx/conf.d/*.conf;
      include /etc/nginx/sites-enabled/*;

      If you are not sure which is the default configuration file for HTTP virtual servers on your system, find the relevant include directive in /etc/nginx/nginx.conf.

      To configure NGINX or NGINX Plus as a reverse proxy, add the following three configuration blocks to the default configuration file for HTTP virtual servers:

      • The first server block accepts HTTP requests on port 80 and redirects them to the virtual server for HTTPS requests.

      • The second server block accepts HTTPS requests on port 443 and proxies them to a group of one or more upstream (backend) servers, here called dotnet. (If in Step 1 you installed your self‑signed SSL certificate in a directory other than /etc/nginx, substitute the correct path in the ssl_certificate and ssl_certificate_key directives.)

      • The upstream block defines the dotnet group of backend servers.

        In Run the Kestrel HTTP Server, we configured Kestrel on localhost:5000, meaning that it listens for both IPv4 and IPv6 traffic on that port. (Configuring Kestrel for just one protocol can cause instability and potentially 502 errors.) Similarly, NGINX and NGINX Plus resolve localhost to both its IPv4 and its IPv6 address (127.0.0.1 and ::1). For the sake of simplicity, here we identify the upstream server as 127.0.0.1 instead of localhost, so it listens for IPv4 traffic only. You can use localhost if you are comfortable with a more advanced configuration that includes IPv6.

      server {
          listen 80 default_server;
          listen [::]:80 default_server;
          return 301 https://$host$request_uri;
      }
      
      server {
          listen 443 ssl http2 default_server;
          listen [::]:443 ssl http2 default_server;
      
          ssl_certificate /etc/nginx/cert.pem;
          ssl_certificate_key /etc/nginx/cert.key;
      
          location / {
              proxy_pass http://dotnet;
              proxy_set_header Host $host;
          }
      }
      
      upstream dotnet {
          zone dotnet 64k;
          server 127.0.0.1:5000;
      }
    3. Run this curl command to test connectivity to the .NET Core app via HTTPS. (You can also point your browser at your Linux server instead.)

      $ curl -kv https://localhost
      * Rebuilt URL to: https://localhost/
      *   Trying ::1...
      * Connected to localhost (::1) port 443 (#0)
      ...[SKIPPED]...
      < HTTP/1.1 200 OK
      < Server: nginx/1.10.0 (Ubuntu)
      < Date: Tue, 14 Feb 2017 20:22:07 GMT
      < Transfer-Encoding: chunked
      < Connection: keep-alive
      <
      Current date: 02/14/17 1:22:07 PM

      Note: If you see 502 Bad Gateway errors, it means that NGINX or NGINX Plus cannot connect to your .NET application. Make sure it’s running and serving responses on port 5000.

      If you have installed the nghttp2 package, you can also run the following nghttp command to test connectivity over HTTP/2. Look for the line highlighted in orange in the following example, near the beginning of the rather lengthy output.

      $ nghttp -v https://localhost
      [  0.000] Connected
      The negotiated protocol: h2
      [  0.009] send SETTINGS frame 
                (niv=2)
                [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
                [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
      [  0.009] send PRIORITY frame 
                (dep_stream_id=0, weight=201, exclusive=0)

    Configure NGINX Plus Live Activity Monitoring and Active Health Checks

    At this point we’ve finished the basic configuration of NGINX or NGINX Plus with .NET Core. NGINX or NGINX Plus is providing HTTP handling, passive health checks, security with SSL/TLS, and HTTP/2 connectivity for our .NET Core app.

    If you have installed NGINX Plus, you can configure two additional capabilities: live activity monitoring and active health checks.

    Configure Live Activity Monitoring

    [Editor – This section has been updated to refer to the NGINX Plus API, which replaces and deprecates the separate extended Status module originally discussed here.]

    Add the following server block to the default NGINX configuration file for HTTP virtual servers. We strongly recommend that you restrict access to the statistics and metrics. Here we allow access only to users on localhost and a local network.

    For more information on live activity monitoring, see Live Activity Monitoring of NGINX Plus in 3 Simple Steps on our blog and the NGINX Plus Admin Guide.

    server {
        listen 8080;
        allow 127.0.0.1;   # Allow localhost to access the statistics
        allow 10.3.3.0/24; # Allow local network to access the statistics
        deny all;          # Prevent access from anywhere else
    
        root /usr/share/nginx/html;
    
        location / {
            return 302 /dashboard.html;
        }
    
        location /api {
            api write=on;
        }
    
        location = /dashboard.html {
            root /usr/share/nginx/html;
        }
    
        # Redirect requests made to the old dashboard
        location = /status.html {
            return 301 /dashboard.html;
        }
    }

    Configure Active Health Checks

    Active health checks guarantee that NGINX Plus sends traffic only to applications that are working correctly. You define the HTTP requests that NGINX Plus periodically sends to the app, and the type of response that the app must return to be considered healthy.

    Here we require that the response from the app meets the following conditions:

    • The response code is 200 OK
    • The app server is Kestrel and not some other software
    • The body of the response includes the words “Current date”
    • The app responds within a 1‑second timeout period

    In the default configuration file for HTTP virtual servers, add the following location block to the main server block (the block for HTTPS traffic defined in Step 2 of Configure NGINX or NGINX Plus to Reverse Proxy the .NET Application):

    location @healthcheck {
        proxy_pass http://dotnet;
        proxy_set_header Host localhost;
        health_check match=currentdate;
        proxy_connect_timeout 1s;
        proxy_read_timeout 1s;
    }

    Also add the following match block at the same level in the hierarchy as the server and upstream blocks:

    match currentdate {
        status 200;
        header Server = Kestrel;
        body ~ "Current date";
    }

    You can verify that your backend app is healthy on the Upstreams tab of the built‑in live activity monitoring dashboard (point your browser at //http://nginx-plus-server-address:8080/):

    The NGINX Plus live activity monitoring dashboard reports the health of the backend .NET applications NGINX is proxying

    For more NGINX configuration options, see the Microsoft documentation.

    Conclusion

    For production‑ready deployments of the apps you develop with ASP.NET, NGINX and NGINX Plus provide the traffic‑management features you need in a reverse proxy. NGINX and NGINX Plus provide security, scalability, authentication, traffic limiting, and intelligent routing of your HTTP requests to .NET Core applications.

    To try NGINX Plus for yourself, start your free 30-day trial today or contact us to discuss your use cases.

Hero image
Free O'Reilly eBook: The Complete NGINX Cookbook

Updated for 2024 – Your guide to everything NGINX



About The Author

Nick Shadrin

Software Architect

About F5 NGINX

F5, Inc. is the company behind NGINX, the popular open source project. We offer a suite of technologies for developing and delivering modern applications. Together with F5, our combined solution bridges the gap between NetOps and DevOps, with multi-cloud application services that span from code to customer.

Learn more at nginx.com or join the conversation by following @nginx on Twitter.