Deploying Shiny Apps
Publish apps to shinyapps.io, Shiny Server, and Dockerized environments.
Deploying Shiny Apps is a free R Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the R Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Deployment Options Overview
Shiny apps can be deployed on several platforms depending on budget, traffic expectations, and control requirements:
- shinyapps.io: Managed cloud hosting by Posit — easiest, free tier available.
- Shiny Server (open source): Self-hosted on Linux, free.
- Posit Connect: Commercial, enterprise-grade, supports R Markdown and Plumber too.
- Docker + rocker/shiny: Maximum portability and control.
# Summary of deployment options and their trade-offs:
# shinyapps.io: managed, auto-scaling, per-instance pricing
# Shiny Server: self-hosted, unlimited users, needs sysadmin
# Posit Connect: enterprise SSO, scheduling, REST APIs
# Docker: any cloud provider, reproducible environments
cat('Choose based on: scale, budget, infra control needs')app.R Structure for Deployment
A deployable Shiny app must have either a single app.R file (containing both ui and server) or two files ui.R and server.R. All package dependencies must be declared with library() at the top. Data files must be in the same directory or accessible relative to the app.
# Minimal app.R for deployment
library(shiny)
library(ggplot2)
library(dplyr)
# Load data relative to app directory
data <- readRDS('data/processed_data.rds')
ui <- fluidPage(
titlePanel('My Deployed App'),
sidebarLayout(
sidebarPanel(selectInput('var', 'Variable:', choices = names(data))),
mainPanel(plotOutput('main_plot'))
)
)
server <- function(input, output, session) {
output$main_plot <- renderPlot({
ggplot(data, aes_string(x = input$var)) + geom_histogram()
})
}
shinyApp(ui, server)rsconnect::deployApp()
The rsconnect package handles deployment to shinyapps.io and Posit Connect. First, configure your account credentials, then call rsconnect::deployApp() from the app directory. It automatically bundles all R files, data, and a manifest.
library(rsconnect)
# One-time setup: link your shinyapps.io account
# rsconnect::setAccountInfo(
# name = 'your_account_name',
# token = 'YOUR_TOKEN',
# secret = 'YOUR_SECRET'
# )
# Deploy the app in the current directory
rsconnect::deployApp(
appDir = '.', # or 'path/to/app'
appName = 'my_analysis_app',
forceUpdate = TRUE
)shinyapps.io Setup
shinyapps.io is the quickest path to a public URL. Create a free account at shinyapps.io, copy your token and secret from the dashboard, and call setAccountInfo(). The free tier allows 5 apps with 25 active hours per month.
library(rsconnect)
# Copy credentials from shinyapps.io -> Account -> Tokens
rsconnect::setAccountInfo(
name = 'your_username',
token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
secret = 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'
)
# Verify the connection
rsconnect::accountInfo()
# List currently deployed apps
rsconnect::applications()Shiny Server Open Source
Shiny Server runs on Linux (Ubuntu/RHEL) and serves Shiny apps from the /srv/shiny-server/ directory. Each subdirectory becomes an app at http://server:3838/subdirname/. It is free and supports unlimited concurrent users (within server capacity).
# Install Shiny Server on Ubuntu:
# sudo apt-get install r-base
# sudo R -e 'install.packages(c("shiny", "rmarkdown"))'
# wget https://download3.rstudio.org/ubuntu-20.04/x86_64/shiny-server-1.5.21.1012-amd64.deb
# sudo dpkg -i shiny-server-1.5.21.1012-amd64.deb
# Deploy an app:
# sudo cp -r /path/to/myapp /srv/shiny-server/myapp
# Access at: http://your-server:3838/myapp
# Config file: /etc/shiny-server/shiny-server.conf
cat('Shiny Server config controls ports, auth, and app paths')Docker with rocker/shiny
The rocker/shiny Docker image provides a pre-configured Shiny Server. Mount your app directory into the container to serve it immediately. Docker ensures reproducibility — the same image runs identically on any host.
# Dockerfile for a custom Shiny app
# FROM rocker/shiny:4.4.0
#
# # Install R package dependencies
# RUN R -e 'install.packages(c("ggplot2","dplyr","DT"))'
#
# # Copy app files
# COPY myapp/ /srv/shiny-server/myapp/
#
# EXPOSE 3838
# Build and run:
# docker build -t my-shiny-app .
# docker run -p 3838:3838 my-shiny-app
# Access at: http://localhost:3838/myapprenv for Reproducible Dependencies
renv creates a project-level package library with a lockfile (renv.lock) recording exact package versions. When deploying, this lockfile ensures the server or Docker image installs exactly the same package versions used during development.
library(renv)
# Initialise renv in the project (creates renv.lock)
renv::init()
# After installing/updating packages, record the state
renv::snapshot()
# On the server: restore exact versions from lockfile
renv::restore()
# Check lockfile for a specific package version
renv::dependencies() # lists all used packagesEnvironment Variables for Configuration
Never hardcode API keys, database passwords, or environment-specific settings in app code. Use environment variables read with Sys.getenv('VAR_NAME', unset = NA). Set them in .Renviron locally and in the hosting platform's environment configuration for production.
# In .Renviron (never commit this file):
# DB_HOST=prod-db.example.com
# DB_PASSWORD=secret123
# API_KEY=key_abc
# In app.R:
db_host <- Sys.getenv('DB_HOST', unset = 'localhost')
db_pass <- Sys.getenv('DB_PASSWORD', unset = '')
api_key <- Sys.getenv('API_KEY', unset = '')
if (api_key == '') {
warning('API_KEY not set — some features disabled')
}Posit Connect Overview
Posit Connect is the enterprise platform for deploying Shiny apps, R Markdown documents, Plumber APIs, and more. It supports:
- Single Sign-On (SSO) via LDAP or SAML
- Scheduled execution of R Markdown reports
- Access control at the app level
- Automatic scaling and load balancing
Deployment uses the same rsconnect::deployApp() workflow as shinyapps.io.
library(rsconnect)
# Configure connection to Posit Connect server
rsconnect::addServer(
url = 'https://connect.yourcompany.com',
name = 'my_connect_server'
)
# Authenticate with API key
rsconnect::connectApiUser(
account = 'jsmith',
server = 'my_connect_server',
apiKey = Sys.getenv('CONNECT_API_KEY')
)
# Deploy
rsconnect::deployApp(server = 'my_connect_server')Pre-Deployment Checklist
Before deploying a Shiny app to production, verify:
- All file paths use relative paths, not absolute paths.
- Sensitive data (API keys, passwords) are read from environment variables.
- Package dependencies are explicit with
library()orrenv.lock. - Large data files are loaded once at the top of server.R (not inside reactive).
- Error messages don't expose internal information to end users.
# Production-hardened app.R patterns
# Load data once at startup (not per-session)
app_data <- readRDS('data/processed.rds') # outside server()
server <- function(input, output, session) {
# Reference app_data directly — already in memory
output$plot <- renderPlot({
subset_data <- app_data[app_data$group == input$grp, ]
plot(subset_data$x, subset_data$y)
})
# Sanitise error messages
tryCatch({
risky_operation()
}, error = function(e) {
showNotification('An error occurred.', type = 'error')
# log the real error internally, not to user
message('Error: ', conditionMessage(e))
})
}Scaling Considerations
Shiny apps are session-based: each user gets their own R process (or worker). Scaling strategies:
- Load pre-computed data at startup instead of computing per session.
- Use reactive caching with
bindCache()for expensive computations shared across sessions. - Multiple workers: Shiny Server Pro and Posit Connect spawn parallel R processes.
- Database backends: use DBI/pool for concurrent DB access.
library(shiny)
library(pool)
# Create a database connection pool at startup
# (shared across all sessions)
pool <- dbPool(
drv = RPostgres::Postgres(),
host = Sys.getenv('DB_HOST'),
dbname = 'analytics',
user = Sys.getenv('DB_USER'),
password = Sys.getenv('DB_PASSWORD'),
minSize = 2,
maxSize = 10
)
onStop(function() { poolClose(pool) })
server <- function(input, output, session) {
output$tbl <- renderTable({
dbGetQuery(pool, 'SELECT * FROM metrics LIMIT 100')
})
}Quick Check
What is the recommended way to handle API keys and database passwords in a deployed Shiny app?
Deploying Shiny Apps Recap
Key takeaways from Deploying Shiny Apps:
- Deployment options: shinyapps.io (easiest), Shiny Server (self-hosted), Posit Connect (enterprise), Docker.
- app.R must declare all dependencies with
library(); userenv::snapshot()for reproducible versions. rsconnect::deployApp()bundles and pushes to shinyapps.io or Posit Connect.- Docker with
rocker/shinyprovides maximum portability. - Use
Sys.getenv()for credentials — never hardcode secrets. - Load large datasets at startup (outside
server()) to share across sessions. - Use connection pools (
poolpackage) for concurrent database access.
# Minimal production-ready structure
# myapp/
# app.R <- ui + server + shinyApp()
# data/ <- processed datasets
# R/ <- modules and helpers
# renv.lock <- package version snapshot
# .Renviron <- local env vars (gitignored)
# Deploy:
library(rsconnect)
rsconnect::deployApp(
appDir = 'myapp',
appName = 'my_production_app'
)Frequently asked questions
Is the “Deploying Shiny Apps” lesson free?
Yes — the full text of “Deploying Shiny Apps” is free to read here on the web, and the R Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the R Academy course, upgrade to CoddyKit PRO.
What will I learn in “Deploying Shiny Apps”?
Publish apps to shinyapps.io, Shiny Server, and Dockerized environments. You practise R Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start R Academy?
No prior experience is required. R Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Deploying Shiny Apps” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this R Academy lesson?
Yes. Every R Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.