# Ứng dụng .NET

> Cài .NET 8, 9 hoặc 10 SDK, deploy ASP.NET Core và ứng dụng web C# bằng dotnet publish, cho Kestrel lắng nghe trên $PORT qua ASPNETCORE_URLS và xử lý các lỗi thường gặp.

Source: https://zopanel.net/vi/docs/dotnet  
Updated: 2026-10-09

ZoPanel build ứng dụng web .NET bằng `dotnet publish`, chạy ứng dụng đã publish như một dịch vụ của tài khoản hosting và đặt nginx kèm SSL phía trước. Dùng cho ASP.NET Core (MVC, Razor Pages, Blazor Server, minimal API) và mọi ứng dụng .NET phục vụ HTTP.

## Cài .NET SDK

Quản trị viên cài một lần cho mỗi server:

1. Mở **Runtime**.
2. Trên thẻ **.NET SDK**, bấm phiên bản cần cài (**.NET 10.0**, **.NET 9.0** hoặc **.NET 8.0**). Log tác vụ hiển thị tiến trình cài. Hãy cài mọi phiên bản mà các ứng dụng của bạn nhắm tới: chúng được cài song song.

ZoPanel cung cấp các phiên bản SDK mà Microsoft còn hỗ trợ: .NET 10 (LTS, hỗ trợ đến tháng 11/2028), cùng .NET 9 và .NET 8, cả hai được hỗ trợ đến ngày 10/11/2026. Mỗi gói (`dotnet-sdk-10.0`, …) đã bao gồm ASP.NET Core runtime tương ứng:

| Hệ điều hành | Nguồn SDK | Kiến trúc |
| --- | --- | --- |
| Ubuntu 22.04 | 8.0 từ gói của Ubuntu; 9.0 và 10.0 từ kho .NET backports của Canonical (`ppa:dotnet/backports`), được thêm tự động | amd64 và arm64 |
| Ubuntu 24.04 | 8.0 và 10.0 từ gói của Ubuntu; 9.0 từ kho .NET backports | amd64 và arm64 |
| Debian 12, 13 | Kho gói của Microsoft, được thêm tự động | amd64: 8.0, 9.0, 10.0. arm64: chỉ 10.0 |

**Quan trọng:** Microsoft chỉ phát hành gói .NET 8 và .NET 9 cho Debian trên amd64. Trên server Debian arm64, thẻ chỉ cho cài .NET 10 và giải thích lý do; với ứng dụng nhắm `net8.0` hoặc `net9.0` trên server đó, hãy chuyển sang `net10.0`, dùng server Ubuntu, hoặc build và chạy ứng dụng dưới dạng container (xem [Deploy bằng Docker](/vi/docs/docker-deploy)). Nếu kho của Microsoft chưa có cấu hình cho bản Debian đang dùng, việc cài dừng với thông báo ".NET is not available for Debian … yet: … (deploy with a Dockerfile instead)".

### SDK nào build ứng dụng

Ô **Phiên bản** của ứng dụng chọn SDK. Tự nhận diện đặt ô này theo `<TargetFramework>` của project (`net9.0` → 9.0; với `<TargetFrameworks>`, lấy bản mới nhất). Khi build, ZoPanel thêm vào bản phát hành một file `global.json` cố định phiên bản SDK đó, nên log build ghi "Using the .NET 9.0 SDK". Nếu repository đã có `global.json` riêng (trong thư mục project hoặc thư mục cha), file đó quyết định và ZoPanel không thêm file nữa. Khi không có phiên bản, dùng SDK mới nhất đã cài.

Nếu SDK của phiên bản đó chưa được cài, deploy dừng với thông báo "the .NET 9.0 SDK is not installed (installed: …)". Hãy cài nó ở mục **Runtime**, hoặc đổi target framework của project.

## ZoPanel nhận diện gì

Dự án được coi là .NET khi thư mục gốc có file `.csproj`, file `.sln`, hoặc có `.csproj` nằm sâu một cấp thư mục. Sau đó ZoPanel tìm project sâu tối đa hai cấp (ví dụ `src/MyApp/MyApp.csproj`), bỏ qua `bin`, `obj` và thư mục ẩn:

- Ưu tiên project web (`Microsoft.NET.Sdk.Web`), gắn nhãn **ASP.NET Core**. Nếu không có, dùng project đầu tiên có `Microsoft.NET.Sdk`, gắn nhãn **.NET**.
- Khi có nhiều project, log build ghi rõ project nào được publish.

Với project `MyApp.csproj`, các lệnh đề xuất là:

