# Create _server.yml in the working directory
create_server_yml(
"api.R",
"routes/users.R",
"routes/data.R",
options = list(docs = TRUE, port = 8000, host = "0.0.0.0")
)Once you have developed your plumber2 API, the next step is to find a way to host it. If you haven’t dealt with hosting an application on a server before, you may be tempted to run the api_run() function from an interactive session on your development machine (either your personal desktop or an RStudio Server instance) and direct traffic there. This is a dangerous idea for a number of reasons:
For these reasons and more, you should consider setting up a separate server on which you can host your plumber2 APIs. There are a variety of options that you can consider.
_server.yml fileSince plumber2 API specifications can be spread out over multiple files we need a single file that is the source of truth for what the API is based on. plumber2 uses the _server.yml specification for this and you can create a scaffold of such a file using create_server_yml().
The _server.yml file not only contains the R files that make up your API, but can also holds options that modify how the API is constructed (see get_opts()).
_server.yml fileAt minimum, your _server.yml file needs to specify the engine:
engine: plumber2A more complete example might look like:
engine: plumber2
routes:
- api.R
- routes/users.R
- routes/data.R
options:
host: 0.0.0.0
port: 8000
docs: trueYou can create a scaffold of this file programmatically:
# Create _server.yml in the working directory
create_server_yml(
"api.R",
"routes/users.R",
"routes/data.R",
options = list(docs = TRUE, port = 8000, host = "0.0.0.0")
)_server.yml fileOnce you have a _server.yml file you should verify that it works as expected before deploying. You can do this by passing it to api() and testing the constructed API locally:
# Test that it works
api("_server.yml") |>
api_run()This will start your API locally using the same configuration that will be used in production. Make sure to test all your endpoints and verify the behavior matches your expectations.
For more details on the _server.yml specification, see the dedicated article.
Posit Connect is an enterprise publishing platform from Posit. It supports automatic deployment of plumber2 APIs from RStudio and Positron using the rsconnect package and from Positron using the Posit Publisher extension.
Posit Connect automatically manages the number of R processes necessary to handle the current load and balances incoming traffic across all available processes. It can also shut down idle processes when they’re not in use. This allows you to run the appropriate number of R processes to scale your capacity to accommodate the current load.
DigitalOcean is a cloud infrastructure provider that makes it easy to deploy and scale web applications. The buoyant R package provides a streamlined way to deploy plumber2 APIs (and any other _server.yml compliant application) to DigitalOcean.
Before deploying to DigitalOcean, you’ll need:
buoyant and analogsea packages installed:install.packages("pak")
pak::pak(c("buoyant", "analogsea"))_server.yml file for your API (see The _server.yml file section above)Here’s how to deploy a plumber2 API to DigitalOcean:
library(buoyant)
library(analogsea)
# Authenticate with DigitalOcean (opens browser for OAuth)
do_oauth()
# Provision a new droplet (virtual server)
droplet <- do_provision(region = "sfo3")
# Deploy your application
do_deploy_server(
droplet = droplet,
path = "myapi",
local_path = "path/to/my-api",
port = 8000
)
# Get the URL to access your API
do_ip(droplet, "/myapi")
#> [1] "http://165.232.143.22/myapi"Your API is now live! The do_deploy_server() function automatically:
For more detailed information about deploying with buoyant, see the buoyant package documentation.
Docker is a containerization platform that allows you to package your API with all its dependencies into a portable container. This ensures consistent behavior across different environments (development, staging, production).
To run a plumber2 API in Docker, create a Dockerfile that:
_server.yml fileWe recommend using the Rocker Project’s rocker/r-ver images as your base. These images provide versioned, minimal R installations optimized for reproducibility. Key benefits include:
Learn more about the rocker/r-ver images at https://rocker-project.org/images/versioned/r-ver.html.
To determine which system packages are required for plumber2 and its dependencies, use pak::pkg_sysreqs():
#| eval: false
pak::pkg_sysreqs(c("plumber2"), sysreqs_platform = "ubuntu")
#> ── Install scripts ─────────────────────────────────────────── Ubuntu NA ──
#> apt-get -y update
#> apt-get -y install libx11-dev libcurl4-openssl-dev libssl-dev make \
#> zlib1g-dev pandoc cmake xz-utils libfreetype6-dev libjpeg-dev \
#> libpng-dev libtiff-dev libwebp-dev libsodium-dev libicu-dev \
#> libfontconfig1-dev libfribidi-dev libharfbuzz-dev rustc cargo \
#> libxml2-dev
#>
#> ── Packages and their system dependencies ─────────────────────────────────
#> clipr – libx11-dev
#> curl – libcurl4-openssl-dev, libssl-dev
#> fs – make
#> httpuv – make, zlib1g-dev
#> knitr – pandoc
#> nanonext – cmake, xz-utils
#> ragg – libfreetype6-dev, libjpeg-dev, libpng-dev, libtiff-dev, libwebp-dev
#> sodium – libsodium-dev
#> stringi – libicu-dev
#> svglite – libpng-dev
#> systemfonts – libfontconfig1-dev, libfreetype6-dev
#> textshaping – libfreetype6-dev, libfribidi-dev, libharfbuzz-dev
#> waysign – cargo, rustc, xz-utils
#> websocket – libssl-dev, make
#> xml2 – libxml2-devHere’s an example Dockerfile for a plumber2 API:
FROM rocker/r-ver:4
RUN apt-get update && apt-get install -y \
libx11-dev libcurl4-openssl-dev libssl-dev make \
zlib1g-dev pandoc cmake xz-utils libfreetype6-dev libjpeg-dev libpng-dev \
libtiff-dev libwebp-dev libsodium-dev libicu-dev libfontconfig1-dev \
libfribidi-dev libharfbuzz-dev rustc cargo libxml2-dev \
&& rm -rf /var/lib/apt/lists/*
RUN R -q -e 'install.packages(c("plumber2"))'
WORKDIR /app
COPY . /app
EXPOSE 8000
CMD ["R", "-e", "plumber2::api('_server.yml') |> plumber2::api_run(host='0.0.0.0', port=8000)"]Build and run your container:
# Build the image
docker build -t my-plumber2-api .
# Run the container
docker run -p 8000:8000 my-plumber2-api
# Your API is now available at http://localhost:8000pm2 is a production process manager for Node.js applications, but it can also be used to manage R processes. While not specifically designed for R, pm2 provides useful features like automatic restarts, logging, and startup scripts. This is a good option if you already have Node.js infrastructure or prefer a cross-platform process manager.
Install pm2 (requires Node.js):
npm install -g pm2First, create a wrapper script that pm2 can execute. Create a file called run-api.R:
#!/usr/bin/env Rscript
# Load the API and run it
plumber2::api("_server.yml") |>
plumber2::api_run(host = "0.0.0.0", port = 8000)Make it executable:
chmod +x run-api.RStart your API using pm2:
pm2 start run-api.R --interpreter="Rscript" --name="my-plumber2-api"pm2 provides several commands for managing your application:
# View status of all applications
pm2 status
# View logs (combined stdout and stderr)
pm2 logs my-plumber2-api
# View only error logs
pm2 logs my-plumber2-api --err
# Restart the application
pm2 restart my-plumber2-api
# Stop the application
pm2 stop my-plumber2-api
# Delete the application from pm2
pm2 delete my-plumber2-apipm2 can run multiple instances of your API for load balancing:
# Start 4 instances of your API
pm2 start run-api.R --interpreter="Rscript" --name="my-api" -i 4
# Or use max (number of CPU cores)
pm2 start run-api.R --interpreter="Rscript" --name="my-api" -i maxNote: Each instance needs to run on a different port, so you’ll need to either: 1. Use a load balancer (like nginx) in front of pm2 instances 2. Modify your script to accept port as an argument
Here’s an improved run-api.R that accepts a port argument:
#!/usr/bin/env Rscript
# Get port from command line argument or environment variable
args <- commandArgs(trailingOnly = TRUE)
port <- if (length(args) > 0) {
as.integer(args[1])
} else {
as.integer(Sys.getenv("PORT", "8000"))
}
# Load the API and run it
plumber2::api("_server.yml") |>
plumber2::api_run(host = "0.0.0.0", port = port)Configure pm2 to start your API automatically when the server boots:
# Save the current pm2 process list
pm2 save
# Generate and configure startup script
pm2 startup
# This will output a command like:
# sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u youruser --hp /home/youruser
# Run that command to enable startup on bootFor more complex configurations, create a ecosystem.config.js file:
module.exports = {
apps: [{
name: 'plumber2-api',
script: './run-api.R',
interpreter: 'Rscript',
instances: 4,
exec_mode: 'cluster',
env: {
PORT: 8000,
NODE_ENV: 'production'
},
error_file: './logs/err.log',
out_file: './logs/out.log',
log_date_format: 'YYYY-MM-DD HH:mm Z',
merge_logs: true
}]
};Start it with:
pm2 start ecosystem.config.jspm2 provides built-in monitoring:
# Real-time dashboard
pm2 monit
# View resource usage
pm2 listLogs are automatically rotated and can be configured with pm2-logrotate:
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7systemd is the standard service manager for modern Linux distributions (Ubuntu 16.04+, CentOS 7+, Debian 8+, etc.). It provides robust process management without requiring Docker or Node.js. This is a good option for Linux servers where you want native system integration and simple process management.
First, create a service file at /etc/systemd/system/plumber2-api.service:
[Unit]
Description=Plumber2 API Service
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/plumber2-api
Environment="PATH=/usr/bin:/usr/local/bin"
# Run the API using _server.yml
ExecStart=/usr/bin/Rscript -e "plumber2::api('_server.yml') |> plumber2::api_run(host='0.0.0.0', port=8000)"
# Restart policy
Restart=always
RestartSec=10
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=plumber2-api
[Install]
WantedBy=multi-user.targetPlace your API files in the working directory specified in the service file:
# Create directory
sudo mkdir -p /var/www/plumber2-api
# Copy your API files
sudo cp -r /path/to/your/api/* /var/www/plumber2-api/
# Set ownership
sudo chown -R www-data:www-data /var/www/plumber2-apiEnable and start your service:
# Reload systemd to recognize the new service
sudo systemctl daemon-reload
# Enable the service to start on boot
sudo systemctl enable plumber2-api
# Start the service
sudo systemctl start plumber2-api
# Check status
sudo systemctl status plumber2-apisystemd logs can be viewed with journalctl:
# View all logs for your service
sudo journalctl -u plumber2-api
# Follow logs in real-time
sudo journalctl -u plumber2-api -f
# View logs from the last hour
sudo journalctl -u plumber2-api --since "1 hour ago"
# View logs with specific priority (error and above)
sudo journalctl -u plumber2-api -p err# Start the service
sudo systemctl start plumber2-api
# Stop the service
sudo systemctl stop plumber2-api
# Restart the service
sudo systemctl restart plumber2-api
# Reload configuration without stopping
sudo systemctl reload plumber2-api
# Check if service is running
sudo systemctl is-active plumber2-api
# Check if service is enabled on boot
sudo systemctl is-enabled plumber2-api
# Disable auto-start on boot
sudo systemctl disable plumber2-api