Deploying a containerized enterprise .NET application and a companion database behind a managed Linux web host introduces multiple abstraction layers. A breakdown in any single tier, from background thread initialization to firewall NAT rules, can prevent an application from serving traffic.
This technical guide outlines five core failure points that can occur when hosting a .NET API and SQL Server container behind a reverse proxy. Understanding how these layers interact can make deployment troubleshooting much easier.
1. Startup Thread Hijack and Kestrel Port Binding Failure
Modern .NET applications can use hosted services such as BackgroundService for background processing. Depending on the .NET version and how the hosted service is implemented, synchronous work performed during startup can delay application initialization.
A common problem occurs when a background worker encounters a failing external dependency and immediately enters a retry loop without yielding or applying a delay. This can consume resources and interfere with application startup.
For example, a worker that continuously retries an unavailable service without any delay can create unnecessary CPU and resource usage.
The Fix
The background operation should yield control and use an appropriate retry strategy with exponential backoff.
For example:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.Yield();
while (!stoppingToken.IsCancellationRequested)
{
try
{
await ProcessWorkAsync(stoppingToken);
}
catch (Exception ex)
{
// Log the exception
}
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
}
For more sophisticated retry scenarios, resilience libraries such as Polly can be used to implement retry policies and exponential backoff.
The important point is that background processing should not continuously perform blocking or tight-loop work during application initialization.
2. Domain-to-Port Mapping on Web Hosting Panels
A containerized application may work correctly when accessed directly through a published port such as:
http://server-ip:8081
However, accessing the application through the root domain may display a generic hosting-panel test page instead.
This can happen when a managed Linux hosting panel generates or regenerates its web-server configuration and overwrites manually modified configuration files.
The Fix
Instead of directly modifying generated configuration files, use the hosting platform's supported userdata or custom-configuration include mechanism when available.
The reverse proxy can then forward requests from the domain to the application running on its published port.
For Apache, a configuration can look like this:
ProxyPass / http://192.168.1.50:8081/
ProxyPassReverse / http://192.168.1.50:8081/
This allows the public domain to act as the entry point while the .NET application continues running on its internal application port.
The exact configuration mechanism depends on the hosting panel and web server being used.
3. Loopback Isolation and Connection Resets
Another common reverse-proxy problem occurs when the web server returns:
502 Bad Gateway
The underlying logs may contain errors such as:
Connection reset by peer
This indicates that the reverse proxy was unable to establish or maintain a valid connection with the upstream application.
Container networking, host networking, firewall rules, and loopback behavior can all contribute to this problem.
The Fix
If routing through localhost does not work correctly in the deployment environment, configure the reverse proxy to use an appropriate reachable host interface, Docker bridge gateway, or published host address.
For example:
ProxyPass / http://192.168.1.50:8081/
ProxyPassReverse / http://192.168.1.50:8081/
The correct address should be verified against the actual network configuration rather than copied blindly.
Useful checks include:
docker ps
and:
curl http://192.168.1.50:8081
If the curl request succeeds from the host but the domain still returns 502, the problem is likely within the reverse-proxy configuration rather than the application itself.
4. The Containerized Database Connection Trap
One of the most common mistakes in a multi-container deployment is using localhost in the application's database connection string.
Consider a .NET application container and a SQL Server container:
.NET API Container
|
|
SQL Server Container
Inside the .NET API container, the following connection string does not normally point to the SQL Server container:
Server=localhost
Instead, localhost refers to the network namespace of the API container itself.
As a result, Entity Framework Core startup migrations or Identity initialization can fail because the application cannot find SQL Server.
This can cause the application container to repeatedly restart if database initialization is performed during startup.
The Fix
Place both containers on the same user-defined Docker bridge network.
For example:
docker network create enterprise-net
Start the SQL Server container on that network:
docker run \
--name sql-container \
--network enterprise-net \
...
Then start the .NET application on the same network:
docker run \
--name api-container \
--network enterprise-net \
...
Docker's internal DNS allows containers on the same user-defined network to communicate using container names.
The connection string can therefore reference the SQL Server container:
Server=sql-container;Database=EnterpriseDb;User Id=sa;Password=<password>;TrustServerCertificate=True;
The key difference is:

Join the conversation! Your thoughts help the community grow.