Deploy

These are just some of the ways in which you can deploy an ambiorix application, and this is by no means an exhaustive list.

Docker

One of the easiest way to deploy an ambiorix app is by using docker + {renv} + docker compose.

Requirements:

  1. Create a file named Dockerfile at the root directory of your project and add this to it:

    FROM rocker/r-ver:4.3.3
    RUN apt-get update && apt-get install -y \
      git-core \
      libssl-dev
    WORKDIR /app
    COPY . .
    RUN rm -rdf renv/library
    RUN R -e "renv::restore()"
    EXPOSE 5000
    CMD ["Rscript", "index.R"]
    

    Here are some simple explanations of what each line does:

    • FROM rocker/r-ver:4.3.3: Specifies the base Docker image with R 4.3.3. Change the R version to the one used in your project. eg. rocker/r-ver:4.4.2.

    • RUN apt-get ...: Installs system dependencies required for many R packages. Add more as required. For example, the {magick} R package requires libmagick++-dev to be installed, so you’d have:

      RUN apt-get update && apt-get install -y \
        git-core \
        libssl-dev \
        libmagick++-dev
      
    • WORKDIR /app: Sets the working directory inside the container to /app. All subsequent commands in the Dockerfile will run in this directory.

    • COPY . .: Copies all files from your local project directory (on the host machine) to the /app directory inside the container.

    • RUN rm -rdf renv/library: Clears the pre-existing library to ensure a clean restore of R packages.

    • RUN R -e "renv::restore()": Runs that R expression which restores the package dependencies defined in your renv.lock file.

    • EXPOSE 5000: This is more of documentation to your future self that the container will use port 5000 to serve the application. This should be the same port you set ambiorix to run on ie. Ambiorix$new(port = 8000L)$...

    • CMD [ "Rscript", "index.R" ]: This sets the default command to run when the container starts. Replace index.R with your app’s entry point if different. eg. server.R.

  2. Build the docker image:

    sudo docker build -t cute-cats .
    
    • docker build: Command to build a Docker image.
    • -t cute-cats: The -t flag tags/names the image as cute-cats.
    • .: The dot refers to the current directory, meaning Docker will look for the Dockerfile in the current folder.
  3. Create another file named docker-compose.yml and place these contents in it:

    services:
      cute-cats:
        image: cute-cats
        ports:
          - "1028:5000"
        volumes:
          - ./data:/app/data
        restart: unless-stopped
    
    • services: Defines the containers Docker Compose will manage. In this case, we only have one service called cute-cats.
    • image: cute-cats: Tell Docker Compose to use the image we built earlier, named cute-cats.
    • ports: Maps port 1028 on the host machine to port 5000 inside the container (where the ambiorix app is served, remember EXPOSE 5000?). This means you can access the app at localhost:1028.
    • volumes: Maps a folder on the host (./data) to the container’s /app/data directory. This ensures that data in the container is synced with the host and ensures data persistence if the container stops. You can remove the volumes mapping if your app doesn’t need persistent data storage.
    • restart: Ensures the container automatically restarts if it crashes, unless it has been manually stopped.
  4. Run the services:

    sudo docker compose up -d --remove-orphans
    
    • docker compose up creates and starts the containers as defined in the docker-compose.yml file.
    • The -d flag runs the containers in detached mode ie. in the background.
    • The --remove-orphans flag cleans up unused containers.

    This will run the app on port 1028 of the host machine, so you will view it at localhost:1028.

  5. To stop the services do:

    sudo docker compose down
    

    This stops and removes the containers but keeps the Docker images intact, so you can start them again later with docker compose up.

Shiny Server

Surprise surprise! Yes, you can actually deploy Ambiorix apps & APIs using Shiny Server.

This is such good news because you can put your Ambiorix & Shiny apps in one directory and have Shiny Server serve all of them!

