Using Docker env vars in .NET Core

One really good thing about packing your apps with Docker is that you're building once and deploying many times in many different places (environments), because of this it's a good idea to make your app configurable at the environment level (not just files). This means that you should avoid having config files in the container that can be different in each environment you run the container, "An app’s config is everything that is likely to vary between deploys" (The 12 Factor App).

But why environment variables?

The idea comes from the 12 factor app methodology (https://12factor.net/config), where it says that we should prefer using this type of configuration because having files can compromise sensitive information (security in Docker is not that mature yet because you can easily inspect the container and see all the environment variables, but that's an interesting talk for another time).

I'm not going to repeat what you already read in the previous link but I want to focus in the following: "In a twelve-factor app, env vars are granular controls, each fully orthogonal to other env vars. They are never grouped together as 'environments', but instead are independently managed for each deploy. This is a model that scales up smoothly as the app naturally expands into more deploys over its lifetime." (The 12 Factor App)

So, the idea is that we should always have our app prepared to behave accordingly in each environment, we should not have to re-compile our code just because we need to change some configuration, imagine doing that in all of the software you use in your computer.

That's good, but how can this be done in .NET Core?

I'm glad you ask. By this moment I hope you are convinced that env vars are a good option and lucky for us this is how Docker works, we just need to know how to achieve this behavior in .NET Core. For this, there's also good documentation here but in this post I will focus only on how to read env vars that comes from Docker.

.NET Core has a really good integration with env vars, actually the default template for a Web API use it as default, so we just need to make a few changes to our code in the Startup.cs class, create a new POCO class and use it in our Controllers by injecting the configuration we need.

Changes in the app

While I list the changes we need to make in the app, I'm going to give some explanation in each step. I have uploaded the code sample in Github, so in I couldn't explain myself enough you can see the source code here. Here's the list of steps:

var builder = new ConfigurationBuilder()
  .SetBasePath(env.ContentRootPath)
  ... other method calls
  .AddEnvironmentVariables();
public class EnvironmentConfig
{
     public string ProfileName { get; set; }
}
services.Configure<EnvironmentConfig>(Configuration);
private readonly EnvironmentConfig _configuration;

public ValuesController(IOptions<EnvironmentConfig> configuration)
{
    _configuration = configuration.Value;
}

In case you don't see it, one of the cool things by doing it in this way is that if you don't specify an env var called "ProfileName" your app won't crash (unless you want/need to) because when you read the property of the configuration class it will be empty.

And that's all, if you want to deep dive in how configuration can be modified/used please read the following article here

How to test and debug this with VS?

I assume you already have added Docker support to your solution, if not take a look to this article. In your solution, open the docker-compose.override.yml file inside docker-compose section and in the environment list the ProfileName var, take a look:

env var docker compose

Go and debug in Docker mode and the app should read that value from the container environment.

But what about Docker?

Yes, I know I almost forgot about this. But you know what? This is really simple, I'm going to put here the way I run my container so you can see how to define the env var that my .NET Core app will read.

docker run -e ProfileName='Christian' -d -p 9000:80 christianhxc/hellodocker:env

Now I need to go to: http://localhost:9000/api/values and boom! I see my name there. Of course, you don't need to specify all env vars in the run command, you can make use of docker compose to group them, but remember: don't include the any config file with the source code in version control.

You don't need to follow this guide to test it, just run the above command with different values :)