April 29, 2026 10 min read

Automating .NET App Deployment on Linux with a Single Bash Script

Every time we spun up a new .NET app on a Linux VM, we had to manually create directories, write a systemd service, configure Nginx, and wire up a deploy script. I got tired of doing it by hand — so I automated all of it into one interactive bash script.

Download deploy.sh (5.67 KB)

Overview

The script handles everything needed to get a new .NET app environment running on Linux — in one go. You run it, answer a few prompts, and it generates all the files and configuration automatically.

Here's what it produces after a single run:

        /var/www/{appName}/{env}/
        ├── artifact/ # drop your .zip release here
        ├── app/ # extracted and running app files
        ├── script/
        │   ├── deploy.sh # generated deploy script
        │   └── logs/ # deploy logs with timestamps

        /etc/systemd/system/{appName}-{env}.service
        /etc/nginx/sites-available/{appName}-{env}
      
Prerequisites

You need a Linux server with Nginx, .NET runtime, and systemd already installed. The script must be run as a user with sudo privileges. Certbot is optional — only needed if you want HTTPS.

What the Script Asks You

When you run the script, it walks you through a short set of prompts before doing anything:

Prompt Example Value What It's Used For
App name your-api Folder structure, service name, Nginx config name
Environment uat Separates dev / qa / uat / prod on the same server
Domain app.example.com Nginx server_name and Certbot SSL
Kestrel port 5000 The port your .NET app listens on internally
DLL filename MyApp.dll The entry point passed to dotnet in systemd
Deploy user deploy The OS user that owns the /var/www folder

After you confirm, it runs everything automatically without further interaction — except for the optional SSL step at the end.

How to Use It

1 Upload the script to your server and make it executable
bash
chmod +x setup-dotnet-app.sh
2 Run the script and follow the prompts
bash
sudo ./setup-dotnet-app.sh
3 Drop your release .zip into the artifact folder
bash
cp myapp-release.zip /var/www/your-api/uat/artifact/
4 Run the generated deploy script to go live
bash
bash /var/www/your-api/uat/script/deploy.sh

What Each Generated File Does

deploy.sh

The deploy script is what you run every time you release a new version. It does the following in order:

  1. Finds the .zip file in the artifact/ folder
  2. Extracts it to a temp directory
  3. Detects and flattens any single nested folder inside the zip
  4. Syncs the contents into the app/ directory
  5. Reloads systemd and restarts the service
  6. Writes a timestamped log file and symlinks it to latest.log
Tip

Check the latest deploy log anytime with: cat /var/www/{appName}/{env}/script/logs/latest.log

systemd Service File

The service file registers your app with systemd so it starts automatically on boot and restarts if it crashes. Key settings worth knowing:

Nginx Config

The Nginx config sets up a reverse proxy that forwards traffic from port 80 (or 443 after SSL) to your Kestrel port. It includes tuned buffer settings to handle large headers — useful for apps that pass JWT tokens in requests.

Optional SSL with Certbot

At the end of the setup, the script asks if you want HTTPS. If you say yes, it runs Certbot automatically using the domain you provided:

bash
sudo certbot --nginx -d "$domain" --non-interactive --agree-tos -m "$sslEmail"

If Certbot fails (usually due to DNS not pointing to the server yet), the script prints a warning but doesn't exit. You can always run Certbot manually later once DNS is sorted.


Summary

This script eliminates the repetitive manual work of setting up a new .NET app environment on Linux. Here's what a single run gives you:

  1. A clean folder structure under /var/www
  2. A ready-to-use deploy script with timestamped logging
  3. A systemd service that auto-starts and auto-restarts the app
  4. An Nginx reverse proxy config pointed at your domain
  5. Optional HTTPS via Let's Encrypt in the same session

The next time you spin up a new environment — dev, QA, UAT, or prod — it's one script run instead of 30 minutes of manual config.

J
John Ripdos

Writing tutorials based on real tasks I encounter at work. Everything here is battle-tested and built from experience.