Joe's Code

a pragmatic approach to web and software development

Tutorial: ServiceStack API with FastCGI Mono Server and Nginx hosted on DigitalOcean

with 21 comments

Who’s this targeted for?
I created this post for my fellow Windows .NET developers who don’t know where to start. As a Windows and .NET developer it was difficult for me to get this working. Mostly because of my lack of experience with Linux and the Nginx/FastCGI stack. After going through this install/deploy/config process many times (really an embarrassing number of times) I’ve come up with a working pattern and documented the process. Hopefully it spares you some of the frustration I encountered. Let me know your thoughts/problems. As a side note, I’m aware I don’t follow some best practices. For example, I’m going to use “root” user everywhere!

First some basic information for the uninitiated.

  • Nginx is an open source high-performance HTTP server. Pronounce “engine x” for the sake of your street credit.
  • FastCGI is a protocol for interfacing external applications to web servers
  • Mono – cross platform, open source .NET development framework. This is how it will our c# code will work on Linux.
  • FastCGI Mono Server – FastCGI server implemented for mono. We will be using fastcgi-mono-server4 for .NET 4.0

1) DigitalOcean Server Setup

If you already have a server or a local environment with Ubuntu 12.04 x32. Great. You can skip the to the good stuff.

For this demo I created an account on DigitalOcean.com. They are a simple cloud hosting site for Linux servers. Its pretty cheap for development work and I’ve only used it for experiments myself. If you are worried about costs then whenever you are done with any development work then take a snapshot of your server, shut it down and destroy it. Offline servers still accrue charges but destroyed servers do not. It is very simple to restore a server from a snapshot so you can preserve your work. Doing this will allow you to work on a server for a few hours and only incur a charge of about a nickel.

I usually create a droplet with 512MB and 1 CPU. This demo was created on the Linux distribution Ubuntu 12.04 x32.

ImageProperties

DigitalOcean – Create Droplet UI

2) Connect to your Server

You will need a SSH client. For windows I use PuTTY Your IP address, and root password will be in an email sent from Digital Ocean upon creation of your Droplet. You can change your password with the “passwd” unix command.

Open up PuTTY and connect to the ip as the user “root”. Don’t worry about the security warning. You will see this often if you are constantly creating/destroying your server.

3) Nginx

First install nginx with the following command from any directory

apt-get install nginx

and now we are almost done with nginx. Seriously, we just need to start the service now. Start it up with the following command for any directory

service nginx restart

Your nginx website is now online. If you hit your ip address from your browser you will see a “Welcome to nginx” page.

Picture Of browser and PuTTY terminal

Chrome and PuTTY

By default the nginx configuration file is located at: /etc/nginx/sites-available/default but you don’t need to make any changes to see it working. Looking at the config file you will see that the welcome page is located at /usr/share/nginx/www/index.html. We will change this path later. Go ahead and make some html changes to index.html now and you will see your changes reflected after refreshing your browser. No restart of the service is necessary. Just like our old friend IIS.

4) Mono FastCGI

You will need to install the following packages:

  1. mono-complete – a large package of the complete Mono runtime, development tools and all libraries.
  2. mono-fastcgi-server4 – the ASP.NET 4.0 backend for FastCGI webservers. It includes mono-xsp4-base

So run the following

apt-get install mono-complete

then

apt-get install mono-fastcgi-server4

If you hit your ip again you will notice our “Welcome to nginx” page is still working because we haven’t re-configured nginx.

5) Deploy your ServiceStack API ASP.NET Website

You are going to need an existing demo API project. You can use one of the official examples but I used a bare bones API for my own purposes. I created an empty WebForms project. Added ServiceStack via nuget then just created a couple routes at the base directory. If you use /api that is great but keep that in mind when testing and configuring your site. If you are having problems you might want to try doing a simple “Hello World” ASP.NET website first. After the project is working locally in Visual Studio I publish to a new folder and will only deploy those files.