| Ô | Lệnh |
| --- | --- |
| **Lệnh cài đặt** | `dotnet restore 'MyApp.csproj'` |
| **Lệnh build** | `dotnet publish 'MyApp.csproj' -c Release -o out --no-restore` |
| **Lệnh chạy** | `ASPNETCORE_URLS=http://127.0.0.1:$PORT dotnet out/'MyApp.dll'` |

Việc nhận diện chạy theo thứ tự cố định: `package.json`, `composer.json`, `Gemfile` hoặc file build Java trong cùng thư mục được xét trước .NET. Nếu framework bị nhận sai, đặt **Thư mục gốc** là thư mục project, hoặc tắt **Tự nhận diện** và tự đặt lệnh. `Procfile` có dòng `web:` sẽ thay lệnh chạy.

## Deploy ứng dụng .NET

1. Ở **Website → Thêm website**, chọn **Git deploy**, nhập tên miền và repository. Xem [Git deploy](/vi/docs/git-deploy).
2. Mở tab **Deploy** của website, kiểm tra **Framework** và các lệnh trong **Build & chạy**.
3. Muốn sửa lệnh, tắt **Tự nhận diện**, sửa lệnh rồi bấm **Lưu & deploy lại**.
4. Thêm cấu hình trong **Biến môi trường**.
5. Bấm **Deploy ngay**.

