September 29, 2026

![Josiah Parry](https://ricochet.rs/author-avatars/parry.png)Josiah Parry

# Simplifying app deployment in R

Understanding the \_server.yml standard.

[r](https://ricochet.rs/blog/tags/r) [plumber2](https://ricochet.rs/blog/tags/plumber2) [announcement](https://ricochet.rs/blog/tags/announcement)

Deploying web applications isn’t easy. Anything served over HTTP can be considered a web app. Shiny apps and Plumber APIs are both, technically, web frameworks—which we’ll refer to generally as “apps.”

Each framework requires, at minimum, two things:

1.  The `host` to serve the app on (e.g. `127.0.0.1`)
2.  The `port` for the app to listen on e.g. the `8080` in `http://127.0.0.1:8080`.

Frameworks often provide different customizations and bespoke ways to start them.

The [\_server.yml](https://posit-dev.github.io/plumber2/articles/server_yml.html) standard, introduced by the [`{plumber2}`](https://posit-dev.github.io/plumber2/) R package, defines a standard interface to start apps which will simplify app deployment for R developers.

## The standard

Each R package has its own way to start an app:

-   [`shiny::runApp()`](https://shiny.posit.co/)
-   [`plumber::pr_run()`](https://www.rplumber.io/) , `plumber::plumb("api.R")$run()`
-   [`plumber2::api_run()`](https://posit-dev.github.io/plumber2/)
-   [`Ambiorix$run()`](https://ambiorix.dev)
-   [`nanonext::http_server()`](https://nanonext.r-lib.org/)

Each of these provides, at minimum, a way to set the `host` and the `port` , as well as additional configuration options.

Instead, the `_server.yml` standard enforces that every app can be started the same way:

R

```r
engine:::launch_server(
    "_server.yml",
    host = "127.0.0.1",
    port = 8080L
)
```

Each **engine** must have a function (it doesn’t need to be exported hence the `:::`) called `launch_server()` with 3 arguments:

-   the path to a `_server.yml`
-   the `host` to serve the app on
-   the `port` on the host to use

The `engine` is the R package that is serving the app which is responsible for defining and reading its own `_server.yml` file for configuration.

### File structure

The standard leaves a lot of room for what actually goes into the `_server.yml` file. However, at minimum, it requires one top level field:

yaml

```yaml
engine: pkgname
```

The `launch_server()` function is actually responsible for capturing the configuration in the yaml file and making its own decisions. In the case of `{plumber2}` this includes fields `routes`, `constructor`, and `options`.

## Support in ricochet

As of ricochet [v0.20](https://docs.ricochet.rs/releases/v0.20.0/), the `_server.yml` standard is supported. As a result, ricochet supports _any_ R package that implements the standard. Today, that includes:

-   [{plumber2}](https://posit-dev.github.io/plumber2/)
-   [{mcptools}](https://github.com/posit-dev/mcptools/)
-   [{fiery}](https://fiery.data-imaginist.com)
-   [`{nanonext}` soon](https://github.com/r-lib/nanonext/pull/349)

To illustrate the \_server.yml capabilities, we deployed a website built on [tailwindcss](https://tailwindcss.com) and `{plumber2}` ([source](https://github.com/ricochet-rs/test-apps/tree/main/r/plumber2-tailwind-ssr)).

[penguins-pages](https://gallery.ricochet.rs/app/penguins-pages/)

## Run the app locally

bash

```bash
git clone --filter=blob:none --sparse https://github.com/ricochet-rs/test-apps.git test-apps
git -C test-apps sparse-checkout set r/plumber2-tailwind-ssr
cd test-apps/r/plumber2-tailwind-ssr
```

## Deploy the app

To deploy this app ensure the latest version of the [ricochet cli](https://github.com/ricochet-rs/cli) is installed and logged into the [`try.ricochet.rs`](http://try.ricochet.rs) instance.

bash

```bash
curl -fsSL https://raw.githubusercontent.com/ricochet-rs/cli/main/install.sh | sh
ricochet server add try https://try.ricochet.rs
ricochet login -S try
ricochet deploy -S try
```

---

Source: https://ricochet.rs/blog/server-yml
