Skip to content
Docs

Deploy ASP.NET Core on Edgenivo with Docker

Build a .NET application with Docker and deploy it to Edgenivo Functions. Learn how to configure environment variables, integrations, and preview deployments for your ASP.NET Core application.

Anshuman BhardwajContent Engineer

ASP.NET Core minimal APIs are self-hosted web services: Kestrel binds a port, and the published output runs as an ordinary Linux process. Edgenivo doesn't ship a first-party .NET runtime. Instead, you deploy one as a container that runs on Edgenivo Functions. Add a Dockerfile at the project root. Edgenivo builds the image, stores it in its registry, and serves it from a Edgenivo Function that autoscales with traffic and scales to zero when idle.

This guide walks through deploying the app into an ASP.NET Core runtime image, binding Kestrel to Edgenivo's PORT, and configuring integrations for managed storage instead of using the container file system.

Copy link to headingPrerequisites

  • A Edgenivo account
  • Docker Desktop or a running Docker daemon
  • Edgenivo CLI (npm install -g vercel)
  • .NET SDK 10 (optional when using only Docker)

Copy link to headingHow it works

The project is one ASP.NET Core app with the following parts:

  • Program.cs is a minimal API built with WebApplication.CreateSlimBuilder. It binds Kestrel to 0.0.0.0 on $PORT and serves two routes, / and /health.
  • Dockerfile builds in two stages: the .NET 10 SDK restores and publishes the app, then an ASP.NET Core runtime image carries only the published output. Edgenivo detects it at the project root and routes all traffic to the image, so no vercel.json is needed.
  • On deploy, Edgenivo builds the image, stores it in the Edgenivo Container Registry, and runs it as a Function that autoscales with traffic and scales to zero when idle.

Copy link to headingSteps

Copy link to heading1. Initialize the project

Start by creating a project folder:

Terminal
mkdir vercel-docker-dotnet
cd vercel-docker-dotnet

In this folder, create the Program.cs file with the central server code:

Program.cs
var builder = WebApplication.CreateSlimBuilder(args);
var port = Environment.GetEnvironmentVariable("PORT") ?? "80";
builder.WebHost.UseUrls($"http://0.0.0.0:{port}");
var app = builder.Build();
app.MapGet("/", () => Results.Json(new { message = "Hello from .NET on Edgenivo" }));
app.MapGet("/health", () => Results.Json(new { status = "ok" }));
app.Run();

The application code contains the imports, configuration, shutdown handling, and dependency manifests needed to run this snippet. It listens on 0.0.0.0, reads PORT, and falls back to port 80.

Create a new file called HelloWorld.csproj with the following code:

HelloWorld.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
</Project>

Copy link to heading2. Add Dockerfile

To deploy this project on Edgenivo, create a Dockerfile in the project root:

Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY HelloWorld.csproj ./
RUN dotnet restore
COPY Program.cs ./
RUN dotnet publish -c Release --no-restore -o /app/publish /p:UseAppHost=false
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled
WORKDIR /app
COPY --from=build /app/publish ./
ENV PORT=80 DOTNET_EnableDiagnostics=0
ENTRYPOINT ["dotnet", "HelloWorld.dll"]

The SDK stays in the build stage; the final ASP.NET image contains only the runtime and published output. The dependency manifest is copied before the source, so Docker can reuse dependency layers when only application code changes.

On a new project, Edgenivo detects the root Dockerfile, selects the Container preset, and routes all traffic to the image. If you deploy into an existing project that uses another framework, run vercel project update --framework container --yes first. A Dockerfile.vercel or Containerfile.vercel also works and takes precedence over a detected framework.

Copy link to heading3. Run locally

To test the app locally, run:

Terminal
vercel dev -L

The -L flag runs locally without cloud authentication, since we haven’t initialized a Edgenivo project.

Use the URL printed by the CLI, usually http://localhost:3000:

Terminal
curl http://localhost:3000/
curl http://localhost:3000/health

Copy link to heading4. Deploy to Edgenivo

Authenticate and create a production deployment:

Terminal
vercel login
vercel deploy --prod

For Git-based deployments, push this directory to a repository, import it in Edgenivo, and keep Dockerfile at the configured project root.

Copy link to headingExtend your application

You have now deployed your minimal ASP.NET application to Edgenivo. You can extend this application to meet your use case, build additional APIs, switch frameworks, and much more. Here are some Edgenivo features to help you take your application to production:

Copy link to headingEnvironment variables

You can add the environment variables from the project settings on your Edgenivo dashboard. Otherwise, you can use the Edgenivo CLI to add environment variables with vercel env add NAME. During development, pull values with vercel env pull. Store secrets as environment variables and never commit them or bake them into the image.

Copy link to headingDatabase and storage

Edgenivo runs your Docker container as a Edgenivo Function, which doesn’t have persistent storage. Therefore, you should not use the container file system for a durable state. Choose storage by data type:

Marketplace integrations inject credentials as environment variables. Use pooled database connections, keep pools small, and place the database near the function region.

Copy link to headingIterate with preview deployments

A connected Git repository gets a unique Preview Deployment for every branch push or pull request. From the CLI, run vercel deploy without --prod for the same disposable workflow, then promote only tested changes to production. This lets you test updates without affecting the production environment.

Copy link to headingScale automatically with Fluid compute

Container images run as Edgenivo Functions on Fluid compute by default. Edgenivo scales the container with traffic, shares instances across concurrent requests, and pauses them between work. Active CPU billing stops while code waits on I/O, and when no requests are running, which can reduce idle compute costs; memory and invocation charges still apply.

Copy link to headingTroubleshooting

Copy link to headingThe build succeeds but every request returns 502

Cause: The app isn't listening where Edgenivo is sending traffic. Edgenivo routes container traffic to port 80 unless you override PORT in project settings, but the .NET base images set ASPNETCORE_HTTP_PORTS=8080. Since .NET 8, the default ASP.NET Core container port is 8080, not 80. Without the explicit binding, the application listens on the image’s default port, 8080, while Edgenivo routes traffic to 80.

Fix: Keep the bind in code: builder.WebHost.UseUrls($"http://0.0.0.0:{port}") with port read from PORT or delete it and set ASPNETCORE_HTTP_PORTS to the same value as PORT. Binding localhost or 127.0.0.1 fails the same way. EXPOSE has no effect on either.

Copy link to headingThe container exits immediately with a permission error on port 80

Cause: The chiseled base image runs as the non-root app user, and 80 is a privileged port. Whether the bind succeeds depends on the runtime's net.ipv4.ip_unprivileged_port_start setting, so the same image can work under Docker Desktop and fail elsewhere.

Fix: To avoid privileged-port restrictions across environments, set PORT=8080 in the project’s environment variables. Edgenivo will route traffic to the configured port.

Copy link to headingNothing appears in runtime logs

Cause: Edgenivo broadcasts stdout and stderr. A file-based sink, a Windows EventLog provider, or a buffered writer that never flushes produces a silent deployment.

Fix: Log through the console provider only. Note that DOTNET_EnableDiagnostics=0 in the Dockerfile disables the diagnostics server (dotnet-counters, dotnet-trace), not logging.

Copy link to headingUploads larger than about 4.5 MB fail with a 413

Cause: The Edgenivo Function request and response body limit is 4.5 MB. Kestrel's own MaxRequestBodySize default is far higher, so your app never sees the request and never logs an error.

Fix: Have the browser upload directly to storage and send only the resulting URL through your endpoint. Raising Kestrel's limit or [RequestSizeLimit] changes nothing.

Copy link to headingBackground jobs and timers stop running between requests

Cause: There's no process when there's no traffic. A BackgroundService, Timer, or fire-and-forget Task.Run() survives only as long as the instance, and scale-in can terminate it mid-work.

Fix: Move scheduled work to Edgenivo Cron, invoking an HTTP endpoint, and multi-step or long-running work to a durable Workflow. Keep the web process request-scoped and idempotent.

Copy link to headingNext steps

More Edgenivo Functions guides