Bản mới chỉ được đưa lên khi trả lời **Đường dẫn health check** (mặc định `/`) với mã dưới 500 trong vòng 90 giây. Trong lúc đó bản cũ vẫn chạy. Muốn build bằng SDK khác, tắt **Tự nhận diện** và chọn **Phiên bản** (xem [SDK nào build ứng dụng](#sdk-nao-build-ung-dung)).

Muốn deploy không qua Git, tạo website kiểu **Node / Proxy**, tải mã nguồn lên `domains/<tên-miền>/source` và chọn nguồn **File đã tải lên (Quản lý file)**. Hãy tải mã nguồn chứ không phải thư mục `publish`: ZoPanel tự chạy restore và publish.

## Cổng và URL

Kestrel phải lắng nghe tại `127.0.0.1`, trên cổng trong biến `PORT`. Lệnh chạy đề xuất làm việc này bằng `ASPNETCORE_URLS=http://127.0.0.1:$PORT`. Giữ nguyên tiền tố đó khi sửa lệnh, và không ghi cứng URL ở nơi khác:

- Bỏ các lời gọi `UseUrls(...)` và mục `Kestrel:Endpoints` trong `appsettings.json`: chúng ghi đè `ASPNETCORE_URLS`.
- Không thêm endpoint HTTPS cho Kestrel. nginx lo SSL và nói chuyện với ứng dụng qua HTTP thường.

`PORT` khác nhau giữa hai slot blue/green (slot thứ hai dùng cổng gốc cộng 10000), nên đừng ghi số cổng vào cấu hình.

### Phía sau nginx

nginx gửi kèm `X-Forwarded-For` và `X-Forwarded-Proto`. Để ứng dụng thấy IP thật của khách và giao thức `https` (cho redirect, cookie, callback OAuth), bật middleware forwarded headers ở đầu `Program.cs`:

```csharp
using Microsoft.AspNetCore.HttpOverrides;

var app = builder.Build();
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
```

Proxy nằm ở `127.0.0.1`, địa chỉ mà ASP.NET Core mặc định tin cậy.

### Tên assembly

Lệnh chạy giả định file DLL được publish trùng tên file project. Nếu project khai báo `<AssemblyName>`, sửa lệnh chạy thành `out/<AssemblyName>.dll`.

## Biến môi trường

Các biến trong **Biến môi trường** dùng được khi build và khi chạy. ZoPanel đặt thêm `PORT` và `HOST=127.0.0.1`.

- Tên biến gồm chữ, số và `_`, bắt đầu bằng chữ hoặc `_`, tối đa 64 ký tự. Giá trị nằm trên một dòng, tối đa 8.192 ký tự. Tối đa 200 biến.
- Giá trị được lưu trong file chỉ root đọc được và được systemd truyền vào, không bao giờ nằm trên dòng lệnh.

ASP.NET Core đọc biến môi trường như cấu hình. Dùng hai dấu gạch dưới cho khoá lồng nhau:

| Biến | Khoá cấu hình |
| --- | --- |
| `ConnectionStrings__Default` | `ConnectionStrings:Default` |
| `Smtp__Host` | `Smtp:Host` |
| `ASPNETCORE_ENVIRONMENT` | Tên môi trường (`Production` khi không đặt) |

Với database MariaDB hoặc PostgreSQL tạo trong ZoPanel, dùng server `127.0.0.1`. Xem [Database](/vi/docs/databases).

## Bộ nhớ

Ứng dụng, quá trình build và mọi thứ khác của tài khoản dùng chung **RAM (MB)** của gói. Tiến trình vượt giới hạn bị kernel dừng và ZoPanel khởi động lại ứng dụng. Khi deploy, quá trình build và khoảng thời gian ngắn bản cũ và bản mới chạy song song cùng cần bộ nhớ.

Project ASP.NET Core mặc định dùng server garbage collection, giữ nhiều bộ nhớ hơn theo số nhân CPU. Với gói nhỏ, chuyển sang workstation GC hoặc giới hạn heap bằng biến môi trường (giá trị heap viết ở dạng hexa):

```text
DOTNET_gcServer=0
DOTNET_GCHeapHardLimit=0x20000000
```

`0x20000000` là 512 MiB. Xem [Gói hosting và giới hạn](/vi/docs/packages-limits).

## File và log

- Ứng dụng chỉ ghi được trong thư mục home của tài khoản và thư mục `/tmp` riêng. Lưu file upload trong **Đường dẫn lưu trữ cố định** (ví dụ `wwwroot/uploads`) hoặc trong database: mỗi bản release là một thư mục mới, 5 bản gần nhất được giữ để **Rollback**.
- **Log ứng dụng** trong tab **Deploy** hiển thị 400 dòng output console mới nhất. Bật **Trực tiếp** để theo dõi liên tục.
- Output restore và publish của từng lần deploy nằm trong **Tác vụ**. Log nginx ở tab **Nhật ký**.

## Xử lý sự cố

| Thông báo hoặc hiện tượng | Cách xử lý |
| --- | --- |
| "the .NET SDK is not installed — install it under Runtimes" | Server chưa có SDK nào. Nhờ quản trị viên cài một phiên bản trên thẻ **.NET SDK** ở mục **Runtime**. |
| "the .NET 9.0 SDK is not installed (installed: …)" | Project nhắm một phiên bản chưa có SDK. Cài nó ở mục **Runtime**, hoặc đổi target framework. |
| ".NET 8.0 cannot be installed: Microsoft publishes .NET 8 and 9 packages for Debian on amd64 only…" | Debian trên arm64. Dùng .NET 10, server Ubuntu hoặc container. |
| ".NET is not available for Debian … yet" khi cài | Microsoft chưa có kho cho bản Debian này. Dùng Ubuntu hoặc deploy bằng Dockerfile. |
| `NETSDK1045: The current .NET SDK does not support targeting .NET 10.0` | **Phiên bản** được đặt tay thành SDK cũ hơn target framework, hoặc `global.json` của repository cố định một SDK cũ hơn. Bật lại **Tự nhận diện**, hoặc sửa `global.json`. |
| `A compatible .NET SDK was not found` | `global.json` của repository yêu cầu một phiên bản SDK chưa được cài. Cài phiên bản đó, hoặc nới `rollForward` trong `global.json`. |
| "The app did not respond on port … within 90s (it must listen on $PORT)" | Kestrel lắng nghe chỗ khác (thường là `localhost:5000`, hoặc URL trong `appsettings.json`, `UseUrls`). Giữ `ASPNETCORE_URLS=http://127.0.0.1:$PORT` và bỏ các cấu hình endpoint khác. |
| "The app exited during startup" | Xem **Log ứng dụng**. `The application to execute does not exist` nghĩa là tên DLL trong lệnh chạy sai (xem mục Tên assembly). |
| Redirect hoặc link sinh ra dùng `http://`, hoặc IP khách luôn là `127.0.0.1` | Thêm middleware forwarded headers như trên. |
| Ứng dụng tự khởi động lại mà không có lỗi | Tài khoản chạm giới hạn RAM. Đặt `DOTNET_gcServer=0` hoặc `DOTNET_GCHeapHardLimit`, hoặc tăng **RAM (MB)** của gói. |

## Xem thêm

- [Ứng dụng Node.js và Python](/vi/docs/apps-node-python): quy trình deploy, cổng và phát hành blue/green
- [Git deploy](/vi/docs/git-deploy)
- [Deploy bằng Docker](/vi/docs/docker-deploy)
- [Gói hosting và giới hạn](/vi/docs/packages-limits)
- [Host ASP.NET Core trên Linux với Nginx (Microsoft)](https://learn.microsoft.com/aspnet/core/host-and-deploy/linux-nginx)
- [Cấu hình ASP.NET Core với proxy (Microsoft)](https://learn.microsoft.com/aspnet/core/host-and-deploy/proxy-load-balancer)
