photo of ben szymanski

Setting up ASP.NET/Mono on Mac OS

A guide on how to install and configure ASP.NET / Mono on macOS.

This is post is essentially just a rehashed walk-through of my "Setting up ASP.NET/Mono on Mac OS Server" article from a few weeks ago, with a modification. This time I am going to show you how to set up mod_mono on regular client Mac OS without Server.app. In some ways, I think it is actually easier and more reliable this way.

Install the MDK from Mono

The first thing you need to do is to download Mono from http://mono-project.com. At the time of this writing, there is a preview MDK build for El Cap, so be sure to grab that if you are indeed running El Capitan (10.11). It may also be a good idea to grab a copy of Xamarin Studio (formerly MonoDevelop), and maybe Visual Studio Code as well.

Here comes the hard part: getting mod_mono, the Apache module that lets Apache dish out all of your sweet, sweet .NET code from a UNIX box to the internet.

Install Homebrew

If you do not already have Homebrew installed on your Mac, you should get it installed pronto. Not only will we be using it here, but it's also very handy to have around if you ever need to install a one-off piece of software.

Homebrew Homepage

Install pkgconfig

pkgconfig is a utility we will need later when we compile mod_mono. The easiest way to install it is from Homebrew using the brew command.

Run the following command in Terminal.app:

$ brew install pkgconfig

Install Xcode Command-Line Tools

In particular, we need to install Xcode command-line tools to get libtool installed. This is also going to get used when we compile mod_mono.

Run the following command in Terminal.app:

$ xcode-select --install

Get mod_mono Source

mod_mono is a module that plugs into httpd and kicks off everything needed to serve out .NET to the internet. When httpd launches, mod_mono kicks off a new Mono process. The new Mono process is the runtime upon which your ASP.NET application will run.

You can download the source to mod_mono from the mono project site. You'll want to grab a copy of the latest, which is mod_mono-3.12.tar.gz at the time of this writing.

After the download completes, run the following commands in Terminal.app:

$ mv ~/Downloads/mod_mono-3.12.tar /usr/local
$ cd /usr/local
$ tar -zxvf mod_mono-3.12.tar

Disable El Capitan "rootless"

El Capitan instills a new security measure that prevents anyone or anything from modifying system files. In particular, everything in /usr is protected, even if you try to modify something in /usr using root/sudo. The exception is that you are still able to modify things in /usr/local just fine.

The problem is that when mod_mono installs itself, it wants to put itself into /usr/libexec/apache2, where all of the other httpd modules are located. Because of rootless, /usr/libexec/apache2 is unable to be modified, so the mod_mono install script will fail.

To get around this, we need to disable rootless.

  1. Restart your Mac.
  2. Press and hold cmd-r when you hear the startup chime.
  3. When the system boots, Open Terminal.app by going to Utilities > Terminal, from the menubar.
  4. Type the following commands in:

    $ csrutil disable $ reboot

Build mod_mono

Once your Mac has restarted after disabling rootless, open Terminal.app and type the following:

$ cd /usr/local/mod_mono-3.12
$ ./configure prefix=/Library/Frameworks/Mono.framework/Versions/4.2.1
$ make
$ sudo make install

Once your machine finishes, you should be able to see mod_mono.so located in /usr/libexec/apache2.

Enable El Capitan "rootless"

Now that mod_mono.so has been installed in its rightful location, you can re-enable rootless.

  1. Restart your Mac.
  2. Press and hold cmd-r when you hear the startup chime.
  3. When the system boots, Open Terminal.app by going to Utilities > Terminal, from the menubar.
  4. Type the following commands in:

    $ csrutil enable $ reboot

When your machine restarts this time, rootless protection will be once again enabled, and mod_mono.so will still be installed in /usr/libexec/apache2.

Integrating mod_mono with Apache Bundled in Mac OS

First, a little bit of discussion. Every Mac comes bundled with Apache httpd installed. You can easily control it by using these three commands:

$ sudo apachectl start
$ sudo apachectl stop
$ sudo apachectl restart
$ apachectl configtest

Other Points of Interest:

  1. Bundled httpd modules are located in /usr/libexec/apache2
  2. httpd configuration files are located in /private/etc/apache2
  3. The default document directory is located at /Library/WebServer/Documents
  4. Type $ man apachectl into Terminal.app to see everything it can do.

Modify Your httpd.conf

  1. Open a new Finder window and navigate to /private/etc/apache2.
  2. You should see a file named http.conf. Open it in your favorite text editor.

To help you along, I'm going to show you what modifications I made to my httpd.conf file to get mod_mono working for me.

Example httpd.conf Modifications

Include /private/etc/apache2/mod_mono.conf

<VirtualHost *>
    DocumentRoot "/Library/WebServer/Documents"
</VirtualHost>

<VirtualHost *>
  ServerName webapp.yourmachine.local
  DocumentRoot "/Library/WebServer/WebApp"
  MonoAutoApplication disabled
   AddHandler mono .aspx .ascx .asax .ashx .config .cs .asmx .axd
  KeepAlive off

  MonoServerPath monoapp "/Library/Frameworks/Mono.framework/Versions/4.2.1/bin/mod-mono-server4"

  MonoSetEnv monoapp MONO_IOMAP=all

  AddMonoApplications monoapp "/:/Library/WebServer/aspnetapplication"

  <Location "/">
    Allow from all
    Order allow,deny
    Require all granted
    MonoSetServerAlias monoapp
    SetHandler mono
  </Location>

  <Location "/mono">
    SetHandler mono-ctrl
    Order deny,allow
    Deny from all
    Allow from 127.0.0.1
   </Location>
</VirtualHost>

Points of Interest:

  1. When mod_mono installed itself, it left a handy config file in /private/etc/apache2/mod_mono.conf. This file will automatically load the mod_mono module into httpd on startup. It will register all of the typical ASP.NET filetypes (.aspx, .asmx, .ashx...) with httpd. We are going to put a link to this file in the includeFiles section.
  2. Replace all of the yourname and yourappname placeholder values with appropriate values.
  3. Adding that first virtual host section will allow httpd to point all requests that aren't mod_mono bound, to simply load flat HTML files from the default document directory (/Library/WebServer/Documents).
  4. I stored my ASP.NET web application in /Library/WebServer. Just make sure you DO NOT put the application in /Library/WebServer/Documents!
  5. A nice thing to do might be to assign the virtual host it's own hostname, like "webapp.yourmachine.local". If you have network DNS service set up, you can just add an A record to it with your desired hostname. Or you can do something else.

Do Something Else

If you do not have network DNS set up, working with a virtual host setup like I shared above probably won't work. There is a way around this though. Instead of adding a proper A record into a DNS server you have control over, you can simply edit your local machines' hosts file.

Do this:

Open Terminal.app, then type the following:

$ sudo nano /private/etc/hosts

Add the following line to the end of the file:

127.0.0.1   webapp.yourmachine.local

Press control + x, then save the file.

Now, when you enter webapp.yourmachine.local in your preferred browser, your system will be able to route your request to our virtual host configured above. Note that where I have put yourmachine.local, I usually pull this from the Sharing pane in System Preferences. For example, your machine's name might be something like Tom Jones Macbook Pro, which would give you a local hostname like tom-jones-macbook-pro.local. Technically it doesn't matter, but I think it makes the most sense.

Reference

I found the following links helpful throughout this process: