# How to deploy dotnet api on aws ec2 with nginx + systemd

> This guide explains how to properly deploy a .NET API on Amazon Linux 2023 (EC2) using Nginx Reverse Proxy and systemd.

Published: 2026-06-05
Tags: dotnet, deployment, ec2, nginx, linux
Source: https://minkhantkyaw.com/writing/2026-06-05-how-to-deploy-dotnet-api-on-aws-ec2-with-nginx-systemd/
Translated to English from Burmese by AI.

This guide explains how to properly deploy a .NET API on Amazon Linux 2023 (EC2) using Nginx Reverse Proxy and systemd.

This is the **Prerequisite Step** required to properly build and test the API on your local machine before preparing it for production, prior to starting Step 1.

## Prerequisite Step — Local Build, Test, and Production Readiness

Before uploading to EC2, you need to perform the following steps on your local Windows machine.

### 1. Build Locally

Build locally first to ensure there are no syntax or compilation errors in your code settings.

- **If using Visual Studio:** Press `Ctrl + Shift + B`.
- **If using CLI (Git Bash/Terminal):** Type the following in your Project Folder:

```bash
dotnet build

```

### 2. Test Locally

Before deploying, test the API on your own machine and verify the endpoints using Swagger or Postman.

- **If using Visual Studio:** Press `F5` or `Ctrl + F5` to run.
- **If using CLI:**

```bash
dotnet run

```
- Check whether the API is working correctly by accessing `http://localhost:<PORT>/swagger` or `http://localhost:<PORT>/weatherforecast` in your browser.

### 3. Prepare for Production

Since this API will now be used in a production environment, you need to define the following basic settings:

- **Connection Strings & Secrets:** If any Database Connection Strings or API Keys were hardcoded in the code, remove them. Move them to `appsettings.Production.json` or prepare to use them as Environment Variables on EC2.
- **Production Config File:** Check if `appsettings.Production.json` exists in your project. If it does not exist, create a new file and write the configuration intended for production separately.

```json
{
  "Logging": {
    "LogLevel": {
      "Default": "Warning",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

  ```
- **CORS Policy:** If your API will be accessed by a frontend (e.g., Next.js, React), ensure that you have properly configured the CORS settings in `Program.cs` to allow only your production domain name.

Only proceed to Step 1 once all these steps are working successfully locally.



---

## Step 1 — Creating an EC2 Instance and preparing the .pem file

1. Go to **AWS Console → EC2 → Launch Instance**.

2. Configure as follows:

- **Name:** `dotnet-api`
- **AMI:** Amazon Linux 2023 (64-bit x86)
- **Instance type:** `t3.small` (or larger)
- **Key pair:** Create new → Give it a name (e.g., `dotnet-api-key`) → **Download the `.pem` file**
- **Security Group:** In the inbound rules, allow **SSH (22)**, **HTTP (80)**, and **HTTPS (443)**.

  3. Click **Launch Instance**.

  4. Move the downloaded `.pem` file into your Project Folder (it should be in the same directory as your `.csproj` file):

```
MyApi/
├── MyApi.csproj
├── dotnet-api-key.pem   ← ဤနေရာတွင် ထားပါ
├── Controllers/
└── ...

```

5. To set the appropriate permissions for the `.pem` file, run the following command in **Git Bash** on your local machine:

```bash
chmod 400 dotnet-api-key.pem

```

## Step 2 — Publishing the App for Amazon Linux

Open **Git Bash** in your local Project Folder and run the following command:

```bash
dotnet publish -c Release -r linux-x64 --self-contained false -o ./publish

```

> **Note:**
>
> - `-c Release` — Builds for better performance.
> - `-r linux-x64` — Targets Amazon Linux (x86_64).
> - `--self-contained false` — Reduces file size by using the .NET Runtime already installed on the server.
> - `-o ./publish` — Puts the published files into the `./publish` folder.

A `publish/` folder will now appear in your project folder.

> If the `dotnet publish` command does not work correctly, you can manually publish by creating a Folder Profile in **Visual Studio** on your local Windows machine as follows:

## Manual Publish with Visual Studio

This is the alternative Step 2 if you prefer to use Visual Studio instead of the .NET CLI command on your local machine.

### How to manually publish using Visual Studio

