A lightweight, WebSocket-enabled proxy server for hosting multiple Shiny applications with automatic health monitoring, session management, and resource cleanup.
π― Perfect for: - Hosting multiple Shiny apps behind a single server - Development environments with multiple projects - Small-scale production deployments - R Markdown and Quarto dashboard hosting
# Install from GitHub
remotes::install_github("lab1702/tinyshinyserver")# Clone and install locally
git clone https://github.com/lab1702/tinyshinyserver.git
cd tinyshinyserver
Rscript -e "devtools::install('.')"The package automatically installs required R dependencies:
Core: shiny, callr, jsonlite, later, httr, digest, httpuv, websocket, openssl Async: future Docs: rmarkdown, quarto Utils: logger
Optional (for examples): DT, plotly, dplyr, flexdashboard
# Load the package
library(tinyshinyserver)# Copy included example apps to your directory
examples_path <- system.file("examples", package = "tinyshinyserver")
file.copy(examples_path, ".", recursive = TRUE)# Start with the example configuration
start_tss(config = "examples/config.json")π Main Interface: http://localhost:3838
βοΈ Management Dashboard: http://localhost:3839
π± Individual Apps:
http://localhost:3838/proxy/{app_name}/
# Package overview and getting started
?tinyshinyserver
# Main function help
?start_tss
# Configuration format reference
?config-formatruntime: shinyserver: shinyThe server uses a JSON configuration file. Hereβs a minimal example:
{
"apps": [
{
"name": "sales",
"path": "./examples/sales",
"resident": true
}
],
"starting_port": 3001,
"proxy_port": 3838,
"proxy_host": "127.0.0.1",
"management_port": 3839,
"log_dir": "./logs"
}π See ?config-format for complete configuration
reference
Recommended: Use the management interface at http://localhost:3839 and click βShutdown Serverβ.
Alternative methods: - Press Ctrl-C in
the R console - API call:
curl -X POST http://localhost:3839/api/shutdown
β Graceful shutdown closes all connections and cleans up resources.
The package includes four example applications:
| App | Type | Description |
|---|---|---|
| sales | Single-file Shiny | Simple dashboard with sample data |
| inventory | Multi-file Shiny | Interactive tables (ui.R + server.R) |
| reports | R Markdown | Flexdashboard with runtime: shiny |
| dashboard | Quarto | Modern dashboard with server: shiny |
Example configuration (from examples/config.json):
{
"apps": [
{
"name": "sales",
"path": "./examples/sales",
"resident": true
},
{
"name": "inventory",
"path": "./examples/inventory"
},
{
"name": "reports",
"path": "./examples/reports",
"resident": false
},
{
"name": "dashboard",
"path": "./examples/dashboard",
"resident": true
}
],
"starting_port": 3001,
"proxy_port": 3838,
"management_port": 3839,
"log_dir": "./logs"
}Apps are automatically assigned ports starting from
starting_port: - sales β port 3001
- inventory β port 3002
- reports β port 3003
- dashboard β port 3004
The system skips reserved ports (proxy_port,
management_port) automatically.
The server supports two application life cycle modes controlled by
the resident configuration option:
"resident": true)"resident": false, default)Example Configuration:
{
"apps": [
{
"name": "critical-dashboard",
"path": "./apps/dashboard",
"resident": true // Always running for immediate access
},
{
"name": "occasional-report",
"path": "./apps/reports",
"resident": false // Starts only when needed
},
{
"name": "dev-prototype",
"path": "./apps/prototype"
// "resident" defaults to false
}
]
}| Option | Description | Default |
|---|---|---|
apps |
Array of Shiny applications to host | Required |
apps[].name |
Application identifier for URLs | Required |
apps[].path |
Relative path to app directory | Required |
apps[].resident |
Keep app running continuously (true) or start on-demand (false) | false |
starting_port |
Starting port for auto-assignment | Required |
log_dir |
Directory for log files | Required |
proxy_port |
Port for the proxy server | 3838 |
proxy_host |
Host interface for proxy server (localhost, 127.0.0.1, 0.0.0.0, ::1, ::) | β127.0.0.1β |
management_port |
Port for the management interface | 3839 |
restart_delay |
Seconds to wait before restarting failed apps | 5 |
health_check_interval |
Seconds between health checks | 10 |
The proxy_host option controls which network interface
the proxy server binds to:
"localhost" or "127.0.0.1" - Binds to
localhost only (default, most secure)"0.0.0.0" - Binds to all network interfaces (allows
external access)"::1" - IPv6 localhost"::" - All IPv6 interfacesβ οΈ Security Note: Using "0.0.0.0" makes
the server accessible from external networks. Only use this if you
understand the security implications and have proper firewall rules in
place.
For production deployments requiring SSL/TLS and authentication, Caddy Server provides a simple solution:
# Caddyfile
myapp.example.com {
reverse_proxy localhost:3838
basicauth {
username password_hash
}
}
manage.myapp.example.com {
reverse_proxy localhost:3839
basicauth {
admin admin_password_hash
}
}
This configuration automatically handles SSL certificates via Letβs Encrypt and adds HTTP basic authentication.
Important: When using Caddy, keep
proxy_host set to "localhost" or
"127.0.0.1" in your config.json to ensure the
server only accepts connections from Caddy, not directly from external
clients.
The landing page at http://localhost:3838 provides an overview of all hosted applications with real-time status information.
The landing page uses the same status API as the management interface, ensuring consistency between all monitoring views.
The management interface provides a professional web-based dashboard for monitoring and controlling your Shiny server.
Visit http://localhost:3839 to access the management dashboard.
π Security: The management interface is restricted to localhost only for security.
The management interface exposes a REST API for programmatic access:
| Endpoint | Method | Description |
|---|---|---|
/api/status |
GET | System overview (apps, connections) |
/api/apps |
GET | Detailed application status |
/api/connections |
GET | Active connection details |
/api/apps/{name}/restart |
POST | Restart specific application |
/api/shutdown |
POST | Graceful server shutdown |
Example usage:
# Get system status
curl http://localhost:3839/api/status
# Restart the sales app
curl -X POST http://localhost:3839/api/apps/sales/restart
# Shutdown server
curl -X POST http://localhost:3839/api/shutdownThe management interface automatically adapts to your systemβs theme
preference: - Light Mode: Clean, professional
appearance for daytime use - Dark Mode: Comfortable
viewing for low-light environments - Automatic
Detection: Uses CSS prefers-color-scheme media
query
apps/myapp/
βββ app.R # Single-file Shiny app
βββ [other files]
apps/myapp/
βββ ui.R # UI definition
βββ server.R # Server logic
βββ [other files]
apps/myapp/
βββ report.Rmd # R Markdown with runtime: shiny
βββ [other files]
apps/myapp/
βββ dashboard.qmd # Quarto document with server: shiny
βββ [other files]
The project includes four example applications demonstrating different application types:
examples/sales/)examples/inventory/)examples/reports/)runtime: shinyexamples/dashboard/)server: shinyformat: dashboardThe server includes automatic memory management features:
Cleanup runs automatically every 5 minutes and logs activity for monitoring.
logs/server.log - Main server logslogs/{app_name}_output.log - Per-app stdout logslogs/{app_name}_error.log - Per-app stderr logsINFO - Normal operationsWARN - Warning conditions (e.g., queue limits
reached)ERROR - Error conditions requiring attentionFor development, you can run apps directly:
# Standard Shiny app
R -e "shiny::runApp('apps/sales', port = 3001)"
# R Markdown app
R -e "rmarkdown::run('apps/reports/report.Rmd', shiny_args = list(port = 3003, host = '127.0.0.1'))"
# Quarto app
R -e "quarto::quarto_serve('apps/dashboard/dashboard.qmd', port = 3004, host = '127.0.0.1')"apps/{app_name}/config.json (only name and
path required)The server will automatically assign the next available port to your new application.
Proxy Server: - Health check:
GET /health - Returns {"status": "healthy"} -
Apps status: GET /api/apps - Returns detailed application
status (used by landing page)
Management Interface: - System status:
GET /api/status - Returns overall system health - Apps
status: GET /api/apps - Returns detailed application status
- Connections: GET /api/connections - Returns active
connection details
tinyshinyserver/
βββ R/ # R source code
β βββ start_tss.R # Main exported function
β βββ config.R # Configuration management
β βββ handlers.R # HTTP request routing
β βββ process_manager.R # Application lifecycle
β βββ ... # Other core modules
βββ inst/
β βββ examples/ # Example apps & config
β β βββ sales/ # Simple Shiny app
β β βββ inventory/ # Multi-file Shiny app
β β βββ reports/ # R Markdown dashboard
β β βββ dashboard/ # Quarto dashboard
β β βββ config.json # Example configuration
β βββ templates/ # HTML templates & CSS
βββ man/ # Documentation (.Rd files)
βββ DESCRIPTION # Package metadata
βββ NAMESPACE # Exported functions
The server uses a multi-process architecture:
βββββββββββββββββββββββ
β Management Server β
β (Port 3839) β
β β
β β’ System Status β
β β’ App Control β
β β’ Connection Info β
β β’ Graceful Shutdownβ
βββββββββββββββββββββββ
βββββββββββββββββββββββ
β Proxy Server β
β (Port 3838) β
β β
β ββWebSocketβββββββββ€
β β Handler β
β β β
β β ββHTTPβββββββββββ€
β β β Handler β
ββββΌβββΌββββββββββββββββ
β β
β βββ HTTP Requests βββ
β β
βββ WebSocket ββββββββββΌββββ
Messages β β
βΌ βΌ
ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββ
β Sales β βInventory β β Reports β βDashboard β
β(Port β β(Port β β(Port β β(Port β
β 3001) β β 3002) β β 3003) β β 3004) β
ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββ
netstat -an | findstr :3838logs/{app_name}_error.log?start_tss for configuration help# Check server logs (created after starting)
list.files("logs", pattern = "\\.(log|txt)$")
# Monitor main server log
tail -f logs/server.log # Linux/macOS
Get-Content logs/server.log -Wait # PowerShellβ οΈ Important: This server is designed for development and internal use.
For production deployment, consider: - Adding authentication (see Caddy reverse proxy example) - SSL/TLS encryption for external access - Firewall rules and network segmentation - Regular security updates - Rate limiting on management endpoints
Built-in security features: - Management interface restricted to localhost only - Input validation for configuration files - Process isolation between applications
Contributions are welcome! Please see our contribution guidelines:
git checkout -b feature/amazing-featuredevtools::check()# Clone and setup for development
git clone https://github.com/lab1702/tinyshinyserver.git
cd tinyshinyserver
# Install with dependencies
devtools::install_deps()
devtools::load_all()
# Run checks
devtools::check()This project is licensed under the MIT License - see the LICENSE file for details.
?tinyshinyserver, ?start_tss,
?config-formatBuilt with β€οΈ for the R and Shiny community