<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Problem Solver’s Diary]]></title><description><![CDATA[I’m Nasim Hossain, a software engineer passionate about problem solving, backend development, cloud, and DevOps. This blog is my personal DevLog where I share w]]></description><link>https://blog.nasimhossain.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 10 Apr 2026 21:55:32 GMT</lastBuildDate><atom:link href="https://blog.nasimhossain.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to Publish and Deploy an ASP.NET Core App — Practical Guide]]></title><description><![CDATA[This article walks through the typical lifecycle of publishing an ASP.NET Core application and deploying it to common targets (IIS, Azure App Service, Linux systemd + Nginx, and Docker). It covers bui]]></description><link>https://blog.nasimhossain.dev/how-to-publish-and-deploy-an-asp-net-core-app-practical-guide</link><guid isPermaLink="true">https://blog.nasimhossain.dev/how-to-publish-and-deploy-an-asp-net-core-app-practical-guide</guid><dc:creator><![CDATA[Nasim Hossain Rabbi]]></dc:creator><pubDate>Fri, 10 Apr 2026 16:51:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69949fba599df99074efa334/68faece9-d704-4366-ae82-317ac3b686b2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This article walks through the typical lifecycle of publishing an ASP.NET Core application and deploying it to common targets (IIS, Azure App Service, Linux systemd + Nginx, and Docker). It covers build/publish options, configuration and secrets, databases and migrations, CI/CD tips, and troubleshooting. Examples use the dotnet CLI, Visual Studio, and GitHub Actions.</p>
<hr />
<h2>1. Quick overview: build vs publish vs deploy</h2>
<ul>
<li><p>Build (<code>dotnet build</code>) compiles the project for development or CI but leaves assets in intermediate folders.</p>
</li>
<li><p>Publish (<code>dotnet publish</code>) produces the final files required to run the app (DLLs, static files, runtime if self-contained, configuration).</p>
</li>
<li><p>Deploy is the process of transferring the published output to a host and configuring it to run (IIS, App Service, container registry, VM).</p>
</li>
</ul>
<hr />
<h2>2. Prepare the app for production</h2>
<ul>
<li><p>Set <code>ASPNETCORE_ENVIRONMENT=Production</code> on the host.</p>
</li>
<li><p>Use <code>appsettings.Production.json</code> to override production settings (connection strings, logging).</p>
</li>
<li><p>Do not store secrets in source code or appsettings.json. Use environment variables, Azure Key Vault, or a secrets manager.</p>
</li>
<li><p>Enable structured logging (e.g., Serilog) and external sinks for production logs.</p>
</li>
<li><p>Add health checks (<code>Microsoft.AspNetCore.Diagnostics.HealthChecks</code>) for readiness and liveness endpoints.</p>
</li>
<li><p>Ensure static files are served with caching headers and compression enabled.</p>
</li>
</ul>
<hr />
<h2>3. Publishing options</h2>
<h3>3.1 Using dotnet CLI</h3>
<p>Basic publish:</p>
<pre><code class="language-bash">dotnet publish -c Release -o ./publish
</code></pre>
<p>Publish for a specific runtime (framework-dependent; smaller):</p>
<pre><code class="language-bash">dotnet publish -c Release -r linux-x64 --self-contained false -o ./publish
</code></pre>
<p>Self-contained deployment (includes runtime; larger, helpful where .NET runtime is not installed):</p>
<pre><code class="language-bash">dotnet publish -c Release -r win-x64 --self-contained true -o ./publish
</code></pre>
<p>Trim unused assemblies (smaller output; test thoroughly):</p>
<pre><code class="language-bash">dotnet publish -c Release -r linux-x64 -p:PublishTrimmed=true -o ./publish
</code></pre>
<p>Produce single-file executable (available in supported SDKs and RIDs):</p>
<pre><code class="language-bash">dotnet publish -c Release -r linux-x64 -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -o ./publish
</code></pre>
<h3>3.2 Using Visual Studio</h3>
<ul>
<li><p>Right-click project → Publish.</p>
</li>
<li><p>Create a publish target (Folder, Azure, IIS, Container Registry).</p>
</li>
<li><p>Configure settings (configuration, runtime, self-contained, target framework).</p>
</li>
<li><p>Save or use publish profiles (<code>.publishsettings</code> / <code>.pubxml</code>) for repeated deployments.</p>
</li>
</ul>
<h3>3.3 VS Code</h3>
<ul>
<li><p>Use tasks or the .NET Core CLI as above.</p>
</li>
<li><p>Consider extensions like “MSBuild Project Tools” and “Docker” for container scenarios.</p>
</li>
</ul>
<hr />
<h2>4. Deployment targets and specifics</h2>
<h3>4.1 Azure App Service</h3>
<ul>
<li><p>Common path: ZIP deploy, FTP, or the built-in deployment from GitHub/ZIP.</p>
</li>
<li><p>App Service can run framework-dependent apps if runtime exists; otherwise configure as self-contained.</p>
</li>
<li><p>Set app settings and connection strings in the portal (they map to environment variables).</p>
</li>
<li><p>Use deployment slots for zero-downtime swaps (staging → production).</p>
</li>
<li><p>For GitHub Actions, use <code>azure/webapps-deploy</code> action to push artifacts.</p>
</li>
</ul>
<p>Example GitHub Actions step (build + deploy):</p>
<pre><code class="language-yaml">- name: Build
  run: dotnet publish -c Release -o ./publish

