The Raspberry Pi 4B as a shiny server
RThis blog post will not have any code, but will document how I went from hosting apps on shinyapps.io to hosting shiny apps on my own server, which is a Raspberry Pi 4B with 8 gigs of ram. First of all, why hosting apps on a Raspberry Pi? And why not continue on shinyapps.io? Or why not get one of hose nifty droplets on DigitalOcean? Well for two reasons; one is that I wanted to have full control of the server, and learn some basic web dev/web engineering skills that I lacked. These services simplify the process of deploying and hosting a lot, which of course is a good thing if your only goal is to deploy apps. But I wanted to learn how to do it myself from scratch for some time. True, with a DigitalOcean droplet, I could have learned quite a lot about the whole process as well, but there’s a second problem; the minimum amount of processing power that the droplet needed to run shiny came at 10€ a month. Not a fortune, but already quite expensive for me, since I just wanted to learn some stuff on my free time. Which is why I got a Raspberry Pi 4B with 8 gigs of ram. It’s less than 100€, and now that I have it, I can do whatever I want whenever I want to. If I don’t touch it for several months, no harm done. And if I get tired of it, I’ll make a retro console out of it and play some old schools games. It’s a win-win situation if you ask me.
So first, you should get a Raspberry Pi. Those are quite easy to find online, and there’s many
tutorials available on how to install Ubuntu (or any other Linux distro) on it, so I won’t bother
with that. I also won’t explain to you how to ssh into your Raspberry Pi, again, there’s many tutorials
online. More importantly, is how to get Shiny on it? There’s two solutions; you either install
it from source, or you use Docker. I chose to use Docker, but maybe not in the way you’d expect;
there’s a lot of talk online about dockerizing apps, complete with all their dependencies and
environment. The advantage is that you’re guaranteed that deployment with be very smooth. But the
big disadvantage is that these dockerized apps are huge, around 1GB, or sometimes more. It is true that disk space is
quite cheap nowadays, but still… so I prefer to run a Shiny server from Docker, and then run the
apps out of this server. My apps are thus very small, and it’s only the Shiny server that is huge.
I found a Github repository from user havlev
that explains how to do it here.
I have followed this guide, and created my own docker container, which is based on havlev
’s
one. I added some dependencies (to the base Debian distro included, as well as some more R packages).
If you’re in a hurry, and want to use my Docker image, you can simply type the following on your Raspberry pi:
mkdir shiny-server
cd shiny-server
mkdir apps
mkdir conf
mkdir logs
docker run -d -p 3838:3838 -v shiny-apps:/srv/shiny-server/ -v shiny-logs:/var/log/ -v shiny-conf:/etc/shiny-server/ --name rpi-shiny-server brodriguesco/shiny_1_5:firstcommit
The first 5 commands will create some folders that we’ll need later on, while the last one will pull
my Docker container, which is based on havlev
’s one, launch the server and it’ll start listening to
port 3838.
I made an app (another blog post, focusing on this app, will follow soon), hosted on my Raspberry Pi that you can find here. I’ll also give you some pointers on how you can achieve that.
But let’s start from the beginning.
Adding dependencies to a Docker container
So let’s suppose that you’re me a few weeks ago, and that you find and follow havlev
’s guide here.
Getting the docker running is quite easy, you just need to set up Docker, and then find the line in the
tutorial that starts with docker run
…. You’ll get Shiny running with its hello world app. Now,
how can you add more packages, either to the base Debian image, or R packages? For this part, I
followed this guide.
The idea is to “log in” to the console of the base Debian distro that is running from the container.
First, find the ID of the container by typing the following command in the terminal:
docker ps
You should see something like this:
ubuntu@ubuntu:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
69420blazeit brodriguesco/shiny_1_5:firstcommit "/etc/shiny-server/i…" About a minute ago Up About a minute 0.0.0.0:3838->3838/tcp rpi-shiny-server
now with the ID in hand, you can start any command line program from your Docker container, for instance bash:
docker exec -it 69420blazeit bash
You’ll be “logged in” as root:
root@69420blazeit:/#
and from there, you can install Debian packages. The following two packages are necessary to install many R packages from source, so I recommend you install them:
root@69420blazeit:/# apt-get install libssl-dev libxml2-dev
Once these Debian packages are installed, you can start R by simply typing R
in the same console,
and install whatever packages your Shiny apps will need. In my case, I installed {golem}
and several
others, but this will be the subject of another blog post. We’re almost done with that; we now need
to save the changes because if you restart the container, you’ll lose all these changes. To save these
changes, let’s run the following command, but in a new terminal on your Raspberry Pi (on the
“local” Ubuntu, not the Debian running in the container):
ubuntu@ubuntu:~$ docker commit -m "added some dependencies" 69420blazeit shiny_with_deps
So now you could run this container with the command from above, by replacing the adequate parts:
docker run -d -p 3838:3838 -v shiny-apps:/srv/shiny-server/ -v shiny-logs:/var/log/ -v shiny-conf:/etc/shiny-server/ --name rpi-shiny-server shiny_with_depsshiny_with_deps
Using your Shiny server
Ok so now that the server is running, you can you deploy apps on it? Remember the folders that we
created at the beginning of the blog post (or that you created if you followed havlev
’s guide)?
This is where you’ll drop your apps, the usual way. You create a folder there, and simply put the
ui.R
and server.R
files in here, and that it. These folders can be found in your $HOME
directory,
and they are accessible to your docker container as well. Once you dropped one or two apps, you’ll be able to access them on a link
similar as this one:
http://192.168.178.55:3838/hello/
where 192.168.178.55
is the local IP address of the Raspberry Pi, 3838
is the port the server
is listening to, and /hello/
is the name of the subfolder contained in the ~/shiny-server/apps
folder that you created before. What is left doing is making your Raspberry Pi a proper server that
can be accessed from the internet. For this, you’ll need to ask your ISP for a dynamic IP address.
Generally, you’ll have to pay some money for it; in my case, I’m paying 2€ a month. This address
can then be used to access your Raspberry Pi from the internet. The problem, is that being dynamic,
the address changes every time you restart your server. To solve this issue, you can use a free
dynamic DNS. I use duckdns. This will allow you to have domain that you
can share with the world. What’s nice is that if you follow their guide
the redirection to the dynamic IP address will happen seamlessly every time it changes, so no need
to think about it and do it manually.
Finally, you’ll also have to open up port 3838
on your router. The procedure changes from router
to router, but you should be able to find the instructions for your router quite easily. If not, you
should also be able to get help from your ISP.
The end result is that you’ll have your own Shiny server running off a Raspberry Pi, and accessible over the internet! You’ll be able to deploy as many apps as you want, but of course, don’t forget that you’re running all this on a Raspberry Pi. While these machines have become quite powerful over the years, they won’t be powerful enough if you’re running some heavy duty apps with hundreds of concurrent users.
In my next blog post, I’ll walk you through the development of a Shiny app using the {golem}
package,
which you can find here.
Hope you enjoyed! If you found this blog post useful, you might want to follow me on twitter for blog post updates and buy me an espresso or paypal.me, or buy my ebook on Leanpub.