Building Production Shiny Dashboards with golem & Docker
From prototype to production — structuring Shiny apps with golem, containerizing with Docker, and deploying to ShinyProxy or Posit Connect.
Why Most Shiny Apps Die in Development
The typical Shiny app starts as app.R — a single file with UI and server
mashed together. It works on your laptop. It breaks everywhere else. The problem isn't
Shiny — it's the lack of engineering structure.
Enter golem
golem is an opinionated framework that treats your Shiny app as an
R package. That means you get:
- DESCRIPTION file for dependency management
- Namespaced modules — each UI/server piece in its own file
- testthat integration for unit testing
- inst/ folder for configs, CSS, JS assets
# Scaffold a new golem app
golem::create_golem("myDashboard")
# Add a module
golem::add_module(name = "sales_overview")
# This creates:
# R/mod_sales_overview.R (UI + server functions)
Structuring Modules
Each module is a self-contained unit. The mod_sales_overview_ui() function
returns the UI, and mod_sales_overview_server() handles the logic. Modules
can call other modules — you compose your app like building blocks.
# R/mod_sales_overview.R
mod_sales_overview_ui <- function(id) {
ns <- NS(id)
tagList(
selectInput(ns("region"), "Region", choices = NULL),
plotOutput(ns("trend_plot"))
)
}
mod_sales_overview_server <- function(id, shared_data) {
moduleServer(id, function(input, output, session) {
output$trend_plot <- renderPlot({
shared_data() %>%
filter(region == input$region) %>%
ggplot(aes(date, sales)) + geom_line()
})
})
}
Dockerizing
golem ships with golem::add_dockerfile() that generates a production-ready
Dockerfile. The key trick: use renv for reproducible package snapshots.
FROM rocker/r-ver:4.3.2
RUN apt-get update && apt-get install -y libcurl4-openssl-dev libssl-dev
COPY renv.lock /app/renv.lock
WORKDIR /app
RUN Rscript -e "install.packages('renv'); renv::restore()"
COPY . /app
EXPOSE 3838
CMD ["R", "-e", "myDashboard::run_app()"]
Deployment Options
Once containerized, you have options:
- ShinyProxy — open-source, spins up a Docker container per user
- Posit Connect — enterprise option with auth, scheduling, and API publishing
- AWS ECS / GCP Cloud Run — serverless container hosting
Takeaway
Treat your Shiny app like a real software project. Use golem for structure, renv for reproducibility, Docker for portability, and CI/CD for safety. Your future self will thank you.