Two things to keep in mind:

  • All your Ambiorix entrypoints will have to be named app.R, you can’t just name the files anyhow (eg. index.R, server.R, etc) because Shiny Server will search for app.R.
  • When building frontend applications using Ambiorix, you’d normally set the href attributes of anchor tags to someting like /about or /contact to link to the different endpoints within the same app. However, when the app is hosted on Shiny Server at a URL like http://localhost:3838/app-name, using /about as the link will direct the browser to http://localhost:3838/about (outside your app), which isn’t what you intend. To fix this, you need to prepend the app name to the href values, like href = "/app-name/about". This ensures that the links point the browser to the correct routes within your app.

Example deployment:

Before Shiny Server starts an R process, it sets the SHINY_PORT environment variable, which tells the app which port to run on. In your Ambiorix app, you can retrieve this port using Sys.getenv("SHINY_PORT").

  1. Build your app. Say I have this app.R at /home/mwavu/projects/random:

    library(ambiorix)
    library(htmltools)
    
    home_get <- \(req, res) {
      html <- tags$h3("Yes, Ambiorix IN Shiny Server!")
      res$set_status(200L)$send(html)
    }
    
    home_post <- \(req, res) {
      response <- list(
        code = 200L,
        msg = "An API too!"
      )
      res$set_status(200L)$json(response)
    }
    
    # get port to run app on from Shiny Server
    port <- Sys.getenv("SHINY_PORT")
    
    Ambiorix$
      new(port = port)$
      get("/", home_get)$
      post("/", home_post)$
      start()
    
  2. Configure Shiny Server to serve apps from the /home/mwavu/projects directory. Shiny Server configuration file is typically located at /etc/shiny-server/shiny-server.conf.

    run_as mwavu; # user under which Shiny Server runs
    
    server {
      listen 3838;
    
      # define a location at the base URL
      location / {
    
        # host the directory of ambiorix/shiny apps stored in this dir:
        site_dir /home/mwavu/projects;
    
        # log dir:
        log_dir /var/log/shiny-server;
    
        # ...other configs...
      }
    }
    
  3. Restart Shiny Server

    systemctl restart shiny-server.service
    
  4. Now visit localhost:3838/random. Voila!

An Important Consideration:

While the open-source version of Shiny Server will work well for most users, some advanced functionality (such as managing the number of R processes, load balancing, etc.) will require Shiny Server Professional. In the long run, you’d benefit from learning how to implement these features independently, especially for more control & flexibility.

Shinyapps.io

You can use that same solution for deploying using Shiny Server to host your Ambiorix apps & APIs on shinyapps.io.

Good thing is now you won’t have to deal with any configurations. All you need to remember is to get the port to run the app on and the point I made on anchor tags (if building the frontend).

  1. Using the same example app:

    library(ambiorix)
    library(htmltools)
    
    home_get <- \(req, res) {
      html <- tags$h3("Ambiorix IN shinyapps.io!")
      res$set_status(200L)$send(html)
    }
    
    home_post <- \(req, res) {
      response <- list(
        code = 200L,
        msg = "An API too!"
      )
      res$set_status(200L)$json(response)
    }
    
    # get port to run app on from Shiny Server
    port <- Sys.getenv("SHINY_PORT")
    
    Ambiorix$
      new(port = port)$
      get("/", home_get)$
      post("/", home_post)$
      start()
    
  2. Follow the same steps as for a shiny app: how to deploy to shinyapps.io.

See example live app running on shinyapps.io

Systemd Service

The application can be deployed as a service on any Linux server.

You might need to run these commands as sudo.

  1. Create a new .service file in the /etc/systemd/system/ directory. The name of the file defines the name of the service. We will use cute-cats.service as an example:

    vim /etc/systemd/system/cute-cats.service
    
  2. In that .service file place the following configuration, it creates a service that runs the application at the defined path (/path/to/app).

    [Unit]
    Description=An Ambiorix app about the cutest of cats
    
    [Service]
    ExecStart=cd /path/to/app && /usr/bin/Rscript --no-save --slave -f app.R
    Restart=on-abnormal
    Type=simple
    
    [Install]
    WantedBy=multi-user.target
    
  3. Reload systemd to recognize the new service:

    systemctl daemon-reload
    
  4. Start the service and enable it to automatically start on boot:

    systemctl start cute-cats
    systemctl enable cute-cats
    
  5. Check the service status to ensure it’s running without any issues:

    systemctl status cute-cats