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
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.
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_ENVIRONMENTandASPNETCORE_ENVIRONMENTwith different values. - Assuming
launchSettings.jsonworks 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_PORTor use the port variables. - Invariant globalization surprises: only enable if you're sure you don't need culture data.
12. References
- Use multiple environments in ASP.NET Core
- Configuration in ASP.NET Core (env provider,
__mapping) - Kestrel endpoints (URLS, HTTP_PORTS, HTTPS_PORTS)
- Default ASP.NET Core port changed to 8080 in container images
- Azure App Service: configure a custom container (
WEBSITES_PORT) - Configure an App Service app (App Settings become env vars)
- Forwarded headers & proxies in ASP.NET Core
- Debugging/profiling runtime config (diagnostics)
- Diagnostic Port details
- GC runtime config & env vars
- Globalization options & invariant mode
- .NET environment variables (runtime + CLI)
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).