Getting an ASP .NET Website Running On Linux


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:

  1. Mono Website

  2. EPM Junkie - Mono / FastCGI Startup Script

  3. Nginx Website - Mono

  4. Github - SSL / TLS Support In Mono