1. Open your project in Visual Studio.
2. **Right-click** on your project name in **Solution Explorer** and select **Publish...**.
3. Select **Folder** for the **Target** and click Next.
4. Select **Folder** again in **Specific Target** and click Next.
5. You can leave the **Location** (where files will be output) at the default `bin\Release\net8.0\publish\` or select any folder path you prefer, then click **Finish**.
6. Once the profile is created, click on **"Show all settings"** (or the settings icon shaped like a key) to configure the **Configuration Settings** before proceeding:
  - **Deployment Mode:** `Framework-dependent` (to use the runtime on the server)
  - **Target Runtime:** `linux-x64` *(This is the most important setting for Amazon Linux)*
7. **Save** the settings.
8. Click the blue **Publish** button at the top of the page to start the build.

Once the publish process is complete, the files will be placed in the folder path you specified earlier (e.g., `bin\Release\net8.0\publish\`).

> **Note (for Step 3):** Since you published using Visual Studio, you will need to slightly adjust the local folder path in the `scp` command used in **Step 3**.
>
> If you still have Git Bash open at the root of your project folder, run the changed `scp` command as follows:

```bash
scp -i dotnet-api-key.pem -r ./bin/Release/net8.0/publish/* ec2-user@<PUBLIC_DNS>:/home/ec2-user/dotnet-api/

```

## Step 3 — Copying the publish folder to EC2

Continuing in your local Git Bash, replace `<PUBLIC_DNS>` with your EC2 Public DNS (found in AWS Console → EC2 → Instance details, e.g., `ec2-xx-xx-xx-xx.compute-1.amazonaws.com`) and run the following command:

```bash
scp -i dotnet-api-key.pem -r ./publish ec2-user@<PUBLIC_DNS>:/home/ec2-user/dotnet-api

```

Example of how to change it:

```bash
scp -i dotnet-api-key.pem -r ./publish ec2-user@ec2-13-215-12-34.compute-1.amazonaws.com:/home/ec2-user/dotnet-api

```

> **Note:** This command will create a folder named `/home/ec2-user/dotnet-api/` on the EC2 server and copy all published files into it.

## Step 4 — SSH into EC2, install .NET and Nginx

### Connecting to EC2 via SSH

```bash
ssh -i dotnet-api-key.pem ec2-user@<PUBLIC_DNS>

```

### Installing the .NET SDK

```bash
sudo dnf update -y
sudo dnf install -y dotnet-sdk-8.0

```

To verify that the version was installed successfully:

```bash
dotnet --version
# 8.0.x ဟု ထွက်လာရမည်။

```

### Installing Nginx

```bash
sudo dnf install -y nginx

```

### Enabling and Starting Nginx

```bash
sudo systemctl enable nginx
sudo systemctl start nginx

```

## Step 5 — Testing Nginx using Public DNS and Localhost

### Checking that Nginx works via a browser

Open your browser and navigate to the following address:

```
http://<PUBLIC_DNS>

```

You should see the default **Welcome to nginx!** page. If you see it, Nginx is working correctly.

### Checking for localhost responsiveness from within EC2

```bash
curl http://localhost

```

Expected Output (You should see the HTML code of the Nginx Welcome Page):

```html
<!DOCTYPE html>
<html>
...
<title>Welcome to nginx!</title>
...

```

## Step 6 — Running the .dll and testing via Public DNS

### Running the App manually (for foreground testing)

```bash
cd /home/ec2-user/dotnet-api
dotnet MyApi.dll --urls "http://0.0.0.0:5000"

```

*Replace `MyApi.dll` with the actual* `.dll` *file name generated from your* `.csproj`*.*

Expected Output-

```
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://0.0.0.0:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started.

```

### Testing in the Browser

```
http://<PUBLIC_DNS>:5000/swagger
# သို့မဟုတ်
http://<PUBLIC_DNS>:5000/weatherforecast

```

> **Note:** To test directly here, port **5000** must be temporarily opened in the EC2 Security Group. Once you have implemented the Nginx proxy later, you can close this port again. Alternatively, you can open another SSH session and test from within the server as follows:

```bash
curl http://localhost:5000/weatherforecast

```

After testing, press **Ctrl+C** to stop the app.

## Step 7 — Creating Nginx Config for .NET API

Create a new Nginx configuration file for your API:

```bash
sudo nano /etc/nginx/conf.d/dotnet-api.conf

```

Copy and paste the following code:

```nginx
server {
    listen 80;
    server_name _;

    location / {
        proxy_pass         http://127.0.0.1:5000;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection keep-alive;
        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;
        proxy_cache_bypass $http_upgrade;
    }
}

```

To save and exit the file: **Ctrl+O → Enter → Ctrl+X**

Check if the config is correct and reload Nginx:

```bash
sudo nginx -t
# nginx: configuration file /etc/nginx/nginx.conf test is successful ဟု ပြရမည်။

sudo systemctl reload nginx

```

## Step 8 — Deleting the default Nginx server block

The `server { listen 80; }` block included in the default Nginx config often conflicts with the port of the config you just created. Therefore, open it:

```bash
sudo nano /etc/nginx/nginx.conf

```

Find the entire section below and **delete it** (delete everything from `server {` to the closing `}`):

```nginx
# ဤ Block တစ်ခုလုံးကို ဖျက်ပါ ↓
server {
    listen       80;
    listen       [::]:80;
    server_name  _;
    root         /usr/share/nginx/html;

    include /etc/nginx/default.d/*.conf;

    error_page 404 /404.html;
    location = /404.html {
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    }
}
# ဤ Block တစ်ခုလုံးကို ဖျက်ပါ ↑

```

To save and exit the file: **Ctrl+O → Enter → Ctrl+X**

Check and reload again:

```bash
sudo nginx -t
sudo systemctl reload nginx

```

Now, visiting `http://<PUBLIC_DNS>` will redirect directly to your .NET API (you can test this after running the service in Step 9).

## Step 9 — Creating a systemd Service

To keep the API running in the background at all times and to ensure it automatically restarts if the server goes down, we will create a systemd unit file:

```bash
sudo nano /etc/systemd/system/dotnet-api.service

```

Insert the following text — don't forget to replace `MyApi.dll` with your actual `.dll` name:

```ini
[Unit]
Description=ASP.NET Core API
After=network.target

[Service]
WorkingDirectory=/home/ec2-user/dotnet-api
ExecStart=/usr/bin/dotnet /home/ec2-user/dotnet-api/MyApi.dll --urls "http://0.0.0.0:5000"
Restart=always
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=dotnet-api
User=ec2-user
Environment=ASPNETCORE_ENVIRONMENT=Production

[Install]
WantedBy=multi-user.target

```

To save and exit the file: **Ctrl+O → Enter → Ctrl+X**

### Enabling and Starting the Service

```bash
sudo systemctl daemon-reload
sudo systemctl enable dotnet-api
sudo systemctl start dotnet-api

```

### Checking that it is running correctly

```bash
sudo systemctl status dotnet-api

```

Expected Output-

```
● dotnet-api.service - ASP.NET Core API
     Loaded: loaded (/etc/systemd/system/dotnet-api.service; enabled)
     Active: active (running) since ...

```

### Testing End-to-End functionality

```bash
# EC2 Server ထဲမှနေ၍ စမ်းသပ်ရန်
curl http://localhost/weatherforecast

# ပြင်ပ Browser မှနေ၍ စမ်းသပ်ရန်
http://<PUBLIC_DNS>/weatherforecast

```

### Useful commands for future use

```bash
# Live တက်လာမည့် Log များကို ကြည့်ရန်
sudo journalctl -u dotnet-api -f

# Code ပြင်ပြီး အသစ်ထပ်တင်တိုင်း Service ကို Restart ချရန်
sudo systemctl restart dotnet-api

# Service ကို ခေတ္တ ပိတ်ထားရန်
sudo systemctl stop dotnet-api

```

### Redeployment (what to do if you want to deploy new code)

Using **Git Bash** from your local machine's project folder, you can easily redeploy in three steps:

```bash
# ၁။ ဗားရှင်းအသစ်ကို ပြန်ပြီး Publish လုပ်သည်
dotnet publish -c Release -r linux-x64 --self-contained false -o ./publish

# ၂။ ဖိုင်အသစ်များကို EC2 Server ပေါ်သို့ လှမ်းပို့သည်
scp -i dotnet-api-key.pem -r ./publish/* ec2-user@<PUBLIC_DNS>:/home/ec2-user/dotnet-api/

# ၃။ API Service ကို Restart ချပေးသည်
ssh -i dotnet-api-key.pem ec2-user@<PUBLIC_DNS> "sudo systemctl restart dotnet-api"

```