Another project you can test with is my open source ServiceStack API project called Open Football Api at GitHub. It is a little more involved and also includes a mono SQLite dependency. This project also works with the steps in this demo.

ASP.NET Project Requirements

  1. First check out the Mono compatibility list here for a general overview.
  2. You should be targeting .NET 4.0 or lower
  3. Use MVC 3 or lower or use WebForms. At the time of this writing MVC 4 is only partial implemented.

I used WinSCP for this but there are many options. Open up the WinSCP and connect to your host via IP and login with root or any user your may have created.

First, setup your server with some paths we will need. I’m going to use the /var/www/ directory to hold all my websites.

mkdir /var/www/

and lets create a place to eventually put our logs. The p switch will create any required parent directories.

mkdir --p /var/log/mono/

Copy the entire fold of your ASP.NET project into /var/www/. I had a project called demo that I copied over. After this step I have the following path /var/www/demo

6) Setup FastCGI

Now we need to tell nginx to proxy requests to mono-fastcgi-server4. We are going to use TCP instead of unix sockets. To do this we need to edit the nginx config. Run the following command:

nano /etc/nginx/sites-available/default

Go ahead and delete everything in the file and then add the code below. I’ve stripped out everything not needed to clarify the code. There are lot of nginx configuration details that are outside the scope of this demo.

server {
   location / {
     fastcgi_index Index.aspx;
     fastcgi_pass 127.0.0.1:9000;
     include /etc/nginx/fastcgi_params;
  }
}

You should of noticed we are including a fastcgi_params file at the bottom. We need to edit that file. Run

nano /etc/nginx/fastcgi_params

Add the following two lines to the top

fastcgi_param  PATH_INFO          "";
fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;

Comment out already existing line by adding a #

#fastcgi_param  SCRIPT_FILENAME         $request_filename;

7) Start Mono FastCGI Server

First verify your configuration changes and reload the service with the following combined commands:

nginx -t && service nginx reload

If you were to hit your ip now from the browser you would see a “502 Bad Gateway” error. This error could be from a lot of things but it will always get thrown when your mono fastcgi server is not running.

So lets start the mono server by running this command

fastcgi-mono-server4 /applications=/:/var/www/demo /socket=tcp:127.0.0.1:9000 /logfile=/var/log/mono/fastcgi.log /printlog=True

Add a space and ampersand (&) to the end of the command to exec as a unix background process.

You will want to run it as one command but here it is for those with small screens.

fastcgi-mono-server4
/applications=/:/var/www/demo
/socket=tcp:127.0.0.1:9000
/logfile=/var/log/mono/fastcgi.log
/printlog=True

You should notice we are using the log path we created earlier with the logfile parameter. We are also going to print any messages to the console/terminal with the printlog=True parameter. Also, if we had not specified the application path then we would of been required to run this command from the directory of our website. In this case from /var/www/demo. Ok. the console should be running the server now. You should not of seen any messages.

Refresh your browser and you should see your demo application running. If you see a 403 Forbidden error Do not panic. Depending on your project you might try /hello or /api/hello.

ServiceStack API Example

ServiceStack API Example

With every request, even successful requests I see the below message logged in the console. I do not know the source of this but the API should still be working fine and returning results.
Error Failed to process connection. Reason: The object was used after being disposed.

To stop the server you can use “Control+C”

Thanks to these resources!

About these ads

Written by jokecamp

June 30, 2013 at 4:20 pm

Posted in Code

Tagged with , , , , ,

21 Responses

