Hosting a Plumber API on AWS

The {plumber} logo.

I’ve been putting together a small proof-of-concept API using R and {plumber}. It works flawlessly on my local machine and I was planning on deploying it on an EC2 instance to demo it for a client. However, I ran into a snag: despite opening the required port in my Security Group I was not able to access the API. This is what I needed to do to get it working.

This Didn’t Work

I spun up an EC2 instance and applied a very liberal Security Group: access allowed on all ports from any location. Disaster from a security perspective, but flexible enough to just to get things working.

I installed R and all of the required dependencies and the started the API.

library(plumber)
r <- plumb("api.R")
r$run(port = 8000)

Everything looking good so far.

I tested the API locally on the EC2 instance using curl and it worked as expected. Awesome! I felt like I was on the finishing straight.

Next I tried to access it using the browser from my local machine.

Connection refused message when trying to connect to API.

Not good. I checked to see if I could access it using telnet.

telnet ec2-54-172-17-150.compute-1.amazonaws.com 8000
Trying 54.172.17.150...
telnet: Unable to connect to remote host: Connection refused

Same story. Time to do some research.

Investigation

A combination of Google and StackOverflow (as usual) came to the rescue.

First I checked for any firewall rules that might be blocking the port.

sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy DROP)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination   

Nothing untoward there. Next I checked what ports were being listened on.

netstat -an | grep 8000
tcp        0      0 127.0.0.1:8000          0.0.0.0:*               LISTEN     

Aha! Something is listening on port 8000 but only on the loopback interface. That’s probably the problem.

Fixing It

I had to rummage through the plumber source on GitHub to find this, but it turns out that you can specify a host parameter as well.

library(plumber)
r <- plumb("api.R")
r$run(host = "0.0.0.0", port = 8000)

Let’s check those ports again.

netstat -an | grep 8000
tcp        0      0 0.0.0.0:8000            0.0.0.0:*               LISTEN      

The API should now be visible outside of localhost.

Success

This small change made all the difference. The API now works perfectly from the EC2 instance.

Note to self (again!): what works locally might very well not work when deployed.