11

I'm using (ASP).NET Core (3.1x), C#, Blazor and Microsoft Kestrel Web-server and I'm wondering if I can run 2 or 3 different websites (domain names) on one Kestrel instance and on port 80. I would really like to use Kestrel as the only web server and not use a proxy server like nginx in front of it.

I've been googling for an answer for a while now but I can't find an answer to this question. I'm renting colocation server space where I run an Ubuntu 18.04 VPS, and I would really like to run multiple websites on this one VPS, instead of renting multiple VPS's. I am thinking about some sort of routing but I can't figure it out.

Is there any way to use Kestrel and run different websites on port 80?

Update - feb 25th 2020

I've created a Github issue about this, long story short: Use a reverse proxy server like Nginx (for Linux). Only one Kestrel process can run on port 80, and there no good way to host multiple websites with one instance.

Update - May 4th 2021

It is now possible with Microsoft's reverse proxy "YARP", which is a separate Kestrel instance. See my answer below.

Jaap
  • 665
  • 1
  • 8
  • 19
  • It is not recommended to use Kestrel without a proxy server... – enet Feb 04 '20 at 10:22
  • 1
    Not anymore I believe. Kestrel does a fantastic job. At [Qualys SSL Labs](https://www.ssllabs.com/) my website gets an A+ rating and at [Internet.nl](https://en.internet.nl/) it scores a solid 100% and they've implemented the latest security protocol checks. – Jaap Feb 04 '20 at 10:29
  • I'd guess not: ["Kestrel largely ignores the host name."](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-3.1#host-filtering). The host filtering middleware it links to filters out requests to other hosts, not splits requests between multiple applications. – Rup Feb 04 '20 at 10:36
  • If your intention is to run different web apps on different domains then I'd think you'd need multiple Kestrel instances, one for each app, and some mechanism of splitting traffic between them. And I'd guess that you can find a lightweight proxy that's much smaller than the Kestrel instances, so that's not the bit that's worth worrying about (I assume you're trying to save VPS memory?) – Rup Feb 04 '20 at 10:38
  • @rup Hmm, yes, that's what I thought. Kestrel is already a pretty lightweight and amazingly fast web server and is highly configurable concerning security, Really a state of the art web server, so I would like keep using Kestrel. – Jaap Feb 04 '20 at 10:48
  • Maybe you can create an application launching controllers and pages from others applications dynamically with [`AddApplicationPart`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.mvccoremvcbuilderextensions.addapplicationpart?view=aspnetcore-3.1#Microsoft_Extensions_DependencyInjection_MvcCoreMvcBuilderExtensions_AddApplicationPart_Microsoft_Extensions_DependencyInjection_IMvcBuilder_System_Reflection_Assembly_) extension – agua from mars Feb 04 '20 at 10:58
  • and create your map table using [`.Map`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.mapextensions.map?f1url=https%3A%2F%2Fmsdn.microsoft.com%2Fquery%2Fdev16.query%3FappId%3DDev16IDEF1%26l%3DEN-US%26k%3Dk(Microsoft.AspNetCore.Builder.MapExtensions.Map);k(SolutionItemsProject);k(DevLang-csharp)%26rd%3Dtrue&view=aspnetcore-3.1) – agua from mars Feb 04 '20 at 10:59
  • 1
    But this not gooing to be easy to maintain – agua from mars Feb 04 '20 at 10:59

4 Answers4

6

To run multiple domains on one server on port 80 and/or 443 with Kestrel you'll need to put a reverse proxy in front of it.

Microsoft has now its own really fast reverse proxy called YARP which is actually another Kestrel instance, see: Getting Started With Yarp

To use it with TLS/HTTPS you'll need Kestrel's SNI feature, see this GitHub discussion for more info

Here's a sample appsettings.Development.json GIST how I did it.

Pyritie
  • 543
  • 4
  • 16
Jaap
  • 665
  • 1
  • 8
  • 19
  • Do you have a suggestion on how I might use YARP to [map ports to controllers](https://stackoverflow.com/q/71481349)? The documentation is a bit daunting, especially for an ASP.NET 6 beginner such as myself (coming from WebForms). Perhaps you might consider adding any suggestion as an answer there. – InteXX Mar 15 '22 at 12:05
  • I managed to [solve my problem](https://stackoverflow.com/a/71490006). YARP looks pretty good, for more complex scenarios. I'll definitely keep it in mind. Good tip. – InteXX Mar 15 '22 at 23:27
2

There is no virtual host config in Kestrel like there is in IIS, so you can't do it without using IIS or something else in front of Kestrel. The best workaround is to bind another port like 12345 and create a reverse proxy for it.

For example, you can use Caddy server to act as a reverse proxy. In Ubuntu:

Install caddy server

The following script will install caddy. Caddy acts as a reverse proxy server which serves multiple website in the same port.

cat /etc/apt/sources.list.d/caddy-fury.list | grep -q caddy || echo "deb [trusted=yes] https://apt.fury.io/caddy/ /" | tee -a /etc/apt/sources.list.d/caddy-fury.list
apt update
apt install -y caddy

Add new proxy website

The following function adds a new reverse proxy config for caddy.

add_caddy_proxy()
{
    domain_name="$1"
    local_port="$2"
    cat /etc/caddy/Caddyfile | grep -q "an easy way" && echo "" > /etc/caddy/Caddyfile
    echo "
$domain_name {
    reverse_proxy /* 127.0.0.1:$local_port
}" >> /etc/caddy/Caddyfile
    systemctl restart caddy.service
}

Get a valid port

This function will find an empty internal port so your ASP.NET Core app can listen to.

get_port()
{
    while true; 
    do
        local PORT=$(shuf -i 40000-65000 -n 1)
        ss -lpn | grep -q ":$PORT " || echo $PORT && break
    done
}

Register ASP.NET Core web app

And this function helps you register an ASP.NET Core web app as a service and listening for a specific port.

register_service()
{
    service_name="$1" # my.service
    local_port="$2" # 12345
    run_path="$3" # .
    dll="$4" # MyProject.dll
    echo "[Unit]
    Description=$dll Service
    After=network.target
    Wants=network.target
    [Service]
    Type=simple
    ExecStart=/usr/bin/dotnet $run_path/$dll.dll --urls=http://localhost:$local_port/
    WorkingDirectory=$run_path
    Restart=always
    RestartSec=10
    KillSignal=SIGINT
    Environment=\"ASPNETCORE_ENVIRONMENT=Production\"
    Environment=\"DOTNET_PRINT_TELEMETRY_MESSAGE=false\"
    [Install]
    WantedBy=multi-user.target" > /etc/systemd/system/$service_name.service
    systemctl enable $service_name.service
    systemctl start $service_name.service
}

Run your ASP.NET Core app

After executing the previous steps, you can add your ASP.NET Core app behind caddy.

server="www.yourhostname.com"
app_path=/opt/myapp
port=$(get_port)

dotnet publish -c Release -o $app_path ./app.csproj
register_service "myapp" $port $app_path "MyApp"
add_caddy_proxy $server $port

You can do that multiple times so multiple ASP.NET Core app just runs on the same server.

Reference:

https://github.com/EdiWang/Moonglade/blob/master/Deployment/install.sh

Anduin Xue
  • 3,266
  • 2
  • 22
  • 43
0

Kestrel is best used with proxy server behind nginx or apache or iis, however it is not correct that kestrel do not support multiple apps on a single server.

There are various ways kestrel can be configured to run multiple apps and Microsoft's document describes it in great details. https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/endpoints?view=aspnetcore-5.0

The easiest is to setup the following in your appsettings.json file, change the url to any other port that you desire to support multiple apps, one app can be on port 4000 another on 4100 and so on. Configure according to the available free ports on that server.

{
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://localhost:5000"
      },
      "Https": {
        "Url": "https://localhost:5001",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "<certificate password>"
        }
      }
    }
  }
}
Kalpesh Popat
  • 1,416
  • 14
  • 12
  • 1
    Kestrel is (for years now) a fully qualified webserver, and with Microsoft's YARP (Yet Another Reverse Proxy, which is also a Kestrel instance) you can run multiple domains _on the same port_. with YARP in front of it. With TLS and all. See: https://microsoft.github.io/reverse-proxy/articles/getting_started.html Both Kestrel & YARP are really really fast also. So no need for NGINX or Apache anymore. – Jaap May 04 '21 at 08:08
0

You can host a URL in Apache and redirect the folders to kestrel using the conf files.

In the conf file (See example below) You can also define environment variables.

#Define APP_CONTEXT Your-Context e.g. Test/Production/Demo
#Define APP_URL Your-URL e.g. http://127.0.0.1
#Define APP_PORT_START Your-Port e.g.  5
#
<VirtualHost *>
<LocationMatch /${APP_CONTEXT}/app1>
    ProxyPreserveHost On
    ProxyPass ${APP_URL}:${APP_PORT_START}001
    ProxyPassReverse ${APP_URL}:${APP_PORT_START}001
</LocationMatch>
<LocationMatch /${APP_CONTEXT}/app2>
    ProxyPreserveHost On
    ProxyPass ${APP_URL}:${APP_PORT_START}002
    ProxyPassReverse ${APP_URL}:${APP_PORT_START}002
</LocationMatch>
<LocationMatch /${APP_CONTEXT}/app3>
    ProxyPreserveHost On
    ProxyPass ${APP_URL}:${APP_PORT_START}003
    ProxyPassReverse ${APP_URL}:${APP_PORT_START}003
</LocationMatch>