Subscribe to comments with RSS.

  1. nice writeup, I find SS API on Mono + Dart client an interesting combination for single page apps. btw, your code samples have escaped some characters, which might confuse linux newbies:

    fastcgi_param PATH_INFO "";

    nginx -t && service nginx reload

  2. thank you for sharing this tutorial

    Ali Hmer

    July 15, 2013 at 3:56 pm

  3. As a .NET developer and a total newbie to linux, you have just saved me about a months worth of trial and error. Thank you!

    Martin McGirk

    August 8, 2013 at 5:25 pm

  4. Very nice explanation and appreciated your effort. Simple asp mvc applications run without problems, but with bigger ones (those with extensive use of reflection) break with this error:
    System.Reflection.ReflectionTypeLoadException: The classes in the module cannot be loaded.

    However you got me on the right track and I owe you a beer. Thanks!

    Mau

    September 6, 2013 at 9:33 pm

    • You’re welcome. This could be because there are missing dlls with the deployment. By default most of the Microsoft dlls are not set to “Copy Local” because they expect the application will be deployed to a windows server with a GAC. So you may want to make sure all the dlls are included in the bin folder.

      Drop me a message if you figure it out.

      jokecamp

      September 7, 2013 at 8:29 am

      • Mmmmhhh… I tried that already, took a look at the log files and there is no clue where could be the issue. Let me go step by step, first deploying a small SS project and increasing the complexity until I get to the source of the error. Will let you know how it was when I’m done with that.

        Thanks again. If this experiment works, and I can move my applications from windows to linux server, that would be worth just because of the money I could save and also, get rid of MS dependencies for good.

        Mau

        September 7, 2013 at 11:18 am

    • I think I found where my code is breaking:

      runDelegate((conn) =>
      {
      foreach (var i in atts.OrderByDescending(c => c.atributeMeta.deleteIndex))
      {
      try
      {
      conn.CreateTable(false, i.tableType);
      }
      catch (Exception ex)
      {
      throw new InvalidConstraintException(string.Format(“Error creating table {0}”, i.tableType.Name), ex);
      }
      }
      });

      atts is just an IEnumerable of a class definition

      The error thrown is: System.Reflection.ReflectionTypeLoadException
      The classes in the module cannot be loaded.

      And the stack is this:
      at (wrapper managed-to-native) System.Reflection.Assembly:GetTypes (System.Reflection.Assembly,bool)
      at System.Reflection.Assembly.GetTypes () [0x00000] in :0
      at Kaizen.Common.Dal.DataManager.b__23[DataClassAttribute] (System.Reflection.Assembly a) [0x00000] in :0
      at System.Linq.Enumerable+c__Iterator20`3[System.Reflection.Assembly,System.Type,f__AnonymousType1`2[System.Reflection.Assembly,System.Type]].MoveNext () [0x00000] in :0
      at System.Linq.Enumerable+c__Iterator2A`1[f__AnonymousType1`2[System.Reflection.Assembly,System.Type]].MoveNext () [0x00000] in :0
      at System.Linq.Enumerable+c__Iterator1C`2[f__AnonymousType1`2[System.Reflection.Assembly,System.Type],System.Type].MoveNext () [0x00000] in :0
      at Kaizen.Common.Dal.DataManager.loadTableTypes (IEnumerable`1 types) [0x00000] in :0
      at Kaizen.Common.Dal.DataManager.loadTableTypes () [0x00000] in :0
      at Kaizen.Common.Dal.DataManager.createDbObjects () [0x00000] in :0
      at Kaizen.Common.Dal.DataManager.createDatabase (System.String connectionStringNoDatabase, System.String databaseName, Boolean createDbObjects) [0x00000] in :0
      at Kaizen.Common.Dal.DataManager.createDatabase (System.String connectionStringNoDatabase, System.String databaseName, Boolean createDbObjects, Boolean insertStartupData) [0x00000] in :0
      at Kaizen.Dal.DataManager.createDatabase (System.String connectionStringNoDatabase, System.String databaseName, Boolean insertStartupData) [0x00000] in :0
      at Kaizen.WebApi.AppHost.checkCreateDbObjects () [0x00000] in :0
      at Kaizen.WebApi.AppHost.Configure (Funq.Container container) [0x00000] in :0
      at ServiceStack.WebHost.Endpoints.AppHostBase.Init () [0x00000] in :0
      at Kaizen.WebApi.AppHost.Start () [0x00000] in :0
      at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
      at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in :0

      I have tried different approach to get there, basiclly removing Microsoft DLL there mono has it counterpart. Other than that I don’t know what could be the issue.

      If you have a moment plase take a look at it.

      Thanks!

      Mau

      September 8, 2013 at 4:14 pm

      • Finally I could make it work. There was a section missing in web.config
        Again, thanks for the post.

        mleyzaola

        September 12, 2013 at 2:21 am

  5. […] jokecamp.wordpress.com […]

  6. Thanks Joe for helpful article,
    I am novice on Linux environment and I tried your way of making servicestack project runs on fstcgi.
    The problem when internet connection between my ssh client and the server ended the website will be no longer working.
    My Question Is there away to make this script running as deamon or automatically when my server starts.

    Thanks in advance…

    Abdulrahman Shaheen

    October 29, 2013 at 1:04 am

    • Hey Abdulrahman,

      Take a look at “Linux startup script for mono FastCGI server” http://yojimbo87.github.io/2010/03/14/mono-startup-script.html It shows you how to make a script for the /etc/init.d/ directory directory that will run on server startup. I have use this for dev/testing servers. Be sure to read that post’s comments and do your own research for a production worthy method.

      jokecamp

      October 29, 2013 at 8:48 am

      • thanks very much

        Abdulrahman Shaheen

        October 30, 2013 at 3:16 am

  7. Dear Joe,
    I liked your open football api and I played around with it. I added pagination and works fine with me. However, when I tried to use sql profiler and request logining features on servicestack, it did not work as expected. Sql profiler does not work on both iis version and mono-fastcgi too. However, request log feature worked on iis but not in mono-fastcgi.
    Any suggestions or work around.

    Regards,
    Abdulrhman

    Abdulrahman Shaheen

    November 6, 2013 at 8:50 am

  8. Thank you very much for your tutorial. I also have this error message “Error Failed to process connection. Reason: The object was used after being disposed” for every incoming connection. My web application still works correctly but I’d prefer to fix this issue because it makes log file a mess and big. Is this error harmless? Have you already figured it out what caused this error?

    Thank you!

    Job

    December 18, 2013 at 1:46 pm

  9. Hello, i’m trying to get web App(page) written in .NET working on Raspberry Pi but without success, fighting with this subject almost 3rd day…

    I have tried multiple “tutorials” but web is still an issue. I can get c# exe working but not a web page. It seem like issue with nginx and cgi setup…
    http://www.raspberry-sharp.org/eric-bezine/2012/10/mono-framework/installing-mono-raspberry-pi/
    http://nyanichan.wordpress.com/2011/05/10/howto-nginx-and-mono-asp-net/

    I have tryed Joe’s code, fresh installation of “raspberry” -> raspbian, all packages updated.

    1st issue seem to be a point 7) from Joe’s code => Start Mono FastCGI Server. There are alerts, see below. I’m not sure how to fixit.

    pi@raspberrypi ~ $ nginx -t && service nginx reload
    nginx: [alert] could not open error log file: open() “/var/log/nginx/error.log” failed (13: Permission denied)
    2014/02/06 13:18:36 [warn] 2301#0: the “user” directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:1
    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    2014/02/06 13:18:36 [emerg] 2301#0: open() “/var/run/nginx.pid” failed (13: Permission denied)
    nginx: configuration file /etc/nginx/nginx.conf test failed

    pi@raspberrypi ~ $ ^C if ignoring this warnigs i’m getting 502 error, here is info from console:

    pi@raspberrypi ~ $ fastcgi-mono-server4 /applications=/:/var/www/demo /socket=tcp:127.0.0.1:9000 /logfile=/var/log/m ono/fastcgi.log /printlog=True
    [0001-00-735270 13:48:51Z] Error Error opening log file: Access to the path “/var/log/mono/fastcgi.log” is denied.
    [0001-00-735270 13:48:51Z] Error Events will not be logged.
    [0001-00-735270 13:51:43Z] Notice Beginning to receive records on connection.
    Exception during TraceManager initialization:
    System.Xml.XmlException: Document element did not appear. Line 11, position 17.
    at Mono.Xml2.XmlTextReader.Read () [0x00000] in :0
    at System.Xml.XmlTextReader.Read () [0x00000] in :0
    at System.Xml.XmlReader.MoveToContent () [0x00000] in :0
    at System.Configuration.Configuration.ReadConfigFile (System.Xml.XmlReader reader, System.String fileName) [0x00000] in :0
    at System.Configuration.Configuration.Load () [0x00000] in :0
    at System.Configuration.Configuration.Init (IConfigSystem system, System.String configPath, System.Configuration.Configuration parent) [0x00000] in :0
    at System.Configuration.Configuration..ctor (System.Configuration.InternalConfigurationSystem system, System.String locationSubPath) [0x00000] in :0
    at System.Configuration.InternalConfigurationFactory.Create (System.Type typeConfigHost, System.Object[] hostInitConfigurationParams) [0x00000] in :0
    at System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration (System.String path, System.String site, System.String locationSubPath, System.String server, System.String userName, System.String password, Boolean fweb) [0x00000] in :0
    [0001-00-735270 13:51:49Z] Error Failed to process connection. Reason: Document element did not appear. Line 11, position 17.

    Your help is really appreciated.

    Thanks, Peter

    parnican

    February 6, 2014 at 8:58 am

    • Looks like you have having a lot of issues related to user permissions. You can try to fix those permissions by explicitly adding them to those directories that are complaining. You can also temporarily reduce the messages by not logging to files. You can remove the logging parameters from the commands to start the server.

      Also for testing purposes only you can try putting “sudo ” before the commands that fail to execute with elevated privileges.

      jokecamp

      February 6, 2014 at 9:15 am

      • Woow, thats really prompt reply! I have tried sudo, even manually changing log folder chmod 777, it helped to get rid of this error:
        nginx: [alert] could not open error log file: open() “/var/log/nginx/error.log” failed (13: Permission denied)

        What i don’t understand is this part:
        2014/02/06 16:31:40 [warn] 5215#0: the “user” directive makes sense only if the master process runs with super-user p rivileges, ignored in /etc/nginx/nginx.conf:1

        If getting this error why default page is working…this really drives me crazy.

        Here are some screenshots from my settings: http://goo.gl/P4SVxX

        Default nginx page [http://goo.gl/9U4Kd4] is working even if getting many errors [http://goo.gl/3EOX3S] from after sudo nginx -t && service nginx reload

        parnican

        February 6, 2014 at 11:54 am

      • Some progress: error disappeared if using sudo also for service -> sudo nginx -t && sudo service nginx reload

        pi@raspberrypi ~ $ sudo nginx -t && sudo service nginx reload nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
        nginx: configuration file /etc/nginx/nginx.conf test is successful
        Reloading nginx configuration: nginx.

        However, this next error: http://goo.gl/EIjuwN

        parnican

        February 6, 2014 at 12:30 pm

      • Next progress with raspberry: if not using default port “80”, its required to define port number in this command fastcgi-mono-server4 /applications=bernolak.dyndns.info:8080/:/var/www/demo /socket=tcp:127.0.0.1:9000 /logfile=/var/log/mono/fastcgi.log /printlog=True

        server {
        listen 8080;
        server_name bernolak.dyndns.info;

        location / {
        root /var/www/demo/;
        index index.html index.htm default.aspx Default.aspx;
        fastcgi_index Default.aspx;
        fastcgi_pass 127.0.0.1:9000;
        include /etc/nginx/fastcgi_params;
        }
        }

        After this change i can see in the console same error as you have: Error Failed to process connection. Reason: The object was used after being disposed.

        But page is still not working, however Bad Gateway 502 disappeared and now i can see:
        No Application Found

        Unable to find a matching application for request:

        Host bernolak.dyndns.info:8080
        Port 8080
        Request Path /Default.aspx
        Physical Path /var/www/demo/Default.aspx

        Any idea?

        parnican

        February 7, 2014 at 6:21 am


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.