Puma-dev is one of my favourite tools
Tags: ruby, rails, pumadev, ssl
Published: 2024-02-05 17:03:20

Running applications locally on your laptop is normally a doddle. Boot up a Rails app and point your browser at

```bash
http://localhost:3000
```

Easy peasy. But what if your application is not your average application. Suppose it's multi-tenanted app with a hostname for each tenant and you want to try that locally. Well you could whack a load of entries in your `/etc/hosts`

/etc/hosts
```bash
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1    demo.fossa-are-great.org
127.0.0.1    demo.fossa-are-ok.org
127.0.0.1	localhost
255.255.255.255	broadcasthost
::1             localhost
```

Then you could visit...

```bash
http://demo.fossa-are-great.org:3000/
http://demo.fossa-are-ok.org:3000/
```

...with ease and everything would be fine, even if port 3000 is now a little jarring. It is possible to remove the port if we ran our own version of Nginx running on port 80 and configured it to proxy onto our running application on port 3000. I'm going to go out on a limb and say there are will be a high proportion of developers that won't know how to configure Nginx to do this and it's a bit overkill anyway. However, *puma-dev* makes pretty light work of this.

*Puma-dev: A fast, zero-config development server for macOS and Linux* - [https://github.com/puma/puma-dev](https://github.com/puma/puma-dev)

On macOS it's pretty easy to set up with `brew`

```bash
brew install puma/puma/puma-dev
# Configure some DNS settings that have to be done as root
sudo puma-dev -setup
# Configure puma-dev to run in the background on ports 80 and 443 with the domain `.test`.
puma-dev -install
```

Then in order to configure our hostnames we create a file the ~/.puma-dev per hostname we want to support with the port of the running application.

~/.puma-dev/demo.fossa-are-great
```bash
3000
```
~/.puma-dev/demo.fossa-are-ok
```bash
3000
```

This time we have to visit our domain with the *.test* Top-Level Domain as Puma Dev, by default, supports that TLD. Puma-dev runs on port 80 and 443 as standard and so there is no need to supply a port.

```bash
http://demo.fossa-are-great.test/
http://demo.fossa-are-ok.test/
```

The way this works is really neat and I'm a bit of a fan to be fair. Hostnames on macOS and Linux flavours uses /etc/resolver/. Puma creates a file `/etc/resolver/test` to indicate that name resolution for .test domains should use a local nameserver running on port 9253 to resolve hostname blah.test

```bash
# Generated by puma-dev
nameserver 127.0.0.1
port 9253
```

In fact let's try it 

```bash
dig blah.test @127.0.0.1 -p 9253

; <<>> DiG 9.10.6 <<>> blah.test @127.0.0.1 -p 9253
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2962
;; flags: qr rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;blah.test.			IN	A

;; ANSWER SECTION:
blah.test.		0	IN	A	127.0.0.1

;; Query time: 0 msec
;; SERVER: 127.0.0.1#9253(127.0.0.1)
;; WHEN: Mon Feb 05 15:42:26 GMT 2024
;; MSG SIZE  rcvd: 52
```

Yep, this returns and resolves our domain to 127.0.0.1, however visiting http://blah.test will result in the following because we haven't configured puma-dev to point at an internal port.

```bash
unknown app
```

This is pretty neat huh? Well that's not the end of the story. What if we wanted to use https://demo.fossa-are-great.test. Well that just works out of the box. Like I said it also runs on port 443. Puma manages the certificate authority for the .test domain locally and adds the certificate and key to the local keychain so now we have valid SSL. Magic.

```bash
% ls -la ~/Library/Application\ Support/io.puma.dev 
total 16
drwx------   4 rl  staff   128 16 Jan 09:03 .
drwx------+ 62 rl  staff  1984 29 Jan 02:06 ..
-rw-r--r--   1 rl  staff  1208 16 Jan 09:03 cert.pem
-rw-------   1 rl  staff  1679 16 Jan 09:03 key.pem
```

I've been using this setup for years now and it has enabled us to run multiple applications locally and fully test running up to 5 different multi-tenanted Rails apps logging users in with the same cookie domain as an extreme example.

```bash
https://app1.blah.something.test
https://app1.club.something.test
https://app2.blah.something.test
https://app2.club.something.test
https://app3.blah.something.test
https://app3.club.something.test
```

I have only ever found one issue with this setup and that was today.

## SSL Certificate seemingly invalid

Today's issue involved building PDFs with Grover that had missing styles and as it turned out the this was only over SSL so there was an issue with the certificate which was fixed by re-configuring Puma-dev to presumably re-add the certificate to the keychain.

```bash
puma-dev -uninstall
sudo puma-dev -setup
puma-dev -install
puma-dev -stop
```

Thumbs up for *puma-dev*