In this article, we are going to learn how to deploy ASP.NET Core applications to an Ubuntu Linux server with Apache.
Let’s get started.
Create a New ASP.NET Core Web API Project
Let’s create a new solution using the dotnet
command line interface. We’ll open a command prompt window in the location where we want to create the solution and use these commands:
mkdir DeployingToLinuxWithApache cd DeployingToLinuxWithApache dotnet new sln --name DeployingToLinuxWithApache dotnet new webapi --name DeployingToLinuxWithApache dotnet sln add DeployingToLinuxWithApache/DeployingToLinuxWithApache.csproj
This creates a new solution and then adds a new Web API project to the solution.
Install the ASP.NET Core Runtime
Before we can run the ASP.NET Core applications on Linux using Apache, we need to first install the ASP.NET Core runtime. The runtime provides the necessary components and libraries required to run ASP.NET Core applications on Linux:
sudo apt-get update && sudo apt-get install -y aspnetcore-runtime-7.0
When this is completed, let’s run dotnet --info
in the terminal to confirm that it’s been installed successfully:
dotnet --info
After we execute the command, we can inspect the output:
Host: Version: 7.0.8 Architecture: x64 Commit: 4b0550942d .NET SDKs installed: No SDKs were found. .NET runtimes installed: Microsoft.AspNetCore.App 7.0.8 [/usr/lib/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.NETCore.App 7.0.8 [/usr/lib/dotnet/shared/Microsoft.NETCore.App] Other architectures found: None Environment variables: DOTNET_ROOT [/usr/lib/dotnet] global.json file: Not found Learn more: https://aka.ms/dotnet/info Download .NET: https://aka.ms/dotnet/download
The output shows that the runtime is installed, specifically version 7.0.8.
Deploy the App and Run It in Kestrel
Before we deploy to the Linux server, we need to create the deployment folder and make sure it’s set to the right permissions:
mkdir -p /var/www/app chmod 666 /var/www/app
Next, let’s publish the app to a folder on the development machine. In Visual Studio, create a new publish profile with Folder
as the target and then select the folder location. After the profile has been created, let’s click Publish
to create the deployment files in the specified location.
Now that we have the files on the development machine, let’s copy them to the Linux server using WinSCP:
When we install the dotnet
runtime on the Linux server, it comes with Kestrel
which is a lightweight web server for ASP.NET Core. We’ll use Kestrel
to confirm the app runs. Let’s navigate to the deployment path and run it:
dotnet DeployingToLinuxWithApache.dll
This starts up the application and produces the output:
info: Microsoft.Hosting.Lifetime[14] Now listening on: http://localhost:5000 info: Microsoft.Hosting.Lifetime[0] Application started. Press Ctrl+C to shut down. info: Microsoft.Hosting.Lifetime[0] Hosting environment: Production info: Microsoft.Hosting.Lifetime[0] Content root path: /var/www/app
The output shows that Kestrel
has started successfully and is listening on port 5000. We can confirm that the app is running using the curl
command:
curl -X 'GET' 'http://localhost:5000/WeatherForecast' -H 'accept: text/plain'
This makes a GET
request to the /WeatherForecast
endpoint and produces the output:
[ {"date":"2023-07-13","temperatureC":24,"temperatureF":75,"summary":"Warm"}, {"date":"2023-07-14","temperatureC":21,"temperatureF":69,"summary":"Sweltering"}, {"date":"2023-07-15","temperatureC":2,"temperatureF":35,"summary":"Chilly"}, {"date":"2023-07-16","temperatureC":46,"temperatureF":114,"summary":"Chilly"}, {"date":"2023-07-17","temperatureC":-9,"temperatureF":16,"summary":"Warm"} ]
Now that we have it running in Kestrel
, let’s set up Apache
as a reverse proxy server.
Install and Configure Apache on the Server
A reverse proxy server like Apache
excels more than Kestrel
at handling some web-serving capabilities such as caching and compressing requests, as well as serving static content. Let’s install it using apt
:
sudo apt-get update && sudo apt-get install -y apache2
Now that it’s installed, let’s configure it to function as a reverse proxy. First, we need to enable the proxy
and proxy_http
modules:
sudo a2enmod proxy proxy_http
The a2enmod
command enables the modules and produces the output:
Enabling module proxy. Considering dependency proxy for proxy_http: Module proxy already enabled Enabling module proxy_http. To activate the new configuration, you need to run: systemctl restart apache2
Next, let’s create a conf
file in /etc/apache2/sites-available/
named app.conf
:
nano /etc/apache2/sites-available/app.conf
The nano
command creates the file if it does not exist and opens it for editing.
Then, we are going to modify the created file:
<VirtualHost *:80> ProxyPreserveHost On ProxyPass / http://127.0.0.1:5000/ ProxyPassReverse / http://127.0.0.1:5000/ ErrorLog ${APACHE_LOG_DIR}/app-error.log CustomLog ${APACHE_LOG_DIR}/app-access.log common </VirtualHost>
The directives in the file instruct Apache
to listen for requests on port 80 and forward them for handling on port 5000 of the same server, which is where our app is running in Kestrel
.
After this let’s disable the default site, enable the one we just created, and then restart Apache
:
sudo a2dissite 000-default sudo a2ensite app sudo systemctl restart apache2
When we test this using curl:
curl -X 'GET' 'http://localhost/WeatherForecast' -H 'accept: text/plain'
We can see it’s still running as expected:
[ {"date":"2023-07-13","temperatureC":24,"temperatureF":75,"summary":"Warm"}, {"date":"2023-07- 14","temperatureC":21,"temperatureF":69,"summary":"Sweltering"}, {"date":"2023-07-15","temperatureC":2,"temperatureF":35,"summary":"Chilly"}, {"date":"2023-07-16","temperatureC":46,"temperatureF":114,"summary":"Chilly"}, {"date":"2023-07-17","temperatureC":-9,"temperatureF":16,"summary":"Warm"} ]
To wrap this up, let’s configure Kestrel
to run as a Linux service so it will be easier to monitor and manage.
Configure the App to Run as a Service
Let’s create a systemd
unit file named kestrel-app.service
in the /etc/systemd/system
directory:
nano /etc/systemd/system/kestrel-app.service
Then we are going to modify the created file:
[Unit] Description=ASP.NET Core Web API running on Ubuntu [Service] WorkingDirectory=/var/www/app ExecStart=/usr/bin/dotnet /var/www/app/DeployingToLinuxWithApache.dll Restart=always # Restart service after 10 seconds if the dotnet service crashes: RestartSec=10 KillSignal=SIGINT SyslogIdentifier=dotnet-web-api # This user should exist on the server and have ownership of the deployment directory User=deploy Environment=ASPNETCORE_ENVIRONMENT=Production [Install] WantedBy=multi-user.target
Let’s save the file after adding the content, and then enable and start up the service:
sudo systemctl enable kestrel-app.service sudo systemctl start kestrel-app.service sudo systemctl status kestrel-app.service
After enabling and starting the service, the last command checks the status of the service and produces the output:
● kestrel-app.service - ASP.NET Core Web App running on Ubuntu Loaded: loaded (/etc/systemd/system/kestrel-app.service; enabled; preset: enabled) Active: active (running) since Thu 2023-07-13 05:37:45 UTC; 1s ago Main PID: 2889 (dotnet) Tasks: 22 (limit: 9379) Memory: 18.3M CPU: 1.615s CGroup: /system.slice/kestrel-app.service └─2889 /usr/bin/dotnet /var/www/app/DeployingToLinuxWithApache.dll Jul 13 05:37:45 ubuntu systemd[1]: Started ASP.NET Core Web App running on Ubuntu. Jul 13 05:37:46 ubuntu dotnet-web-app[2889]: info: Microsoft.Hosting.Lifetime[14] Jul 13 05:37:46 ubuntu dotnet-web-app[2889]: Now listening on: http://localhost:5000 Jul 13 05:37:46 ubuntu dotnet-web-app[2889]: info: Microsoft.Hosting.Lifetime[0] Jul 13 05:37:46 ubuntu dotnet-web-app[2889]: Application started. Press Ctrl+C to shut down. Jul 13 05:37:46 ubuntu dotnet-web-app[2889]: info: Microsoft.Hosting.Lifetime[0] Jul 13 05:37:46 ubuntu dotnet-web-app[2889]: Hosting environment: Production Jul 13 05:37:46 ubuntu dotnet-web-app[2889]: info: Microsoft.Hosting.Lifetime[0] Jul 13 05:37:46 ubuntu dotnet-web-app[2889]: Content root path: /var/www/app
With this, our deployment is complete and we can use the previous curl
command to confirm that everything still works as expected.
Conclusion
In this article, we have deployed an ASP.NET Core Web API to Ubuntu with Apache as a reverse proxy server.