- name: Deploy to Azure WebApp
  uses: azure/webapps-deploy@v2
  with:
    app-name: my-webapp
    slot-name: production
    publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
    package: ./publish
</code></pre>
<h3>4.2 IIS (Windows)</h3>
<ul>
<li><p>Install the .NET Hosting Bundle on the server (contains ASP.NET Core Module).</p>
</li>
<li><p>Use <code>dotnet publish</code> to a folder and copy to the IIS site physical path, or use Web Deploy (MSDeploy).</p>
</li>
<li><p>The ASP.NET Core Module launches Kestrel and proxies requests to it. Configure processPath and arguments in web.config if needed (this is handled automatically by publish).</p>
</li>
<li><p>Set <code>ANCM</code> stdoutLogEnabled temporarily for troubleshooting (remember to disable in production).</p>
</li>
</ul>
<h3>4.3 Linux (systemd + reverse proxy like Nginx)</h3>
<ul>
<li><p>Publish as framework-dependent or self-contained to the server.</p>
</li>
<li><p>Create a systemd service file to run the app as a service:</p>
</li>
</ul>
<pre><code class="language-ini">[Unit]
Description=My ASP.NET Core App

[Service]
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/dotnet /var/www/myapp/MyApp.dll
Restart=always
RestartSec=10
SyslogIdentifier=myapp
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=ASPNETCORE_URLS=http://localhost:5000

