build configurations
app.config
software development
configuration management
.NET

How to select different app.config for several build configurations

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Using one app.config for every environment usually leads to accidental endpoint leaks, noisy debugging, and fragile manual edits before release. Projects with Debug, Staging, and Release builds need deterministic configuration selection as part of the build itself. A robust setup combines environment-specific config files, build-time selection, and automated validation in CI.

Baseline Approach: One Base Config Plus Per-Environment Variants

A practical pattern is keeping shared defaults in App.config and storing environment overrides in dedicated files.

Example base config:

xml
1<?xml version="1.0" encoding="utf-8" ?>
2<configuration>
3  <appSettings>
4    <add key="ApiBaseUrl" value="https://api.example.com" />
5    <add key="LogLevel" value="Information" />
6  </appSettings>
7</configuration>

Example debug variant:

xml
1<?xml version="1.0" encoding="utf-8" ?>
2<configuration>
3  <appSettings>
4    <add key="ApiBaseUrl" value="https://dev-api.example.com" />
5    <add key="LogLevel" value="Debug" />
6  </appSettings>
7</configuration>

The key is that build output should always receive the correct file automatically.

Build-Time Selection with MSBuild

If XML transforms are not available, an explicit MSBuild copy target is simple and transparent.

xml
1<Project Sdk="Microsoft.NET.Sdk">
2  <PropertyGroup>
3    <TargetFramework>net48</TargetFramework>
4  </PropertyGroup>
5
6  <Target Name="CopyEnvConfig" AfterTargets="Build">
7    <PropertyGroup>
8      <SourceConfig>Configs\App.$(Configuration).config</SourceConfig>
9      <DestConfig>$(OutDir)$(TargetFileName).config</DestConfig>
10    </PropertyGroup>
11
12    <Message Text="Copying $(SourceConfig) to $(DestConfig)" Importance="high" />
13    <Copy SourceFiles="$(SourceConfig)" DestinationFiles="$(DestConfig)" />
14  </Target>
15</Project>

This makes configuration selection visible in build logs and easier to debug than manual post-build edits.

Transform-Based Selection

If your tooling supports transforms, you can keep a single source config and apply environment deltas.

xml
1<?xml version="1.0" encoding="utf-8" ?>
2<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
3  <appSettings>
4    <add key="ApiBaseUrl"
5         value="https://staging-api.example.com"
6         xdt:Transform="SetAttributes(value)"
7         xdt:Locator="Match(key)" />
8  </appSettings>
9</configuration>

Transforms are concise when only a few values differ, but they require careful locator rules to avoid duplicate keys.

Validate Generated Config in CI

Treat generated config as a build artifact that must pass checks. A fast validation step catches environment mistakes before deployment.

powershell
1$cfgPath = ".\bin\Release\MyApp.exe.config"
2$content = Get-Content $cfgPath -Raw
3
4if ($content -notmatch "https://api.example.com") {
5  throw "Release config missing expected production endpoint"
6}
7
8if ($content -match "dev-api") {
9  throw "Release config still contains development endpoint"
10}
11
12Write-Host "Config validation passed"

Validation should run for every supported configuration, not only Release.

Keep Secrets Out of app.config

Environment selection is not a reason to store credentials in source control. Keep secrets external and inject at deploy time through secure channels.

Recommended split:

  • source-controlled config: non-sensitive defaults and feature flags
  • secure secret store: API keys, passwords, tokens
  • deployment pipeline: inject secret values for target environment

This reduces breach risk and simplifies key rotation.

Naming and File Layout Conventions

Consistency lowers cognitive load, especially in large repositories. Use a clear naming convention and keep config files together.

Example layout:

  • 'Configs/App.Debug.config'
  • 'Configs/App.Staging.config'
  • 'Configs/App.Release.config'

Document supported keys and expected value types so teams do not introduce drift across environments.

Migration Note for Modern .NET

Many legacy apps still use app.config, while modern .NET commonly uses appsettings with environment providers. Even so, stabilizing the current app.config flow is valuable before migration.

A safe migration path is:

  1. normalize existing config keys
  2. add build-time selection and CI validation
  3. migrate one subsystem at a time to the newer model

This avoids risky all-at-once rewrites.

Common Pitfalls

  • Editing generated output config manually and losing changes on next build.
  • Missing transform locators and creating duplicate or stale keys.
  • Storing secrets in source-controlled config files.
  • Validating only one configuration path in CI.
  • Allowing environment key names to drift across files.

Summary

  • Choose environment config deterministically during build.
  • Use MSBuild copy targets or transforms based on your tooling.
  • Validate generated config files automatically in CI.
  • Keep secrets outside source-controlled app.config artifacts.
  • Enforce naming and layout conventions to prevent configuration drift.

Course illustration
Course illustration

All Rights Reserved.