Getting an ASP .NET Website Running On Linux

This is a technical blog post for those wanting to implement an ASP .NET website and/or subdomains on a Linux platform. We created this blog post as it was clear that there wasn't really a single full solution available where it was easy to find everything you needed to get one off the ground. The main issues we experienced with trying to do this were primarily because .NET is not native to Linux. Information is partially available on forums, specific posts or application help documentation but is not really complete for scenarios more complex than a single site (i.e. not easily scalable) and I found myself uninstalling and reinstalling packages more times than I care to admit. We feel for those who have to read and undergo hours of trial/error coding to get a result that should be straight forward. The core issue comes about in that in order to get .NET running on Linux you need to install a set of third party packages created under the project Mono. This project was kicked off to help encourage .NET to be used more widely which has been a great undertaking that we particularly appreciate. The solution we show off in the blog post is for a Debian 8 Jessie distro using Nginx for the webserver which can be fairly easy to port to other distros. Remember that we are a website developer so we know the solution we are providing works and it allows us to point out a few issues. We presume you are capable of installing the OS and have an Nginx webserver up and running so we will outline only the necessary information to configure Nginx for interaction not for a secure or specific implementation.
Before we proceed it is important to note that after a certain version of Mono in the major version 5 set that do not function on Debian, for this reason we suggest pinning your Mono updates to the last stable release, namely 5.0.1.1 You do this by running the following command line instruction to update your rep file.
echo "deb http://download.mono-project.com/repo/debian jessie/snapshots 5.0.1.1/main" | sudo tee /etc/apt/sources.list.d/mono-official.list
Let's install the packages we need:
sudo apt-get install mono-complete mono-vbnc mono-xsp4 mono-fastcgi-server4 ca-certificates-mono daemon
We will start by defining a basic Nginx configuration which you should adapt to suit your circumstances:
/etc/nginx/nginx.conf
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
events {
worker_connections 768;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
large_client_header_buffers 4 16k;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
gzip_disable "msie6";
gzip_min_length 1100;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css applciation/json application/x-javascript
text/xml application/xml application/rss+xml text/javascript
images/svg+xml application/x-font-ttf font/opentype
application/vnd.ms-fontobject;
include /etc/nginx/fastcgi_params;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
keepalive_timeout 10 10;
client_header_timeout 10;
client_body_timeout 10;
send_timeout 10;
}
Next we'll specify a specific website configuration for our website and a subdomain (optional) including php handling and cache control, we do this as subdomains are often glossed over. It is assumed that you know how to use symbolic links to enable available website configs:
/etc/nginx/etc/nginx/sites-available/domain.com.au
proxy_cache_path /var/domain.com.au/cache levels=1:2
keys_zone=STATIC:75m inactive=24h
max_size=512m;
server {
listen 80;
server_name *.domain.com.au domain.com.au;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name domain.com.au www.domain.com.au;
root /var/domain.com.au/www;
# SSL settings are excluded as these vary
error_log /var/domain.com.au/logs/error.log warn;
access_log /var/domain.com.au/logs/access.log;
fastcgi_buffers 64 4K;
index default.aspx Default.aspx;
fastcgi_index Default.aspx;
location / {
keepalive_timeout 300;
add_header X-Cache $upstream_cache_status;
proxy_cache STATIC;
proxy_cache_valid 200 30m;
proxy_cache_valid 404 1m;
fastcgi_index Default.aspx;
fastcgi_pass 127.0.0.1:9000;
}
location ~ \.php(?:$|/) {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param HTTPS on;
fastcgi_pass unix:/var/run/php5-fpm.sock;
}
location ~* \.(?:jpg|jpeg|gif|bmp|ico|png|css|js|swf|ttf|otf)$ {
expires -1; # Replace -1 with 7d after testing
access_log off; # Optional - don't log access to assets
add_header Cache-Control "public";
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name sub.domain.com.au;
# Duplicate Settings from the primary domain
}
Let's test our nginx config to make sure this is running ok as it is harder to diagnose issues when you have two potential sources:
sudo nginx -c /etc/nginx/nginx.conf -t
Summing the Nginx config is all good, lets get it running:
sudo service nginx start
Now we'll define a new mono service script to allow mono to be run as a daemon service as this is the most likely way you'll want to run mono:
/etc/init.d/monoserve
#!/bin/bash
### BEGIN INIT INFO
# Provides: monoserve.sh
# Required-Start: $local_fs $syslog $remote_fs
# Required-Stop: $local_fs $syslog $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start FastCGI Mono server with hosts
### END INIT INFO
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/bin/mono
NAME=monoserver
DESC=monoserver
PROGRAM=fastcgi-mono-server4 # The program which will be started
ADDRESS=127.0.0.1 # The address on which the server will listen
PORT=9000 # The port on which the server will listen
USER=www-data # The user under which the process will run
GROUP=$USER # The group under which the process will run
LOGFILE=/var/log/mono/fastcgi.log
# Determine the environment
MONOSERVER=$(which ${PROGRAM})
MONOSERVER_PID=""
FCGI_CONFIG_DIR=/var/www/mono-enabled # /etc/mono/fcgi/apps-enabled
# Start up the Mono server
start_up(){
get_pid
if [ -z "${MONOSERVER_PID}" ]; then
echo "Mono FastCGI Server ${PROGRAM} starting as ${USER} on ${ADDRESS}:${PORT}"
export MONO_IOMAP=all
start-stop-daemon -S -c ${USER}:${GROUP} -x ${MONOSERVER} -- --appconfigdir ${FCGI_CONFIG_DIR} /socket=tcp:${ADDRESS}:${PORT} /logfile=${LOGFILE} &
echo "Mono FastCGI Server ${PROGRAM} started as ${USER} on ${ADDRESS}:${PORT}"
else
echo "Mono FastCGI Server is already running - PID ${MONOSERVER_PID}"
fi
}
# Shut down the Mono server
shut_down() {
get_pid
if [ -n "${MONOSERVER_PID}" ]; then
kill ${MONOSERVER_PID}
echo "Mono FastCGI Server stopped"
else
echo "Mono FastCGI Server is not running"
fi
}
# Refresh the PID
get_pid() {
MONOSERVER_PID=$(ps auxf | grep ${PROGRAM}.exe | grep -v grep |awk '{print $2}')
}
case "$1" in
start)
start_up
;;
stop)
shut_down
;;
restart|force-reload)
shut_down
start_up
;;
status)
get_pid
if [ -z "${MONOSERVER_PID}" ]; then
echo "Mono FastCGI Server is not running"
else
echo "Mono FastCGI Server is running - PID ${MONOSERVER_PID}"
fi
;;
*)
echo "Usage: monoserve (start|stop|restart|force-reload|status)"
;;
esac
exit 0
The next step is to define a website fastcgi xml script to tell Mono how to serve your content, note that the path for mono-enabled is used in the daemon script:
/var/www/mono-enabled/domain.com.au.webapp
<apps>
<web-application>
<name>sub.domain.com.au</name>
<vhost>sub.domain.com.au</vhost>
<vport>443</vport>
<vpath>/</vpath>
<path>/var/domain.com.au/sub</path>
<enabled>true</enabled>
</web-application>
<web-application>
<name>www.domain.com.au</name>
<vhost>www.domain.com.au</vhost>
<vport>443</vport>
<vpath>/</vpath>
<path>/var/domain.com.au/www</path>
<enabled>true</enabled>
</web-application>
<web-application>
<name>domain.com.au</name>
<vhost>domain.com.au</vhost>
<vport>443</vport>
<vpath>/</vpath>
<path>/var/domain.com.au/www</path>
<enabled>true</enabled>
</web-application>
</apps>
Now that we've defined our script lets get it running:
sudo service monoserve start
If you intend to run secure communication based functions you'll need to ensure all certificates are synchronised with mono, use this where the SSL path used for your main domain and sub domain are assumed to be:
sudo cert-sync /etc/nginx/ssl/ca_bundle.crt
So you now need ASP. NET pages (if you have a subdomain) to serve which you should create yourself. If they both show up correctly then you have succeeded.
I would like to kindly thank the various contributors who posted partial solutions to this problem I have documented. As the other solutions are somewhat misleading I did not include them in the outbound links but rather focused on reliable information via the links in the section below. I hope that this post has helped you in some way, saved you from days of banging your head against a wall and made using .NET via Mono. If you are interested in finding out more about what we can do for you then please feel free to visit our main website or contact us. Thank you for your time, for reading our blog post and it would be great if you feel the need to share or like our articles via one of our social media platforms with the @ActsIntuitively tag as applies.
Brent Webster
Technical Services Manager
ActsIntuitively
Bunbury, WA
info@actsintuitively.com.au
ActsIntuitively Website | Psychological Services Website | Shop | Digital Shop | Blog Home
Outbound Links:
Read Prev Post Read Next Post