[Install]
WantedBy=multi-user.target
</code></pre>
<ul>
<li>Configure Nginx as a reverse proxy to <code>http://localhost:5000</code>, handle HTTPS, and forward headers:</li>
</ul>
<pre><code class="language-nginx">proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
</code></pre>
<ul>
<li>Ensure Kestrel listens on loopback only when behind a reverse proxy.</li>
</ul>
<h3>4.4 Docker / Containers</h3>
<p>Dockerfile (recommended multi-stage build):</p>
<pre><code class="language-dockerfile">FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /app --no-restore

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "MyApp.dll"]
</code></pre>
<ul>
<li><p>Use container registries (Docker Hub, Azure Container Registry, GHCR).</p>
</li>
<li><p>Orchestrate with Kubernetes for scale and resiliency.</p>
</li>
</ul>
<hr />
<h2>5. Configuration, secrets, and connection strings</h2>
<ul>
<li><p>Prefer environment variables or platform-managed secrets (App Service settings, Azure Key Vault).</p>
</li>
<li><p>Map configuration using the default providers: appsettings.json → appsettings.{Environment}.json → environment variables → command-line args.</p>
</li>
<li><p>For EF Core migrations:</p>
<ul>
<li><p>Apply migrations at deploy time using <code>dotnet ef database update</code> or run migrations on app startup (make sure to handle concurrency and downtime).</p>
</li>
<li><p>Consider running migrations from CI/CD pipeline with appropriate DB credentials and rollback plan.</p>
</li>
</ul>
</li>
</ul>
<p>Example: run migrations at startup (use cautiously):</p>
<pre><code class="language-csharp">using (var scope = host.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService&lt;ApplicationDbContext&gt;();
    db.Database.Migrate();
}
</code></pre>
<hr />
<h2>6. CI/CD best practices</h2>
<ul>
<li><p>Build artifacts in CI (publish to artifact folder). Deploy artifacts rather than building on target servers.</p>
</li>
<li><p>Keep secrets in pipeline secret variables or secret stores.</p>
</li>
<li><p>Use deployment slots and health checks to enable safe swaps.</p>
</li>
<li><p>Run tests (unit and integration) during CI before deploying.</p>
</li>
<li><p>Use auditing and deployment logs (App Service deployment logs, container image tags).</p>
</li>
</ul>
<p>Example pipeline flow:</p>
<ol>
<li><p>lint &amp; test</p>
</li>
<li><p>dotnet publish → artifact</p>
</li>
<li><p>push artifact/container image to registry/artifacts</p>
</li>
<li><p>deployment step to target (App Service, Kubernetes, VM)</p>
</li>
<li><p>smoke tests &amp; health checks</p>
</li>
<li><p>rollback on failure</p>
</li>
</ol>
<hr />
<h2>7. Security and production hardening</h2>
<ul>
<li><p>Use HTTPS and HSTS. Terminate TLS at the edge (load balancer/Nginx) or let Kestrel handle TLS if you manage certificates.</p>
</li>
<li><p>Validate and sanitize inputs, use CSP and security headers.</p>
</li>
<li><p>Keep dependencies and runtime patched.</p>
</li>
<li><p>Limit privileges of the service account running the app.</p>
</li>
<li><p>Use connection string encryption if required and rotate secrets regularly.</p>
</li>
<li><p>Enable Application Insights / Datadog / Prometheus for observability.</p>
</li>
</ul>
<hr />
<h2>8. Performance and scaling</h2>
<ul>
<li><p>Use response compression, static file caching, and CDN for static assets.</p>
</li>
<li><p>Offload session state to distributed caches (Redis) if you scale horizontally.</p>
</li>
<li><p>Configure Kestrel limits appropriately (request body size, connection limits).</p>
</li>
<li><p>For high throughput, tune thread pool and connection settings, and scale out using load balancers or K8s.</p>
</li>
</ul>
<hr />
<h2>9. Troubleshooting checklist</h2>
<ul>
<li><p>App fails to start: check service logs (<code>journalctl -u myapp.service</code> on Linux, Event Viewer or stdout log for IIS).</p>
</li>
<li><p>Port already in use: ensure only one service binds to the configured port; behind a reverse proxy prefer loopback.</p>
</li>
<li><p>502/502.5 from IIS/App Service: check ANCM logs, and ensure the correct runtime is present.</p>
</li>
<li><p>Missing runtime errors: deploy a self-contained build or install the matching runtime on the host.</p>
</li>
<li><p>Environment mismatch: confirm <code>ASPNETCORE_ENVIRONMENT</code> and appsettings files are correct.</p>
</li>
<li><p>Connection string failures: verify secrets and network/firewall access to DB.</p>
</li>
</ul>
<hr />
<h2>10. Example: Minimal publish + systemd deploy workflow</h2>
<ol>
<li><p>CI job: <code>dotnet publish -c Release -o ./artifacts</code></p>
</li>
<li><p>Copy <code>./artifacts</code> to server (scp/rsync)</p>
</li>
<li><p>On server:</p>
<ul>
<li><p>Stop service: <code>sudo systemctl stop myapp</code></p>
</li>
<li><p>Replace files: <code>rsync -a ./artifacts/ /var/www/myapp/</code></p>
</li>
<li><p>Restart: <code>sudo systemctl daemon-reload &amp;&amp; sudo systemctl restart myapp</code></p>
</li>
<li><p>Check: <code>sudo journalctl -u myapp -f</code></p>
</li>
</ul>
</li>
</ol>
<hr />
<h2>Summary checklist before going live</h2>
<ul>
<li><p>[ ] Publish in Release configuration.</p>
</li>
<li><p>[ ] Use appropriate runtime (framework-dependent vs self-contained).</p>
</li>
<li><p>[ ] Externalize secrets and connection strings; never commit them.</p>
</li>
<li><p>[ ] Enable structured logging and monitoring.</p>
</li>
<li><p>[ ] Have automated CI/CD with artifact storage and rollback strategy.</p>
</li>
<li><p>[ ] Use HTTPS, HSTS, and secure headers.</p>
</li>
<li><p>[ ] Plan for migrations, backups, and scaling.</p>
</li>
<li><p>[ ] Test deployment in staging and use deployment slots where available.</p>
</li>
</ul>
]]></content:encoded></item></channel></rss>