Getting Started with buoyant

This guide walks you through deploying your first _server.yml application using buoyant.

What is buoyant?

buoyant is an R package for deploying web server applications to DigitalOcean. It works with any R web framework that implements the _server.yml standard, including:

When you deploy an application with buoyant:

  1. Your application files are uploaded to the server
  2. The engine package is installed if not present
  3. A systemd service is created for automatic restarts
  4. Nginx is configured as a reverse proxy
  5. Your application is accessible via HTTP (or HTTPS with do_configure_https())

The _server.yml Standard

The _server.yml standard is a lightweight specification that allows different R web frameworks to be deployed consistently. At minimum, your application needs a _server.yml file with an engine field:

engine: plumber2

The engine R package must contain a launch_server(settings, host = NULL, port = NULL, ...) function that: - Accepts the path to the starting server file as the first argument. The starting server file must have a _server.yml in the same folder. - Can override host and port via parameters - Starts the web server

There are no regulations on any other fields within _server.yml.

Case study: plumber2

{plumber2} adheres to the _server.yml standard and in the following we will go through how it does so as an example for other engine developers who wish to support it.

YAML content

{plumber2} currently defines 3 fields in addition to the engine field:

{plumber2} provide a function (create_server_yml()) that creates this file for you so that the user can be confident that it is formatted correctly. This is generally a good idea and also provides a place to document the content of the file.

launch_server()

The launch_server() function is quite simple for {plumber2} since the api() constructor itself understands _server.yml files as input. Because of this the function looks like this:

{.r launch_server} function(settings, host = NULL, port = NULL, ...) { pa <- api(settings) if (!is.null(host)) { pa$host <- host } if (!is.null(port)) { pa$port <- port } api_run(pa) } <environment: namespace:plumber2>

Prerequisites

  1. R (>= 3.0.0)
  2. DigitalOcean Account - Sign up at https://www.digitalocean.com
  3. SSH Keys - Configure SSH keys for your system

Step 1: Create Your Application

Create a _server.yml compliant application. For example, with {plumber2}:

{.yaml server_yaml} #| file: _server.yml engine: plumber2 routes: app.R constructor: ~ options: {}

```{.r app} #| file: app.R #* @get /hello function() { list(message = “Hello from buoyant!”) }

#* @get /add #* @query a First number #* @query b Second number function(query) { result <- as.numeric(querya) + as.numeric(queryb) list(result = result) }


## Step 2: Test Locally (Optional)

If you have `{plumber2}` installed:

```{.r}
library(plumber2)
api("_server.yml") |> api_run()

Visit http://127.0.0.1:8080/api-docs to see your API documentation.

Step 3: Set Up DigitalOcean

# Authenticate with DigitalOcean
# This will open a browser for authentication
analogsea::do_oauth()

# Check authentication and existing droplets
analogsea::droplets() # Should return a list (may be empty)

Configure SSH Keys

Make sure you provide digitial ocean your public key at https://cloud.digitalocean.com/ssh_keys. Github has some good advice on creating a new public key if you don’t already have one: https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent.

Be sure to store your Host to digitalocean.com within your ssh config to point to your private key that you’ve created:

Host github.com
  UseKeychain yes
  IdentityFile ~/.ssh/newly_created_key_file

If you didn’t use a password when creating your SSH key, you can skip the UseKeychain line.

You can verify your DigitalOcean SSH key exists with the proper host with by calling buoyant::do_keyfile() and having it return the path to your private key.

Step 4: Provision a Server

library(buoyant)

# This will cost money! Check DigitalOcean pricing first.
# A basic droplet costs around $6/month (as of 2025)

droplet <- do_provision(
  name = "my-api-server",
  region = "sfo3", # San Francisco
  size = "s-1vcpu-1gb" # Basic tier
)

# This will take a few minutes...
# It installs R, nginx, firewall, and sets up the server structure

Step 5: Deploy Your Application

# Validate your _server.yml file first
validate_server_yml("./")

# Deploy!
droplet |>
  do_deploy_server(
    path = "api", # URL sub-path: /api
    local_path = "./_dev/barret", # Local directory
    port = 8000, # Internal port
    forward = TRUE, # Forwards `/` to `/api`
    overwrite = TRUE
  )

The deployment process:

  1. Uploads your application files
  2. Installs the engine package (e.g. {plumber2})
  3. Creates a systemd service for auto-restart
  4. Configures nginx as a reverse proxy
  5. Starts your application

Step 6: Access Your API

# Get the URL
url <- do_ip(droplet, "/api")
print(url) # e.g., "http://165.232.134.23/api"

# Open in browser
browseURL(paste0(url, "/hello"))

# Or use httr2 to test
httr2::request(paste0(url, "/hello")) |>
  httr2::req_headers(Accept = "text/plain") |>
  httr2::req_perform() |>
  httr2::resp_body_string()
#> [1] "Hello from buoyant!"

httr2::request(paste0(url, "/add?a=5&b=3")) |>
  httr2::req_headers(Accept = "text/plain") |>
  httr2::req_perform() |>
  httr2::resp_body_string()
#> [1] "8"

Step 7: Monitor Your Application

SSH into your droplet to check logs:

analogsea::droplet_ssh(droplet, "journalctl -u server-api -f")

Or to see if the service is running

analogsea::droplet_ssh(droplet, "systemctl status server-api")

Optional: Add HTTPS

If you have a domain name:

# Point your domain to the droplet IP
ip <- do_ip(droplet)
print(ip)

# After DNS is configured (wait for propagation):
do_configure_https(
  droplet = droplet,
  domain = "api.yourdomain.com",
  email = "you@example.com",
  terms_of_service = TRUE # Agree to Let's Encrypt TOS
)

Now your API is available at https://api.yourdomain.com/api

What’s Next?

You’ve successfully deployed your first application! Here are some next steps:

For detailed information, see:

Getting Help