How to Host Shiny Apps on the DigitalOcean App Platform
Tags: shiny, docker, rstats
Published: 2021-10-02 17:08:38

*By: [Peter Solymos](https://hosting.analythium.io/author/peter/)*

**The DigitalOcean App Platform is another PaaS offering that allows you to publish Shiny apps without worrying about infrastructure.**

DigitalOcean announced the App Platform a [year ago](https://www.digitalocean.com/press/releases/digitalocean-launches-app-platform/) as the platform-as-a-service(PaaS) offering that automates infrastructure management. Developers can deploy their code to production in just a few clicks.

The App Platform can publish applications from your GitHub, GitLab or public Git repositories, or publish a container image you have already uploaded to a Docker registry. The platform supports GitOps (git push-based deployment), and vertical and horizontal scaling.

In spirit and most practical aspects, the platform is very similar to [Heroku](https://hosting.analythium.io/tag/heroku/). You can use [Buildpacks](https://docs.digitalocean.com/products/app-platform/build-system/cloud-native-buildpacks/) or [Dockerfiles](https://docs.digitalocean.com/products/app-platform/build-system/dockerfile-build/) to build applications. There is no native Buildpack for R. But as we saw before, Shiny apps can be containerized. This gives us a way to deploy to the App Platform seamlessly.

This post focuses on how to deploy a Shiny app from an [existing Docker image](https://docs.digitalocean.com/products/app-platform/how-to/deploy-from-registry/) to the App Platform. This is a fully no-code experience because we already built and pushed the image. You will use the `r-minimal` Alpine Linux-based [COVID19 app](https://hub.docker.com/r/analythium/covidapp-shiny/tags?page=1&ordering=last_updated) that we build before.


## Prerequisites

Visit the [App Platform](https://www.digitalocean.com/products/app-platform/) landing page and sign up for a DigitalOcean account:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-17-at-11.39.28-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-17-at-11.39.28-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-17-at-11.39.28-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-17-at-11.39.28-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-17-at-11.39.28-PM.png 2400w" width="2000" height="1301" />
</figure>

Once have an account, log in and go to your dashboard. In the left drawer, click on 'Apps':

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-17-at-11.40.52-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-17-at-11.40.52-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-17-at-11.40.52-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-17-at-11.40.52-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-17-at-11.40.52-PM.png 2400w" width="2000" height="1301" />
</figure>

## Launch your Shiny app from the dashboard

### Deploy

Click on the 'Launch Your App' button:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-17-at-11.41.14-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-17-at-11.41.14-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-17-at-11.41.14-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-17-at-11.41.14-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-17-at-11.41.14-PM.png 2400w" width="2000" height="1301" />
</figure>

The next screen will present your options. Select Docker Hub:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-17-at-11.41.43-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-17-at-11.41.43-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-17-at-11.41.43-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-17-at-11.41.43-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-17-at-11.41.43-PM.png 2400w" width="2000" height="1301" />
</figure>

Next, type in the repository name (`analythium/covidapp-shiny`) and the tag (`minimal`), then click 'Next':

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-17-at-11.44.40-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-17-at-11.44.40-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-17-at-11.44.40-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-17-at-11.44.40-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-17-at-11.44.40-PM.png 2400w" width="2000" height="1301" />
</figure>

On the next screen, leave the Type as `Web Services`, set the Run command to the `CMD` command of the [Dockerfile](https://github.com/analythium/covidapp-shiny/blob/main/99-images/Dockerfile.minimal) that you are deploying (without the square brackets), and change the port to the one exposed by Docker (3838):

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-17-at-11.54.01-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-17-at-11.54.01-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-17-at-11.54.01-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-17-at-11.54.01-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-17-at-11.54.01-PM.png 2400w" width="2000" height="1301" />
</figure>

On the next screen, you can edit the app's name your data region:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-17-at-11.47.26-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-17-at-11.47.26-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-17-at-11.47.26-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-17-at-11.47.26-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-17-at-11.47.26-PM.png 2400w" width="2000" height="1301" />
</figure>

The last step is to select the plan. Non-static apps require a paid plan ($5 US/month and above):

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-17-at-11.48.14-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-17-at-11.48.14-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-17-at-11.48.14-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-17-at-11.48.14-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-17-at-11.48.14-PM.png 2400w" width="2000" height="1301" />
</figure>

This is where you can set the number of replicates (horizontal scaling). Finally, click the 'Launch' button:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-17-at-11.48.25-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-17-at-11.48.25-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-17-at-11.48.25-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-17-at-11.48.25-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-17-at-11.48.25-PM.png 2400w" width="2000" height="1301" />
</figure>

### Check the deployment

After a few seconds, you'll see the app deployed on a subdomain: `app-name-hash.ondigitalocean.app`:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-17-at-11.55.02-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-17-at-11.55.02-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-17-at-11.55.02-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-17-at-11.55.02-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-17-at-11.55.02-PM.png 2400w" width="2000" height="1301" />
</figure>

Visit the link to check the app:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-17-at-11.55.15-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-17-at-11.55.15-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-17-at-11.55.15-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-17-at-11.55.15-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-17-at-11.55.15-PM.png 2400w" width="2000" height="1301" />
</figure>

Look around in the dashboard to inspect the deployment and runtime logs. You can even access the container through the console. Under the insights tab, you can check CPU and memory consumption:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-12.06.34-AM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-12.06.34-AM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-12.06.34-AM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-12.06.34-AM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-18-at-12.06.34-AM.png 2400w" width="2000" height="1252" />
</figure>

### Add a custom domain

You can add a [custom domain](https://docs.digitalocean.com/products/app-platform/quickstart/#register-a-custom-domain) using a CNAME record with your DNS provider. Go to your app's settings tab, find the 'Add Domain' button:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-17-at-11.57.30-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-17-at-11.57.30-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-17-at-11.57.30-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-17-at-11.57.30-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-17-at-11.57.30-PM.png 2400w" width="2000" height="1301" />
</figure>

Follow the prompts and copy the app's current URL:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-17-at-11.58.57-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-17-at-11.58.57-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-17-at-11.58.57-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-17-at-11.58.57-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-17-at-11.58.57-PM.png 2400w" width="2000" height="1354" />
</figure>

Go to your DNS provider (Google Domains shown here) and add a CNAME record, and save:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-12.01.39-AM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-12.01.39-AM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-12.01.39-AM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-12.01.39-AM.png 1600w, https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-12.01.39-AM.png 1666w" width="1666" height="836" />
</figure>

After some time the custom domain is live and listed under settings:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-12.03.29-AM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-12.03.29-AM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-12.03.29-AM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-12.03.29-AM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-18-at-12.03.29-AM.png 2400w" width="2000" height="1252" />
</figure>

Visit the new link, note that it is served over secure HTTPS protocol:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-12.03.03-AM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-12.03.03-AM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-12.03.03-AM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-12.03.03-AM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-18-at-12.03.03-AM.png 2400w" width="2000" height="1252" />
</figure>

### Update the app image

You can edit the source image tag in your app's settings. Once the new version is tagged and pushed to the Docker registry, just edit the tag and the app will be [redeployed](https://docs.digitalocean.com/products/app-platform/how-to/deploy-from-registry/#change-the-tag-of-the-container-source-after-deployment) to the App Platform.

## Programmatic image deployment

Of course, you can use the `doctl` [command-line tool](https://docs.digitalocean.com/products/app-platform/references/command-line/) and a YAML-based [app specification](https://docs.digitalocean.com/products/app-platform/references/app-specification-reference/) file format for deployment. You can find the app specification in the control panel, look under the Settings tab:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-17-at-11.56.51-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-17-at-11.56.51-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-17-at-11.56.51-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-17-at-11.56.51-PM.png 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/09/Screen-Shot-2021-09-17-at-11.56.51-PM.png 2400w" width="2000" height="1301" />
</figure>

Most of the info in the spec file is self-explanatory and relates to all the settings we made manually. Let's grab the YAML file and use it as a starter.

To use the app spec from the command line you have to [install the](https://docs.digitalocean.com/reference/doctl/how-to/install/) `doctl` [command-line tool](https://docs.digitalocean.com/reference/doctl/how-to/install/) and follow the steps described in the article to use the API token to grant `doctl` access to your DigitalOcean account.

### Deploy an app

We'll make some changes to the app specs and deploy a different app, the one that [visualizes the correlation](https://hosting.analythium.io/the-quickest-way-to-add-new-apps-to-shinyproxy/) between 2 variables. This will use the `analythium/correlation:v1` [Docker image](https://hub.docker.com/repository/docker/analythium/correlation). Create an empty file called `shiny-app.yml` and add the following content to it:

``` yaml
alerts:
- rule: DEPLOYMENT_FAILED
- rule: DOMAIN_FAILED
name: analythium-correlation-shiny
region: nyc
services:
- http_port: 3838
  image:
    registry: analythium
    registry_type: DOCKER_HUB
    repository: correlation
    tag: v1
  instance_count: 1
  instance_size_slug: basic-xxs
  name: shiny-app
  routes:
  - path: /
  run_command: R -e "shiny::runApp('/home/app',port=3838,host='0.0.0.0')"
  source_dir: /
```

Let's use some `doctl apps` commands now (these are the commands specific to the App Platform). First, we validate the YAML specification:

``` bash
doctl apps spec validate shiny-app.yml
```

We either get an error message or the file content. Once the app spec passed inspection, we can use it to create an app:

``` bash
doctl apps create --spec shiny-app.yml
```

We'll see the newly created app info. Take note of the `ID`. You can get the `ID` later using the `doctl apps list` command to list all the apps. This will give the `ID`, and the Default Ingress, which is the assigned subdomain (https://analythium-correlation-shiny-vil3a.ondigitalocean.app in this case). If the ingress field is empty, it means the deployment is in progress or it has failed.

Use the app ID to see the deployment and runtime logs (edit the `APP_ID` value according to your console output):

``` bash
export APP_ID="5c187c9a-5d1a-4988-8848-82905d0f0302"

doctl apps logs $APP_ID --type deploy
doctl apps logs $APP_ID --type run
```

If all went well, you should see the app after visiting the ingress URL:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-2.09.53-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-2.09.53-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-2.09.53-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-2.09.53-PM.png 1600w, https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-2.09.53-PM.png 1918w" width="1918" height="1476" />
</figure>

### Update the app

You can change the YAML spec file, e.g. change the `services.image.tag` property to `tag: v2` and save the file. Then run the following command:

``` bash
doctl apps update $APP_ID --spec shiny-app.yml
```

This will update the app in a few minutes:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-2.15.46-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/09/Screen-Shot-2021-09-18-at-2.15.46-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/09/Screen-Shot-2021-09-18-at-2.15.46-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/09/Screen-Shot-2021-09-18-at-2.15.46-PM.png 1600w, https://hosting.analythium.io/content/images/2021/09/Screen-Shot-2021-09-18-at-2.15.46-PM.png 1918w" width="1918" height="1476" />
</figure>

### Delete the apps

Once you are done playing with the new deployment and you don't need it anymore, just click the 'Actions' button, select 'Destroy' from the drop-down menu. Or use the app ID and `doctl apps delete $APP_ID` to get rid of the apps. Also, remove the CNAME record from your DNS if you have set up a custom domain.

## Conclusions

The DigitalOcean App Platform makes it straightforward to deploy Shiny apps without ever opening your terminal. But if you prefer, the command-line option only needs a YAML file and a single command.

Docker once again proved to be a really useful piece of technology. We saw how immutable container images can be moved around the internet without issues. We'll cover the GitHub and App Platform integration in some of the next posts.

## Further reading

-   [App Platform documentation](https://docs.digitalocean.com/products/app-platform/)