Jul 26, 2019Hosting Vapor on a Mac

How to host a Vapor site on macOS, using an nginx proxy.

First off, a back story. You should jump ahead if you just want instructions on how to handle hosting Vapor on a Mac.

One of the things I've learned about myself over the years is that I need variety in my work. Every two or three years I look around and see what I could be doing differently to increase productivity and keep software development interesting.

I've had the opportunity over the past 15 years to do a lot of web development work for a variety of clients. Several years ago, after a particularly demanding project, it was clear to me that I needed to find something better on the server side (I was mostly doing PHP at the time). That's when I found Laravel. Laravel felt comfortable from the start, and I've been using it for the past 5 years. I've used it to power everything from simple sites with a few forms to larger sites that process several hundred orders per day. In short, if you program in PHP, you should check it out.

Last year, when I started working on our new website - I again had this feeling that I needed something different. Maybe it was the fact that I'd been using Swift a lot, and going back to PHP just felt... wrong. So, I set about searching to see if there was something that would allow me to stay in Swift-land, something again to make web development fun. I found Vapor.

What's great about Vapor? Much like Laravel, it's just comfortable. I found out later that Laravel was somewhat of an inspiration for Vapor, so that probably helped. The fact that it's Swift means it is instantly familiar, and I feel like I'm context switching less. True, I still need to do front end work in a JavaScript framework, but not continuously moving between PHP and Swift has eliminated a lot of the friction to just getting things done.

This brings me to the other really cool thing about Swift: you can easily leverage existing macOS frameworks to build your backend. For me, this means I can utilize libraries like Core Image or Foundation, opening up a huge set of frameworks that I'm already familiar with. This also exposes what I consider a hole in the ecosystem - hosting. In order to take advantage of Apple frameworks, you need to be running on macOS. For many projects that don't rely only on Foundation, this isn't a problem - Swift on Linux supports these just fine, and there are instructions out there to handle this. Even better, the creators of Vapor have an excellent service they offer, Vapor Red, which I prefer to handle sites that don't require a macOS host. But what if you want to take advantage of the many frameworks available only on macOS?

Hosting Vapor on macOS

This assumes you have the developer tools on your host Mac, but little else. On the host Mac, install Homebrew:

% /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Once installed, pull in Vapor:

 % brew tap vapor/tap<br/> % brew install vapor/tap/vapor

Clone your vapor project to wherever it is you want it to run from, then change your directory to your vapor project and build:

% vapor build --release<br/> % swift build --configuration release

Run your project:

% .build/release/Run --env=production

You need to make sure that your site starts up on boot. For this, I use supervisord, which you can install using homebrew:

% brew install supervisor

Create the supervisord configuration file:

% mkdir /usr/local/etc/supervisor.d
% nano /usr/local/etc/supervisor.d/yourapp.ini

Open a text editor, and enter the following for the yourapp.ini file:

[program:<program name>]
directory = [program directory]
command= bash -c ".build/release/Run serve --env production --hostname 0.0.0.0"
user=<user name>
autostart=true
autorestart=true
startretries=3

stdout_logfile=/usr/local/var/log/supervisor/<program name>-stdout.log
stderr_logfile=/usr/local/var/log/supervisor/<program name>-stderr.log

Make sure that whatever directory you choose to put your log files in, that it exists before you start supervisord. Then, start the supervisord service: % brew services start supervisor

The next thing to do is to create a proxy which passes traffic from the http port (80) and, optionally, the https port (443), to the running application. To do so, first ensure that apache isn't already running on your host:

% sudo launchctl unload -w /System/Library/LaunchDaemons/org.apache.httpd.plist</div>

Then go ahead and install nginx (also fomr homebrew):

% brew install nginx
% sudo brew services start nginx</div>

Set up nginx:

% sudo nano /usr/local/etc/nginx/nginx.conf

Modify the user on the first line:

user [your user id] staff;

Comment out the existing server block (or remove it).

Create a new server in the servers directory:

% cd /usr/local/etc/nginx/servers
% nano [servername]

Set up a new server block that redirects all traffic on port 80 to port 8080 (where your vapor project is running):

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    location / {
        proxy_pass http://127.0.0.1:8080;
    }
}

Once this is complete, just restart nginx and you should be able to browse to your server on port 80:

% sudo brew services start nginx

I'd recommend setting up SSL for whatever you're doing, certbot works great for this. Instructions are available on their website.

That's it for now. If you have any questions or comments, I'm reachable both via email at rob@boundarylabs.com and on Twitter at @rminerick.

Other pertinent references that might help: