How to automatically choose a free port in ASP.NET Core 3.0
Tags: aspnetcore, netcore30, routing
Published: 2020-05-03 17:50:09

---
title: How to automatically choose a free port in ASP.NET Core 3.0
published: true
date: 2020-04-14 10:00:00 UTC
tags: ASP.NET Core,.NET Core 3.0,Routing
canonical_url: https://andrewlock.net/how-to-automatically-choose-a-free-port-in-asp-net-core/
---

 ![How to automatically choose a free port in ASP.NET Core 3.0](https://andrewlock.net/content/images/2020/random_port_banner.jpg)

This post is in response to a discussion I had with a friend recently who was trying out .NET Core. Unfortunately, when they attempted to start their new application they received the following message:

```bash
crit: Microsoft.AspNetCore.Server.Kestrel[0]
      Unable to start Kestrel.
System.IO.IOException: Failed to bind to address http://127.0.0.1:5000: address already in use.
```

When you create a new .NET project using a template, it always uses the same URLs, defined in

- [http://localhost:5000](http://localhost:5000)
- [https://localhost:5001](https://localhost:5001)

Unfortunately, the MacBook had a driver installed that was already bound to port 5000, so whenever the .NET Core project attempted to start, the port would conflict, and they'd the see error above. Not a great experience!

In this post I show one way to resolve the problem by randomising the ports ASP.NET Core uses when it starts the application. I'll also show how you can work out which port the application has selected from inside your app.

## Randomly selecting a free port in ASP.NET Core

[In my previous post](https://andrewlock.net/5-ways-to-set-the-urls-for-an-aspnetcore-app/), I showed some of the ways you can set the URLs for your ASP.NET Core application. Unfortunately, all of those approaches still require that you choose a port to use. When you're developing locally, you might not care about that, just run the application!

You can achieve exactly this by using the special port `0` when setting the URL to use. For example, to bind to a random http and https port on the loopback (localhost) address, run your application using the following command:

```bash
dotnet run --urls "http://[::1]:0;https://[::1]:0"
```

This will randomly select a pair of ports that aren't currently in use, for example:

```bash
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://[::1]:54213
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://[::1]:54214
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
```

Alternatively, instead of binding to the loopback address, you can bind to _any_ IP address (using a random port) with the following command:

```bash
dotnet run --urls "http://*:0"
```

This binds to all IPv4 and IPv6 addresses on a random port.

> The `*` isn't actually special, you just need to use something that isn't a valid IPv4 or IPv6 IP address (or `localhost`). Even a hostname is treated the same as `*` i.e. it binds to all IP addresses on the machine.

The downside of choosing at random port at runtime is that you get a _different_ pair of ports every time you run the application. That may or may not be a problem for you.

## When is this useful?

On the face of it, having your application listen on a different URL _every time_ you restart it doesn't sound very useful. It would be _incredibly_ irritating to have to type a new URL into your browser (instead of just hitting refresh) every time you restart the app. So why would you do this?

The one time I use this approach is when building [worker services](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio) that run background tasks in Kubernetes.

> But wait, isn't the whole point of worker services that they **don't** run Kestrel and expose URLs?

Well, yes, but due to [the issues in the 2.x implementation of worker services](https://andrewlock.net/the-asp-net-core-generic-host-namespace-clashes-and-extension-methods/), I typically still use a full `WebHost` based ASP.NET Core app, instead of a generic `Host` app. Now, in ASP.NET Core 3.0, those problems have been resolved, but I _still_ don't use the generic host…

The problem is, I'm running applications in Kubernetes. An important part of that is having liveness/health checks, that check that the application hasn't crashed. The typical approach is to expose an HTTP or TCP endpoint that the Kubernetes infrastructure can call, to verify the application hasn't crashed.

Exposing an HTTP or TCP endpoint…that means, you guessed it, Kestrel!

> An HTTP/TCP health check endpoint is very common for applications, but there are [other options](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-a-liveness-command). For example you could use a command that checks for the presence of a file, or some other mechanism. I'd be interested to know in the comments if you're using a different mechanism for health checks of your worker services!

When the application is running in Kubernetes, the application obviously needs to use a known URL, so I don't use random port selection running when it's running in production. But when developing _locally_ on my dev machine, I don't care about the port at all. Running locally, I only care that the background service is running, not the health check endpoint. So for those services, the random port selection works perfectly.

## How do I found out which port was selected?

For the scenario I've described above, it really doesn't matter which port is selected, as it's not going to be used. But in some cases you may _need_ to determine that at runtime.

You can find out which port (and IP Address) your app is listening on using the `IServerAddressesFeature`, using the following approach:

```csharp
var server = services.GetRequiredService<IServer>();
var addressFeature = server.Features.Get<IServerAddressesFeature>();
foreach(var address in addressFeature.Addresses)
{
    _log.LogInformation("Listing on address: " + address);
}
```

> Note that Kestrel logs this information by default on startup, so you shouldn't need to log it yourself. You might need it for other purposes though, to register with [Consul](https://www.consul.io/api/agent/service.html) for example, so logging is just a simple example.

The question is, where should you write that code? Depending on where you put it, you can get very different answers.

For example, if you put that code in a hosted service in ASP.NET Core 3.0, then the `Addresses` collection on `addressFeature` will be null! That's because in ASP.NET Core 3.0, the [hosted services are started _before_ the middleware pipeline and Kestrel are configured](https://andrewlock.net/running-async-tasks-on-app-startup-in-asp-net-core-3/). So that's no good.

You might consider placing it inside `Startup.Configure()`, where you can easily access the server features on `IApplicationBuilder`:

```csharp
public class Startup
{
    public void Configure(IApplicationBuilder app, ILogger<Startup> log)
    {
        // IApplicationBuilder exposes an IFeatureCollection property, ServerFeatures
        var addressFeature = app.ServerFeatures.Get<IServerAddressesFeature>();
        foreach(var address in addressFeature.Addresses)
        {
            _log.LogInformation("Listing on address: " + address);
        }
    }

    // ... other configuration
}
```

Unfortunately, that doesn't work _either_. In this case, `Addresses` isn't empty, but it contains the values you provided with the `--urls` command, or using the `ASPNETCORE_URLS` variable, with the port set to 0:

```bash
Listing on address: http://*:0
Listing on address: http://[::1]:0
```

That's not very useful either, we want to know which ports are chosen!

The only safe place to put the code is somewhere that will run _after_ the application has been completely configured, and Kestrel is handling requests. The obvious place is in an MVC controller, or in middleware.

The following middleware shows how you could create a simple endpoint that returns the addresses being used as a comma delimited string:

```csharp
public class ServerAddressesMiddleware
{
    private readonly IFeatureCollection _features;
    public ServerAddressesMiddleware(RequestDelegate _, IServer server)
    {
        _features = server.Features;
    }

    public async Task Invoke(HttpContext context)
    {
        // fetch the addresses
        var addressFeature = _features.Get<IServerAddressesFeature>();
        var addresses = addressFeature.Addresses;

        // Write the addresses as a comma separated list
        await context.Response.WriteAsync(string.Join(",", addresses));
    }
}
```

We can add this middleware as an endpoint:

```csharp
public class Startup
{
    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app)
    {
        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            // Create the address endpoint, consisting of our middleware
            var addressEndpoint = endpoints
                .CreateApplicationBuilder()
                .UseMiddleware<ServerAddressesMiddleware>()
                .Build();

            // Register the endpoint
            endpoints.MapGet("/addresses", addressEndpoint);
        });
    }
}
```

Now when you hit the `/addresses` endpoint, you'll finally get the _actual_ addresses your application is listening on:

![Image of the addresses in use being shown](https://andrewlock.net/content/images/2020/random_port.png)

Of course, middleware is clearly not the place to be handling this sort of requirement, as you would need to know the URL to call _before_ you call the URL that tells you what URL to call! 🤪 The point is just that this information isn't available until _after_ you can handle requests!

So where _can_ we put this code?

One option is to hook into the `IHostApplicationLifetime` lifetime events. These events are [triggered at various points in your application's lifetime](https://andrewlock.net/introducing-ihostlifetime-and-untangling-the-generic-host-startup-interactions/), and give the option of running a synchronous callback.

For example, the following code registers a callback that waits for Kestrel to be fully configured, and then logs the addresses:

```csharp
public class Startup
{
    public void Configure(IApplicationBuilder app, IHostApplicationLifetime lifetime, ILogger<Startup> logger)
    {
        // Register a callback to run after the app is fuly configured
        lifetime.ApplicationStarted.Register(
          ()=> LogAddresses(app.ServerFeatures, logger));

        // other config

    }

    // Called after configuration is complete
    static void LogAddresses(IFeatureCollection features, ILogger logger)
    {
        var addressFeature = features.Get<IServerAddressesFeature>();
      // Do something with the addresses
        foreach(var addresses in addressFeature.Addresses)
        {
            logger.LogInformation("Listening on address: " + addresses);
        }
    }
}
```

This approach gives you access to your application's URLs at one of the earliest points they're available in your application's lifetime. Just be aware that the callback can't be async, so you can't do anything especially fancy there!

## Summary

In this post I described how to use the "magic port 0" to tell your ASP.NET Core application to choose a random port to listen on. I use this approach locally when creating background services that I don't need to make HTTP requests to (but which I want to expose an HTTP endpoint for liveness checks in Kubernetes).

I also showed how you can find out the actual URLs your application is listening on at runtime using the `IServerAddressesFeature`. I showed that you need to be careful when you call this feature - calling it too early in your application's startup could give you either an _empty_ list of addresses, the _requested_ list of addresses (i.e. the "port 0" addresses), or the _actual_ addresses. Make sure to only use this feature after application configuration is complete, for example from middleware, from an MVC controller, or in the `IHostApplicationLifetime.ApplicationStarted` callback.