An in-depth look at the various ways of specifying the IP or host ASP.NET Core listens on

Posted on Friday, 10th February 2017

Recently I’ve been working on an ASP.NET Core application where I’ve needed to configure at runtime, the host address and port the webhost will listen on. For those that have built an ASP.NET Core app you’ll know that the default approach generated by the .NET CLI is less than ideal in this case as it’s hard-coded.

After a bit of digging I quickly realised there weren’t any places that summarised all of the options available, so I thought I’d summarise it all in a post.

Enough talk, let’s begin.

Don’t set an IP

The first approach is to not specify any IP address (this means removing the .NET Core CLI template convention of using the .UseUrls() method. Without it the web host will listen on localhost:5000 by default.

Whilst this approach is far from ideal, it is an option so deserves a place here.

Hard-coded approach via .UseUrls()

As I alluded to earlier, the default approach that .NET Core’s CLI uses is to hard-code the IP address in your application’s program.cs file via the UseUrls(...) extension method that’s available on the IWebHostBuilder interface.

If you take a look at the UseUrls extension method’s signature you’ll see the argument is an unbounded string array allowing you to specify more than one IP address that the web host will listen on (which may be preferable to you depending on your development machine, network configuration or preference as specifying more than one host address can save people running into issues between localhost vs 0.0.0.0 vs 127.0.0.1).

public static IWebHostBuilder UseUrls(this IWebHostBuilder hostBuilder, params string[] urls);

Adding multiple IP addresses can either be done by comma-separated strings, or a single string separated by a semi-colon; both result in the same configuration.

var host = new WebHostBuilder()
    .UseConfiguration(config)
    .UseKestrel()
    .UseUrls("http://0.0.0.0:5000", "http://localhost:5000")
    ///.UseUrls("http://0.0.0.0:5000;http://localhost:5000") also works
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseIISIntegration()
    .UseStartup<Startup>()
    .Build();

If you’d rather not explicitly list every IP address to listen on you can use a wildcard instead resulting in the web host binding to all IPv4 and IPv6 IPs addresses on port 5000:

.UseUrls("http://*:5000")

A bit about wildcards: The wildcard is not special in any way, in fact anything not recognised as an IP will be bound to all IPv4 or IPv6 addresses, so http://@£$%^&*:5000 is considered the same as "http://*:5000" and vice versa.

Whilst this hard-coded approach makes it easy to get your application up and running, the very fact that it’s hard-coded does make it difficult to configure externally via automation such as a continuous integration/deployment pipeline.

Note: It’s worth mentioning that setting a binding IP address directly on the WebHost as we are in this approach always takes preference over any of the other approaches listed in this post, but we’ll go into this later.

Environment variables

You can also specify the IP your application listens on via an environment variable. To do this first you’ll need to download the Microsoft.Extensions.Configuration package from NuGet then call the AddEnvironmentVariables() extension method on your ConfigurationBuilder object like so:

public static void Main(string[] args)
{
    var config = new ConfigurationBuilder()
        .AddEnvironmentVariables()
        .Build();

    var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseConfiguration(config)
        .UseStartup<Startup>()
        .Build();

    host.Run();
}

Now if you were to set the following environment variable and run your application it will listen to the IP address specified:

ASPNETCORE_URLS=https://*:5123

Command line argument

The another option available is to supply the host name and port via a command line argument when your application’s initially executed (notice how you can use one or more addresses as we did above).

dotnet run --urls "http://*:5000;http://*:6000"

or

dotnet YourApp.dll --urls "http://*:5000;http://*:6000"

Before you can use command line arguments, you’re going to need the Microsoft.Extensions.Configuration.CommandLine package and update your Program.cs bootstrap configuration accordingly:

public static void Main(string[] args)
{
    var config = new ConfigurationBuilder()
        .AddCommandLine(args)
        .Build();

    var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseConfiguration(config)
        .UseStartup<Startup>()
        .Build();

    host.Run();
}

Notice how I’ve removed the .ForUrls() method; this prevents .ForUrls() overwriting the IP address provided via command line.

hosting.json approach

Another popular approach to specifying your host and port address is to read the IP address from a .json file during the application boot up. Whilst you can name your configuration anything, the common approach appears to be hosting.json, with the contents of your file containing the IP address you want your application to listen on:

{
  "urls": "http://*:5000"
}

In order to use this approach you’re first going to need to include the Microsoft.Extensions.Configuration.Json package, allowing you to load configurations via .json documents.

public static void Main(string[] args)
{
    var config = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("hosting.json", optional: true)
        .Build();

    var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseConfiguration(config)
        .UseStartup<Startup>()
        .Build();

    host.Run();
}

Now when you run the dotnet run or dotnet YourApp.dll command you’ll noticed the output will reflect the address specified within your hosting.json document.

Just a reminder that before publishing your application be sure to include your hosting file in your publishing options (in either your project.json or your .csproj file:

// project.json

"publishOptions": {
    "include": [
      "wwwroot",
      "Views",
      "appsettings.json",
      "web.config",
      "hosting.json"
    ]
}
// YourApp.csproj

<ItemGroup>
  <Content Update="wwwroot;Views;appsettings.json;web.config;hosting.json">
  <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
  </Content>
</ItemGroup>

Out of all of the approaches available this has to be my most preferred option. It’s simple enough to overwrite or modify within your test/release pipeline whilst removing hurdles co-workers may need to jump through when wanting to download your source code and run the application (as opposed to the command line approach).

Order of preference

When it comes down to the order the IP addresses are loaded, I would recommend you check out the documentation here, especially this snippet:

You can override any of these environment variable values by specifying configuration (using UseConfiguration) or by setting the value explicitly (using UseUrls for instance). The host will use whichever option sets the value last. For this reason, UseIISIntegration must appear after UseUrls, because it replaces the URL with one dynamically provided by IIS. If you want to programmatically set the default URL to one value, but allow it to be overridden with configuration, you could configure the host as follows:

var config = new ConfigurationBuilder()
   .AddCommandLine(args)
   .Build();

var host = new WebHostBuilder()
   .UseUrls("http://*:1000") // default URL
   .UseConfiguration(config) // override from command line
   .UseKestrel()
   .Build();

Conclusion

Hopefully this post helped you gain a better understanding of the many options available to you when configuring what IP address you want your application to listen on, writing it has certainly helped me cement them in my mind!