Over the years shipping ASP.NET Core services, I've learned that a surprising amount of behavior hangs off a small set of environment variables. This page is my field guide: what I set, why I set it, and exactly how I wire it up on Windows, Linux, Docker/Kubernetes, and Azure App Service. Everything below is battle-tested in real deployments.

1. Choosing the environment

In modern minimal hosting, both ASPNETCORE_ENVIRONMENT and DOTNET_ENVIRONMENT work. In practice, I keep a simple rule: web apps use ASPNETCORE_ENVIRONMENT; non-web/background apps use DOTNET_ENVIRONMENT. I never set them to different values in the same process.

Windows (PowerShell)

$env:ASPNETCORE_ENVIRONMENT = "Development"
dotnet run

Linux (bash)

export ASPNETCORE_ENVIRONMENT=Production
dotnet MyApp.dll
Azure App Service: set an App Setting named ASPNETCORE_ENVIRONMENT (or DOTNET_ENVIRONMENT). App Settings are exposed to your app as environment variables.

2. Binding URLs & ports

Two patterns I use:

Full URLs

ASPNETCORE_URLS="http://0.0.0.0:8080;https://0.0.0.0:8443"

Ports-only (my default in containers)

HTTP_PORTS=8080
HTTPS_PORTS=8443

These are shorthand for the equivalent ASPNETCORE_URLS values. In .NET 8 container images, the default HTTP port changed from 80 to 8080, which plays nicely with most PaaS platforms.

Azure App Service – custom Linux containers: if you listen on a non-default port, set WEBSITES_PORT in App Settings. For built-in (code-based) Linux, I either stick with 8080 or set the HTTP(S) port variables explicitly.

3. Configuration via env vars (nested keys)

Environment variables map to configuration keys. For nested keys, use __ (double underscore) in the variable name to represent a colon:

Logging__LogLevel__Default=Information
Kestrel__Endpoints__Http__Url=http://0.0.0.0:8080
Kestrel__Certificates__Default__Path=/certs/site.pfx
Kestrel__Certificates__Default__Password=changeit

4. Diagnostics on/off switches

For production hardening or incident response, I rely on these:

  • DOTNET_EnableDiagnostics – master switch for debugger/profiler/EventPipe.
  • DOTNET_DiagnosticPorts – sets the diagnostic port behavior (listen/connect, suspend).
# Disable attach and diagnostic IPC entirely
DOTNET_EnableDiagnostics=0

# Or fine-tune via Diagnostic Port (example)
DOTNET_DiagnosticPorts=/tmp/dotnet-diagnostic-01,suspend

Note: for .NET 8+, disabling diagnostics may also disable profilers; use with intent.

5. Globalization & ICU

When running in containers or minimal environments, culture data can add 20+ MB to your app size. For APIs that don't need localization:

DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1

This disables culture-specific formatting, but also breaks things like DateTime.Parse with culture-specific formats. Test thoroughly.

6. GC & performance knobs

For high-throughput services, I sometimes tune:

# Server GC (default in server scenarios)
DOTNET_gcServer=1

# Concurrent GC
DOTNET_gcConcurrent=1

# Retain VM for faster allocations
DOTNET_GCRetainVM=1

7. Container awareness

In Kubernetes with resource limits, help .NET detect container boundaries:

DOTNET_RUNNING_IN_CONTAINER=1

This makes .NET respect container memory limits when deciding GC behavior and ThreadPool sizing.

8. Hosting startup assemblies

For cross-cutting concerns (telemetry, monitoring), I sometimes use:

ASPNETCORE_HOSTINGSTARTUPASSEMBLIES=MyApp.Observability

This loads the assembly at startup, allowing you to register services without modifying Program.cs.

9. Proxies & forwarded headers

Behind load balancers or reverse proxies:

ASPNETCORE_FORWARDEDHEADERS_ENABLED=true

This enables forwarded header middleware automatically, helping with HTTPS redirection and host detection.

10. How I set these (copy/paste)

Windows (PowerShell)

$env:ASPNETCORE_ENVIRONMENT = "Production"
$env:HTTP_PORTS = "8080"
$env:DOTNET_EnableDiagnostics = "0"
dotnet MyApp.dll

Linux (bash)

export ASPNETCORE_ENVIRONMENT=Production
export HTTP_PORTS=8080
export DOTNET_EnableDiagnostics=0
dotnet MyApp.dll

systemd service

[Unit]
Description=My ASP.NET Core App
After=network.target

[Service]
Type=notify
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=HTTP_PORTS=8080
Environment=DOTNET_EnableDiagnostics=0
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/dotnet MyApp.dll
Restart=always
RestartSec=10
User=www-data

[Install]
WantedBy=multi-user.target

Docker

docker run --rm -p 8080:8080 \
  -e ASPNETCORE_ENVIRONMENT=Production \
  -e HTTP_PORTS=8080 \
  myregistry/myapp:latest

docker-compose

services:
  web:
    image: myregistry/myapp:latest
    environment:
      ASPNETCORE_ENVIRONMENT: "Production"
      HTTP_PORTS: "8080"
    ports:
      - "8080:8080"

Azure App Service (CLI)

az webapp config appsettings set -g MyRg -n myapp-prod \
  --settings ASPNETCORE_ENVIRONMENT=Production WEBSITES_PORT=8080

11. Pitfalls I keep seeing

  • Precedence confusion: mixing DOTNET_ENVIRONMENT and ASPNETCORE_ENVIRONMENT with different values.
  • Assuming launchSettings.json works in prod: it's local-only. Use env vars or app settings.
  • Forgetting __ for nested keys: e.g., Logging__LogLevel__Default.
  • Azure custom containers not listening on the expected port: set WEBSITES_PORT or use the port variables.
  • Invariant globalization surprises: only enable if you're sure you don't need culture data.

12. References


If you spot anything I should add (or a platform quirk I missed), feel free to reach out and I'll update this guide.

❓ Frequently Asked Questions

What's the difference between ASPNETCORE_ENVIRONMENT and DOTNET_ENVIRONMENT?

ASPNETCORE_ENVIRONMENT is specific to ASP.NET Core applications and sets IWebHostEnvironment.EnvironmentName. DOTNET_ENVIRONMENT is for all .NET applications and affects IHostEnvironment.EnvironmentName. Use ASPNETCORE_ENVIRONMENT for web apps.

Why isn't my launchSettings.json environment variable working in production?

launchSettings.json only works during development (F5/dotnet run). In production, use actual environment variables, appsettings.json, or cloud provider configuration like Azure App Settings.

How do I set nested configuration values with environment variables?

Use double underscores (__) to represent hierarchy. For example, Logging:LogLevel:Default becomes Logging__LogLevel__Default as an environment variable.

Should I use HTTP_PORTS or ASPNETCORE_URLS?

HTTP_PORTS is simpler for basic scenarios (just port numbers). ASPNETCORE_URLS gives full control over schemes, IPs, and ports. Use HTTP_PORTS for containers, ASPNETCORE_URLS for complex hosting scenarios.

What's WEBSITES_PORT and when do I need it?

WEBSITES_PORT is Azure-specific and tells Azure App Service which port your container listens on. Required for custom containers that don't use the default port (80/8080).