flowchart TD A[User Request] --> B[ShinyProxy Server] B --> C{Authentication} C -->|Authenticated| D[Container Manager] C -->|Failed| E[Login Page] D --> F[Docker Engine] F --> G[Shiny App Container 1] F --> H[Shiny App Container 2] F --> I[Shiny App Container N] J[Load Balancer] --> B K[Authentication Provider] --> C L[Container Registry] --> F style A fill:#e1f5fe style B fill:#f3e5f5 style D fill:#e8f5e8 style F fill:#fff3e0
Key Takeaways
- Enterprise-Grade Architecture: ShinyProxy provides multi-tenant container orchestration that scales from departmental tools to organization-wide platforms
- Security and Authentication: Built-in integration with enterprise authentication systems (LDAP, OAuth, SAML) ensures secure access control
- Resource Isolation: Each Shiny application runs in its own Docker container, providing complete isolation and resource management
- Production-Ready Features: Load balancing, health monitoring, and automatic container lifecycle management for reliable production deployments
- Cost-Effective Scaling: Dynamic container allocation optimizes resource usage while supporting hundreds of concurrent users
Introduction
ShinyProxy represents the evolution of enterprise Shiny deployment, bridging the gap between basic Shiny Server and complex cloud orchestration platforms. As organizations increasingly need sophisticated analytical applications that serve multiple users securely and reliably, ShinyProxy has emerged as the preferred solution for enterprise container orchestration of Shiny applications.
Unlike traditional deployment approaches that struggle with multi-tenancy and resource isolation, ShinyProxy leverages Docker containers to provide complete application isolation while maintaining the simplicity that makes Shiny development accessible. This tutorial guides you through implementing a production-ready ShinyProxy deployment that meets enterprise requirements for security, scalability, and reliability.
Whether you’re deploying analytical applications for internal teams, client-facing dashboards for external stakeholders, or building a comprehensive analytics platform for your organization, ShinyProxy provides the enterprise-grade foundation that transforms prototype applications into production-ready analytical solutions.
Understanding ShinyProxy Architecture
ShinyProxy’s container-first architecture fundamentally changes how Shiny applications are deployed and managed in enterprise environments:
Core Architecture Components
ShinyProxy Server
Acts as the orchestration layer that manages container lifecycle, user authentication, and request routing. Unlike traditional web servers, ShinyProxy dynamically creates and destroys containers based on user demand and session management policies.
Container Manager
Interfaces with Docker Engine to create, monitor, and destroy application containers. Each user session gets its own dedicated container, ensuring complete resource isolation and eliminating the cross-user contamination issues common in shared server environments.
Authentication Integration
Native support for enterprise authentication systems including LDAP, Active Directory, OAuth providers, and SAML. This integration ensures that Shiny applications inherit organizational access control policies without requiring custom authentication implementation.
Resource Management
Dynamic resource allocation with configurable memory limits, CPU constraints, and automatic scaling policies. Containers are created on-demand and destroyed when sessions end, optimizing resource utilization while maintaining performance.
Prerequisites and Environment Setup
System Requirements
Server Infrastructure:
- Operating System: Linux (Ubuntu 20.04+ or RHEL 8+ recommended)
- Memory: Minimum 8GB RAM (16GB+ recommended for production)
- CPU: Minimum 4 cores (8+ cores recommended for concurrent users)
- Storage: 50GB+ available disk space for containers and logs
- Network: Stable internet connection for Docker image downloads
Software Dependencies:
- Docker Engine: Version 20.10+
- Java Runtime: OpenJDK 11 or higher
- Reverse Proxy: Nginx or Apache (recommended for production)
- SSL Certificate: For HTTPS termination (required for production)
Docker Environment Configuration
Install and configure Docker with appropriate permissions and resource limits:
# Install Docker Engine (Ubuntu/Debian)
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# Add system user to docker group
sudo usermod -aG docker $USER
# Configure Docker daemon for production
sudo tee /etc/docker/daemon.json <<EOF
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
]
}
EOF
# Restart Docker service
sudo systemctl restart docker
sudo systemctl enable docker
Java Runtime Installation
ShinyProxy requires Java 11 or higher for optimal performance:
# Install OpenJDK 11 (Ubuntu/Debian)
sudo apt update
sudo apt install openjdk-11-jdk
# Verify Java installation
java -version
javac -version
# Set JAVA_HOME environment variable
echo 'export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64' >> ~/.bashrc
source ~/.bashrc
ShinyProxy Installation and Basic Configuration
Download and Initial Setup
# Create ShinyProxy directory structure
sudo mkdir -p /opt/shinyproxy
sudo mkdir -p /opt/shinyproxy/logs
sudo mkdir -p /opt/shinyproxy/config
# Download latest ShinyProxy release
cd /opt/shinyproxy
sudo wget https://www.shinyproxy.io/downloads/shinyproxy-3.0.2.jar
# Create system user for ShinyProxy
sudo useradd -r -s /bin/false shinyproxy
sudo chown -R shinyproxy:shinyproxy /opt/shinyproxy
Basic Configuration File
Create the foundational configuration file that defines application settings and container specifications:
# /opt/shinyproxy/config/application.yml
proxy:
title: Enterprise Analytics Platform
logo-url: file:///opt/shinyproxy/config/logo.png
landingpage: /
heartbeat-rate: 10000
heartbeat-timeout: 60000
port: 8080
bind-address: 0.0.0.0
authentication: simple
admin-groups: [admin]
users:
- name: admin
password: changeThisPassword123!
groups: [admin]
- name: analyst
password: analystPassword456!
groups: [analyst]
# Container runtime configuration
container-backend: docker
docker:
url: http://localhost:2375
port-range-start: 20000
port-range-max: 25000
specs:
- id: data-explorer
display-name: Interactive Data Explorer
description: Advanced data exploration and visualization tool
container-cmd: ["R", "-e", "shiny::runApp('/srv/shiny-server/data-explorer', host='0.0.0.0', port=3838)"]
container-image: your-registry/shiny-data-explorer:latest
container-memory: "2GB"
container-cpu-limit: 2
container-privileged: false
access-groups: [analyst, admin]
- id: financial-dashboard
display-name: Financial Analytics Dashboard
description: Real-time financial performance monitoring
container-cmd: ["R", "-e", "shiny::runApp('/srv/shiny-server/financial-dashboard', host='0.0.0.0', port=3838)"]
container-image: your-registry/shiny-financial:latest
container-memory: "4GB"
container-cpu-limit: 2
container-privileged: false
access-groups: [admin]
logging:
level:
root: INFO
eu.openanalytics: DEBUG
file:
name: /opt/shinyproxy/logs/shinyproxy.log
Systemd Service Configuration
Create a systemd service for production deployment management:
# /etc/systemd/system/shinyproxy.service
[Unit]
Description=ShinyProxy
After=docker.service
Requires=docker.service
[Service]
Type=simple
User=shinyproxy
ExecStart=/usr/bin/java -jar /opt/shinyproxy/shinyproxy-3.0.2.jar
WorkingDirectory=/opt/shinyproxy/config
Restart=always
RestartSec=10
# Resource limits
LimitNOFILE=65536
Environment=JAVA_OPTS=-Xmx2g
# Security settings
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/opt/shinyproxy/logs
[Install]
WantedBy=multi-user.target
Enable and start the ShinyProxy service:
# Reload systemd configuration
sudo systemctl daemon-reload
# Enable ShinyProxy service
sudo systemctl enable shinyproxy
# Start ShinyProxy service
sudo systemctl start shinyproxy
# Check service status
sudo systemctl status shinyproxy
# Monitor logs
sudo journalctl -u shinyproxy -f
Container Image Preparation
Creating Optimized Shiny Container Images
Effective ShinyProxy deployment requires well-designed container images that balance functionality with security and performance:
# Dockerfile for Shiny Data Explorer Application
FROM rocker/shiny:4.3.2
# Install system dependencies
RUN apt-get update && apt-get install -y \
\
libcurl4-gnutls-dev \
libssl-dev \
libxml2-dev \
libcairo2-dev \
libgit2-dev \
default-libmysqlclient-dev \
libpq-dev \
libsasl2-dev \
libldap2-dev && rm -rf /var/lib/apt/lists/*
# Install R packages
RUN R -e "install.packages(c('shiny', 'shinydashboard', 'DT', 'plotly', 'dplyr', 'ggplot2', 'readr', 'RMySQL', 'RPostgreSQL'), repos='https://cran.rstudio.com/')"
# Copy application files
COPY shiny-apps/data-explorer/ /srv/shiny-server/data-explorer/
COPY config/Rprofile.site /usr/local/lib/R/etc/
# Set proper permissions
RUN chown -R shiny:shiny /srv/shiny-server/
# Configure security settings
USER shiny
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3838/ || exit 1
# Expose port
EXPOSE 3838
# Start command (will be overridden by ShinyProxy)
CMD ["R", "-e", "shiny::runApp('/srv/shiny-server/data-explorer', host='0.0.0.0', port=3838)"]
Multi-Stage Build Optimization
For production environments, implement multi-stage builds to reduce image size and security footprint:
# Multi-stage Dockerfile for optimized production images
FROM rocker/r-base:4.3.2 as builder
# Install build dependencies
RUN apt-get update && apt-get install -y \
\
libcurl4-gnutls-dev \
libssl-dev \
libxml2-dev \
build-essential && rm -rf /var/lib/apt/lists/*
# Install R packages
RUN R -e "install.packages(c('shiny', 'shinydashboard', 'DT', 'plotly', 'dplyr', 'ggplot2'), repos='https://cran.rstudio.com/')"
# Production stage
FROM rocker/shiny:4.3.2
# Copy installed packages from builder
COPY --from=builder /usr/local/lib/R/site-library /usr/local/lib/R/site-library
# Install only runtime dependencies
RUN apt-get update && apt-get install -y \
\
libcurl4-gnutls-dev \
libssl-dev \
libxml2-dev && rm -rf /var/lib/apt/lists/*
# Copy application
COPY shiny-apps/data-explorer/ /srv/shiny-server/data-explorer/
# Security configuration
RUN chown -R shiny:shiny /srv/shiny-server/ && \
chmod -R 755 /srv/shiny-server/
USER shiny
EXPOSE 3838
# Health check
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:3838/ || exit 1
Build and Registry Management
# Build optimized container image
docker build -t your-registry/shiny-data-explorer:latest .
# Tag for version management
docker tag your-registry/shiny-data-explorer:latest your-registry/shiny-data-explorer:v1.0.0
# Push to container registry
docker push your-registry/shiny-data-explorer:latest
docker push your-registry/shiny-data-explorer:v1.0.0
# Verify image
docker images | grep shiny-data-explorer
Advanced Authentication Integration
LDAP/Active Directory Integration
Enterprise environments typically require integration with existing directory services:
# LDAP Authentication Configuration
proxy:
authentication: ldap
ldap:
url: ldap://ldap.company.com:389
user-dn-pattern: uid={0},ou=users,dc=company,dc=com
user-search-filter: (uid={0})
user-search-base: ou=users,dc=company,dc=com
group-search-base: ou=groups,dc=company,dc=com
group-search-filter: (member={0})
manager-dn: cn=admin,dc=company,dc=com
manager-password: ${LDAP_MANAGER_PASSWORD}
# Map LDAP groups to ShinyProxy groups
authorization:
expression: hasRole('ROLE_USER')
admin-groups: [IT-ADMINS, DATA-SCIENCE-LEADS]
specs:
- id: executive-dashboard
display-name: Executive Dashboard
access-groups: [C-SUITE, VP-LEVEL]
# ... container configuration
- id: analyst-tools
display-name: Analyst Toolkit
access-groups: [ANALYSTS, DATA-SCIENTISTS]
# ... container configuration
OAuth2 Integration
For modern authentication workflows, integrate with OAuth2 providers:
# OAuth2 Configuration (example with Google)
proxy:
authentication: oauth2
oauth2:
client-id: ${OAUTH2_CLIENT_ID}
client-secret: ${OAUTH2_CLIENT_SECRET}
access-token-uri: https://oauth2.googleapis.com/token
user-authorization-uri: https://accounts.google.com/o/oauth2/auth
scope: openid email profile
user-info-uri: https://www.googleapis.com/oauth2/v2/userinfo
jwk-set-uri: https://www.googleapis.com/oauth2/v3/certs
username-attribute: email
# Additional OAuth2 settings
logout-url: https://accounts.google.com/logout
specs:
- id: public-analytics
display-name: Public Analytics Tool
# No access-groups restriction for OAuth2 authenticated users
SAML Integration
For enterprise SSO environments requiring SAML:
# SAML Configuration
proxy:
authentication: saml
saml:
app-entity-id: shinyproxy-analytics
idp-entity-id: https://sso.company.com/entity-id
idp-single-sign-on-service-url: https://sso.company.com/sso/saml
idp-single-logout-service-url: https://sso.company.com/slo/saml
idp-x509-certificate: |
-----BEGIN CERTIFICATE-----
[Certificate content]
-----END CERTIFICATE-----
# Attribute mapping
username-attribute: NameID
groups-attribute: memberOf
logout-url: https://sso.company.com/logout
Production Security Configuration
SSL/TLS Implementation with Traefik
Configure HTTPS termination using Traefik reverse proxy (recommended for ShinyProxy):
# docker-compose.yml with Traefik integration
version: '3.8'
services:
traefik:
image: traefik:v3.0
command:
# API and dashboard
- "--api.dashboard=true"
- "--api.insecure=false"
# Docker provider
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
# Entry points
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
# SSL/TLS configuration
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.email=admin@company.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
# Security headers
- "--entrypoints.websecure.http.middlewares=security-headers@docker"
# Logging
- "--log.level=INFO"
- "--accesslog=true"
ports:
- "80:80"
- "443:443"
- "8080:8080" # Dashboard (secure in production)
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./letsencrypt:/letsencrypt"
labels:
# Dashboard access (secure with authentication in production)
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.company.com`)"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
- "traefik.http.routers.dashboard.service=api@internal"
# Security headers middleware
- "traefik.http.middlewares.security-headers.headers.stsSeconds=31536000"
- "traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.security-headers.headers.stsPreload=true"
- "traefik.http.middlewares.security-headers.headers.forceSTSHeader=true"
- "traefik.http.middlewares.security-headers.headers.frameDeny=true"
- "traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true"
- "traefik.http.middlewares.security-headers.headers.browserXssFilter=true"
- "traefik.http.middlewares.security-headers.headers.referrerPolicy=strict-origin-when-cross-origin"
networks:
- shinyproxy
shinyproxy:
image: openanalytics/shinyproxy:3.0.2
volumes:
- ./config:/opt/shinyproxy/config
- ./logs:/opt/shinyproxy/logs
- /var/run/docker.sock:/var/run/docker.sock
environment:
- SPRING_PROFILES_ACTIVE=prod
labels:
# Traefik configuration
- "traefik.enable=true"
# HTTP to HTTPS redirect
- "traefik.http.routers.shinyproxy-http.rule=Host(`analytics.company.com`)"
- "traefik.http.routers.shinyproxy-http.entrypoints=web"
- "traefik.http.routers.shinyproxy-http.middlewares=redirect-to-https@docker"
# HTTPS configuration
- "traefik.http.routers.shinyproxy.rule=Host(`analytics.company.com`)"
- "traefik.http.routers.shinyproxy.entrypoints=websecure"
- "traefik.http.routers.shinyproxy.tls.certresolver=letsencrypt"
- "traefik.http.routers.shinyproxy.middlewares=security-headers@docker"
# Service configuration
- "traefik.http.services.shinyproxy.loadbalancer.server.port=8080"
# WebSocket support (automatic with Traefik)
- "traefik.http.services.shinyproxy.loadbalancer.sticky.cookie=true"
# Redirect middleware
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true"
networks:
- shinyproxy
depends_on:
- traefik
networks:
shinyproxy:
external: false
For traditional server deployment without Docker Compose, use Traefik configuration file:
# /etc/traefik/traefik.toml
[global]
checkNewVersion = false
sendAnonymousUsage = false
[api]
dashboard = true
insecure = false
[entryPoints]
[entryPoints.web]
address = ":80"
[entryPoints.websecure]
address = ":443"
[certificatesResolvers]
[certificatesResolvers.letsencrypt]
[certificatesResolvers.letsencrypt.acme]
email = "admin@company.com"
storage = "/etc/traefik/acme.json"
[certificatesResolvers.letsencrypt.acme.tlsChallenge]
[http]
[http.routers]
[http.routers.shinyproxy]
rule = "Host(`analytics.company.com`)"
entryPoints = ["websecure"]
service = "shinyproxy"
[http.routers.shinyproxy.tls]
certResolver = "letsencrypt"
[http.routers.shinyproxy-http]
rule = "Host(`analytics.company.com`)"
entryPoints = ["web"]
middlewares = ["redirect-to-https"]
[http.services]
[http.services.shinyproxy]
[http.services.shinyproxy.loadBalancer]
[[http.services.shinyproxy.loadBalancer.servers]]
url = "http://localhost:8080"
[http.middlewares]
[http.middlewares.redirect-to-https]
[http.middlewares.redirect-to-https.redirectScheme]
scheme = "https"
permanent = true
[http.middlewares.security-headers]
[http.middlewares.security-headers.headers]
stsSeconds = 31536000
stsIncludeSubdomains = true
stsPreload = true
forceSTSHeader = true
frameDeny = true
contentTypeNosniff = true
browserXssFilter = true
referrerPolicy = "strict-origin-when-cross-origin"
Container Security Hardening
Implement security best practices for container runtime:
# Enhanced security configuration in application.yml
proxy:
docker:
# Use Docker socket with TLS
url: https://docker-daemon:2376
cert-path: /opt/shinyproxy/docker-certs
tls-verify: true
specs:
- id: secure-app
# Security constraints
container-privileged: false
container-memory: "2GB"
container-cpu-limit: 2
# Network isolation
container-network: shinyproxy-net
# Security options
container-security-opts:
- no-new-privileges
- apparmor:docker-default
# Read-only root filesystem
container-read-only: true
# Temporary filesystem mounts
container-tmpfs:
- /tmp
- /var/tmp
# Resource limits
container-ulimits:
- name: nofile
soft: 1024
hard: 2048
Monitoring and Logging Configuration
Application Monitoring
Implement comprehensive monitoring for production ShinyProxy deployments:
# Enhanced logging and monitoring configuration
logging:
level:
root: INFO
eu.openanalytics: DEBUG
org.springframework.security: DEBUG
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file:
name: /opt/shinyproxy/logs/shinyproxy.log
max-size: 10MB
max-history: 30
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
base-path: /actuator
endpoint:
health:
show-details: when-authorized
metrics:
export:
prometheus:
enabled: true
# Custom metrics configuration
proxy:
usage-stats-url: http://localhost:9090/metrics
container-log-path: /opt/shinyproxy/logs/containers
External Monitoring Integration
Configure integration with monitoring systems like Prometheus and Grafana:
# docker-compose.yml for monitoring stack
version: '3.8'
services:
shinyproxy:
image: openanalytics/shinyproxy:latest
ports:
- "8080:8080"
volumes:
- ./config:/opt/shinyproxy/config
- ./logs:/opt/shinyproxy/logs
- /var/run/docker.sock:/var/run/docker.sock
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana-storage:/var/lib/grafana
- ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards
- ./monitoring/grafana/datasources:/etc/grafana/provisioning/datasources
volumes:
grafana-storage:
Performance Optimization and Scaling
Resource Management Strategies
Optimize resource allocation for different application types and user loads:
# Advanced resource management configuration
proxy:
container-backend: docker
docker:
# Connection pooling
pool-size: 10
max-total-connections: 100
# Container lifecycle management
port-range-start: 20000
port-range-max: 25000
container-wait-time: 60000
container-idle-timeout: 300000
specs:
# High-performance analytics application
- id: heavy-analytics
display-name: Advanced Analytics Suite
container-memory: "8GB"
container-cpu-limit: 4
container-cpu-reservation: 2
container-memory-swap: "16GB"
# JVM optimization for R applications
container-env:
R_MAX_VSIZE: "8GB"
JAVA_OPTS: "-Xmx6g -XX:+UseG1GC"
# Lightweight dashboard application
- id: simple-dashboard
display-name: Executive Dashboard
container-memory: "1GB"
container-cpu-limit: 1
container-cpu-reservation: 0.5
# Quick startup optimization
container-env:
R_MAX_VSIZE: "1GB"
Load Balancing and High Availability
Configure ShinyProxy for high availability with multiple instances using Traefik:
# High Availability ShinyProxy with Traefik Load Balancing
version: '3.8'
services:
traefik:
image: traefik:v3.0
command:
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.email=admin@company.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
# Health checking
- "--ping=true"
- "--metrics.prometheus=true"
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./letsencrypt:/letsencrypt"
healthcheck:
test: ["CMD", "traefik", "healthcheck", "--ping"]
interval: 30s
timeout: 3s
retries: 3
networks:
- shinyproxy
# Primary ShinyProxy instance
shinyproxy-1:
image: openanalytics/shinyproxy:3.0.2
environment:
- SPRING_PROFILES_ACTIVE=prod
- SERVER_PORT=8080
volumes:
- ./config:/opt/shinyproxy/config
- ./logs:/opt/shinyproxy/logs/instance-1
- /var/run/docker.sock:/var/run/docker.sock
labels:
- "traefik.enable=true"
- "traefik.http.routers.shinyproxy.rule=Host(`analytics.company.com`)"
- "traefik.http.routers.shinyproxy.entrypoints=websecure"
- "traefik.http.routers.shinyproxy.tls.certresolver=letsencrypt"
- "traefik.http.services.shinyproxy.loadbalancer.server.port=8080"
# Health check configuration
- "traefik.http.services.shinyproxy.loadbalancer.healthcheck.path=/actuator/health"
- "traefik.http.services.shinyproxy.loadbalancer.healthcheck.interval=30s"
- "traefik.http.services.shinyproxy.loadbalancer.healthcheck.timeout=5s"
# Session stickiness for WebSocket connections
- "traefik.http.services.shinyproxy.loadbalancer.sticky.cookie=true"
- "traefik.http.services.shinyproxy.loadbalancer.sticky.cookie.name=shinyproxy-server"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
networks:
- shinyproxy
depends_on:
- traefik
# Secondary ShinyProxy instance
shinyproxy-2:
image: openanalytics/shinyproxy:3.0.2
environment:
- SPRING_PROFILES_ACTIVE=prod
- SERVER_PORT=8080
volumes:
- ./config:/opt/shinyproxy/config
- ./logs:/opt/shinyproxy/logs/instance-2
- /var/run/docker.sock:/var/run/docker.sock
labels:
- "traefik.enable=true"
- "traefik.http.services.shinyproxy.loadbalancer.server.port=8080"
# Same service name for automatic load balancing
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
networks:
- shinyproxy
depends_on:
- traefik
# Optional: Third instance for additional capacity
shinyproxy-3:
image: openanalytics/shinyproxy:3.0.2
environment:
- SPRING_PROFILES_ACTIVE=prod
- SERVER_PORT=8080
volumes:
- ./config:/opt/shinyproxy/config
- ./logs:/opt/shinyproxy/logs/instance-3
- /var/run/docker.sock:/var/run/docker.sock
labels:
- "traefik.enable=true"
- "traefik.http.services.shinyproxy.loadbalancer.server.port=8080"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
networks:
- shinyproxy
depends_on:
- traefik
networks:
shinyproxy:
driver: bridge
For production environments, add monitoring and scaling configuration:
# docker-compose.override.yml for production scaling
version: '3.8'
services:
# Prometheus for metrics collection
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--storage.tsdb.retention.time=30d'
- '--web.enable-lifecycle'
networks:
- shinyproxy
# Grafana for visualization
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana-data:/var/lib/grafana
- ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards:ro
- ./monitoring/grafana/datasources:/etc/grafana/provisioning/datasources:ro
networks:
- shinyproxy
depends_on:
- prometheus
volumes:
prometheus-data:
grafana-data:
Common Issues and Troubleshooting
Container Startup Issues
Issue 1: Containers Fail to Start
Problem: Applications fail to launch with container creation errors.
Solution:
# Check Docker daemon status
sudo systemctl status docker
# Verify ShinyProxy can communicate with Docker
docker ps
# Check container image availability
docker images | grep your-app-name
# Test container manually
docker run --rm -p 3838:3838 your-registry/shiny-app:latest
# Review ShinyProxy logs
sudo journalctl -u shinyproxy -f --lines=100
Issue 2: Memory and Resource Constraints
Problem: Applications crash due to insufficient resources or memory limits.
Solution:
# Adjust resource allocations in application.yml
specs:
- id: memory-intensive-app
container-memory: "4GB" # Increase from default
container-memory-swap: "8GB" # Allow swap usage
container-cpu-limit: 4 # Increase CPU allocation
# JVM and R memory settings
container-env:
R_MAX_VSIZE: "4GB"
JAVA_OPTS: "-Xmx3g -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
Performance and Scaling Issues
Issue 5: Slow Application Startup
Problem: Applications take too long to start, affecting user experience.
Solution:
# Optimize container startup
specs:
- id: fast-startup-app
# Pre-built, optimized container image
container-image: your-registry/optimized-shiny:latest
# Reduce startup overhead
container-cmd: ["R", "--slave", "-e", "shiny::runApp('/srv/shiny-server/app', host='0.0.0.0', port=3838)"]
# Resource reservation for faster allocation
container-cpu-reservation: 1
# Environment tuning for faster R startup
container-env:
R_COMPILE_PKGS: "0" # Skip package compilation
R_DISABLE_HTTPD: "1" # Disable R help server
# Monitor container startup times
proxy:
container-log-path: /opt/shinyproxy/logs/containers
usage-stats-url: http://localhost:9090/metrics
Issue 6: High Memory Usage and OOM Kills
Problem: Containers are killed due to out-of-memory conditions.
Solution:
# Memory optimization strategies
specs:
- id: memory-optimized-app
# Appropriate memory allocation
container-memory: "4GB"
container-memory-swap: "6GB"
# R memory management
container-env:
R_MAX_VSIZE: "3GB" # Leave headroom for system
GC_INITIAL_HEAP_SIZE: "100MB"
R_GC_MEM_GROW: "3"
# JVM tuning for R processes
container-env:
JAVA_OPTS: "-Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+UseStringDeduplication"
# Monitor memory usage
docker stats --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"
Common Questions About ShinyProxy Deployment
ShinyProxy offers unique advantages that position it between cloud platforms and traditional server deployments:
vs. shinyapps.io:
- Control: Full control over infrastructure, security, and customization vs. managed service convenience
- Cost: Potentially lower long-term costs for high-usage applications vs. predictable monthly fees
- Scalability: Manual scaling configuration vs. automatic scaling
- Maintenance: Requires infrastructure management vs. zero maintenance overhead
vs. Shiny Server (Open Source):
- Isolation: Complete container isolation vs. shared R processes
- Security: Enterprise authentication integration vs. basic access control
- Resource Management: Dynamic container allocation vs. fixed resource sharing
- Multi-tenancy: Built-in multi-tenant architecture vs. single-tenant limitations
vs. Shiny Server Pro/RStudio Connect:
- Cost: Open source (free) vs. commercial licensing fees
- Features: Container-first approach vs. traditional server management
- Flexibility: Docker ecosystem integration vs. proprietary solutions
Best Use Cases for ShinyProxy:
- Organizations requiring container-based deployment
- Multi-tenant environments with strict user isolation
- Integration with existing Docker/Kubernetes infrastructure
- Custom authentication and enterprise security requirements
- Cost-sensitive deployments with high usage volumes
Production ShinyProxy deployments require careful infrastructure planning to ensure reliability and performance:
Minimum Production Requirements:
- CPU: 8+ cores (4 cores minimum for testing)
- Memory: 16GB+ RAM (32GB+ recommended for heavy usage)
- Storage: 100GB+ SSD storage for containers and logs
- Network: Stable internet connection with adequate bandwidth
Scaling Considerations:
# Estimate resource needs based on concurrent users
# Rule of thumb: 2GB RAM + 1 CPU core per 10 concurrent users
concurrent_users = 50
estimated_memory = concurrent_users * 0.2 # GB per user
estimated_cores = concurrent_users / 10
echo "Estimated needs: ${estimated_memory}GB RAM, ${estimated_cores} CPU cores"
Infrastructure Architecture:
- Load Balancer: Traefik, Nginx, or cloud load balancer for high availability
- Container Storage: Fast SSD storage for Docker images and temporary files
- Backup Strategy: Regular backups of configuration and user data
- Monitoring: Prometheus, Grafana, or equivalent monitoring stack
Network and Security:
- Firewall Configuration: Secure access to necessary ports only (80, 443, management ports)
- SSL Certificates: Valid certificates for production domains
- VPN Access: Consider VPN requirements for internal applications
- DNS Configuration: Proper DNS setup for custom domains
High Availability Setup:
- Multiple ShinyProxy instances behind load balancer
- Shared configuration storage (NFS, cloud storage)
- Database clustering for authentication backends
- Geographic distribution for disaster recovery
Container issues are among the most common ShinyProxy problems, but systematic troubleshooting can resolve most issues:
Step 1: Check ShinyProxy Logs
# View ShinyProxy logs
sudo journalctl -u shinyproxy -f --lines=100
# Check for specific error patterns
sudo journalctl -u shinyproxy | grep -i "error\|failed\|exception"
Step 2: Test Container Images Directly
# Test the container image outside of ShinyProxy
docker run --rm -p 3838:3838 your-registry/shiny-app:latest
# Check if the application starts correctly
curl http://localhost:3838/
# Examine container logs
docker logs [container-id]
Step 3: Verify Resource Constraints
# Check if resource limits are too restrictive
specs:
- id: problematic-app
container-memory: "4GB" # Increase if needed
container-cpu-limit: 2 # Ensure adequate CPU
# Add resource monitoring
container-env:
R_MAX_VSIZE: "3GB"
Step 4: Common Issues and Solutions
Memory Issues:
- Increase container memory limits
- Optimize R memory usage with
gc()
calls - Check for memory leaks in application code
Package/Dependency Problems:
- Verify all required packages are in the container image
- Check package version compatibility
- Ensure system dependencies are installed
Permission Issues:
- Verify container runs with appropriate user permissions
- Check file system permissions for shared volumes
- Ensure Docker daemon permissions are correct
Network/Port Conflicts:
- Verify port ranges don’t conflict with other services
- Check firewall rules and network policies
- Ensure containers can communicate with required services
Step 5: Advanced Debugging
# Access container for debugging
docker exec -it [container-id] /bin/bash
# Check container resource usage
docker stats [container-id]
# Inspect container configuration
docker inspect [container-id]
Yes, ShinyProxy provides robust enterprise authentication integration capabilities:
LDAP/Active Directory Integration:
# Complete LDAP configuration example
proxy:
authentication: ldap
ldap:
url: ldap://ad.company.com:389
user-dn-pattern: cn={0},ou=users,dc=company,dc=com
user-search-base: ou=users,dc=company,dc=com
user-search-filter: (sAMAccountName={0})
group-search-base: ou=groups,dc=company,dc=com
group-search-filter: (member={0})
group-role-attribute: cn
manager-dn: cn=service-account,ou=service-accounts,dc=company,dc=com
manager-password: ${LDAP_SERVICE_PASSWORD}
SAML Integration for SSO:
# SAML configuration for enterprise SSO
proxy:
authentication: saml
saml:
app-entity-id: shinyproxy-analytics
idp-entity-id: https://sso.company.com/adfs/services/trust
idp-single-sign-on-service-url: https://sso.company.com/adfs/ls/
idp-single-logout-service-url: https://sso.company.com/adfs/ls/
idp-x509-certificate: |
-----BEGIN CERTIFICATE-----
[Your IdP certificate content]
-----END CERTIFICATE----- username-attribute: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
groups-attribute: http://schemas.microsoft.com/ws/2008/06/identity/claims/groups
OAuth2 Integration:
# OAuth2 with Azure AD example
proxy:
authentication: oauth2
oauth2:
client-id: ${AZURE_CLIENT_ID}
client-secret: ${AZURE_CLIENT_SECRET}
access-token-uri: https://login.microsoftonline.com/[tenant-id]/oauth2/v2.0/token
user-authorization-uri: https://login.microsoftonline.com/[tenant-id]/oauth2/v2.0/authorize
scope: openid profile email
user-info-uri: https://graph.microsoft.com/v1.0/me
username-attribute: userPrincipalName
Advanced Group Mapping:
# Map enterprise groups to ShinyProxy roles
proxy:
admin-groups: [IT-ADMINS, SHINY-ADMINISTRATORS]
specs:
- id: executive-dashboard
access-groups: [C-SUITE, VP-LEVEL, EXECUTIVE-TEAM]
- id: analyst-tools
access-groups: [DATA-ANALYSTS, BUSINESS-ANALYSTS, DATA-SCIENTISTS]
- id: public-reports
# No access-groups = available to all authenticated users
Testing Authentication:
# Test LDAP connectivity
ldapsearch -H ldap://ad.company.com:389 \
-D "cn=service-account,ou=service-accounts,dc=company,dc=com" \
-W -b "ou=users,dc=company,dc=com" \
"(sAMAccountName=testuser)"
# Verify group membership
ldapsearch -H ldap://ad.company.com:389 \
-D "cn=service-account,ou=service-accounts,dc=company,dc=com" \
-W -b "ou=groups,dc=company,dc=com" \
"(member=cn=testuser,ou=users,dc=company,dc=com)"
Security Best Practices:
- Use service accounts with minimal required permissions
- Secure LDAP connections with TLS/SSL
- Store sensitive credentials as environment variables
- Implement proper group-based access control
- Regular security audits of authentication configuration
Scaling ShinyProxy requires a multi-layered approach combining load balancing, redundancy, and resource optimization:
High Availability Architecture:
# docker-compose.yml for HA deployment
version: '3.8'
services:
traefik:
image: traefik:v3.0
command:
- "--providers.docker=true"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
ports:
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
networks:
- shinyproxy
shinyproxy-1:
image: openanalytics/shinyproxy:3.0.2
environment:
- SPRING_PROFILES_ACTIVE=prod
volumes:
- ./config:/opt/shinyproxy/config
- /var/run/docker.sock:/var/run/docker.sock
labels:
- "traefik.enable=true"
- "traefik.http.services.shinyproxy.loadbalancer.server.port=8080"
- "traefik.http.services.shinyproxy.loadbalancer.healthcheck.path=/actuator/health"
networks:
- shinyproxy
shinyproxy-2:
image: openanalytics/shinyproxy:3.0.2
environment:
- SPRING_PROFILES_ACTIVE=prod
volumes:
- ./config:/opt/shinyproxy/config
- /var/run/docker.sock:/var/run/docker.sock
labels:
- "traefik.enable=true"
- "traefik.http.services.shinyproxy.loadbalancer.server.port=8080"
networks:
- shinyproxy
networks:
shinyproxy:
driver: bridge
Scaling Strategies:
Horizontal Scaling:
- Deploy multiple ShinyProxy instances
- Use load balancer for traffic distribution
- Implement session stickiness for WebSocket connections
- Share configuration through network storage
Vertical Scaling:
- Increase server resources (CPU, memory)
- Optimize container resource allocation
- Tune JVM parameters for ShinyProxy
- Implement resource monitoring and alerting
Container Optimization:
# Optimized container configuration
proxy:
docker:
# Connection pooling
pool-size: 20
max-total-connections: 200
# Resource management
port-range-start: 20000
port-range-max: 25000
specs:
- id: optimized-app
container-memory: "2GB"
container-cpu-limit: 2
container-cpu-reservation: 1
# Quick startup optimization
container-env:
R_COMPILE_PKGS: "0"
R_DISABLE_HTTPD: "1"
Monitoring and Auto-scaling:
# Monitor resource usage
docker stats --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}"
# Implement auto-scaling triggers
# Scale up when CPU > 80% for 5 minutes
# Scale down when CPU < 30% for 15 minutes
Performance Optimization:
- Pre-build and cache container images
- Use fast SSD storage for Docker
- Implement container image layering optimization
- Regular cleanup of unused containers and images
- Network optimization for container communication
Geographic Distribution:
- Deploy in multiple regions for global users
- Use CDN for static assets
- Implement database replication for multi-region setups
- Consider edge caching for improved performance
Test Your Understanding
What are the key advantages of ShinyProxy’s container-per-user architecture compared to traditional shared Shiny Server deployments?
- Containers use less memory than shared processes
- Each user gets complete resource isolation and security separation
- Container startup is faster than shared server connections
- Authentication is simpler with containers
- Think about what happens when multiple users access the same server
- Consider security implications of shared vs. isolated environments
- Remember the resource management capabilities discussed
B) Each user gets complete resource isolation and security separation
ShinyProxy’s container-per-user architecture provides several critical advantages: - Complete isolation: Each user’s session runs in its own container, preventing memory leaks, crashes, or security issues from affecting other users - Resource control: Individual memory and CPU limits per container prevent one user from consuming all server resources - Security boundaries: Containers provide process-level isolation that protects sensitive data and prevents cross-user access - Scalability: Dynamic container creation allows for better resource utilization and scaling
While containers do have overhead compared to shared processes, the benefits of isolation, security, and resource management far outweigh the costs in enterprise environments.
Complete this LDAP authentication configuration for an enterprise environment:
proxy:
authentication: ldap
ldap:
url: ldap://ldap.company.com:389
user-dn-pattern: uid={0},ou=users,dc=company,dc=com
group-search-base: ou=groups,dc=company,dc=com
group-search-filter: ______
manager-dn: cn=admin,dc=company,dc=com
manager-password: ${LDAP_MANAGER_PASSWORD}
What should replace the blank for proper group membership checking?
- Think about how LDAP stores group membership information
- Consider the standard LDAP attribute for group members
- The {0} placeholder represents the user’s distinguished name
group-search-filter: (member={0})
Explanation:
(member={0})
searches for groups where the user’s DN is listed as a member- The
{0}
placeholder is replaced with the user’s full distinguished name - This is the standard LDAP group membership pattern
- Alternative patterns might include
(memberUid={1})
for username-based membership or(uniqueMember={0})
for certain LDAP implementations
Additional considerations:
- Verify your LDAP schema’s group membership attribute
- Test with
ldapsearch
to confirm the correct filter - Some organizations use nested groups requiring recursive search filters
You’re deploying ShinyProxy for a financial services company that requires:
- High availability with zero downtime
- Automatic SSL certificate management
- Load balancing across multiple instances
- Health monitoring and failover
Which deployment approach would you recommend and why?
- Single ShinyProxy instance with Nginx reverse proxy
- Multiple ShinyProxy instances with Traefik orchestration
- ShinyProxy with manual load balancer configuration
- Cloud-managed container service without reverse proxy
- Consider the specific requirements: HA, automatic SSL, load balancing, monitoring
- Think about which solution provides the most automation
- Remember the advantages of different reverse proxy solutions
B) Multiple ShinyProxy instances with Traefik orchestration
Why this is the best choice:
High Availability:
- Multiple ShinyProxy instances provide redundancy
- Automatic failover when instances become unhealthy
- Zero-downtime deployments with rolling updates
Automatic SSL Management:
- Traefik’s built-in Let’s Encrypt integration
- Automatic certificate renewal
- No manual certificate management overhead
Load Balancing:
- Dynamic service discovery and load balancing
- Session stickiness for WebSocket connections
- Health check integration with automatic instance removal
Monitoring and Failover:
- Built-in health checking and metrics
- Prometheus integration for monitoring
- Automatic traffic routing away from failed instances
Enterprise Benefits:
- Container-native approach aligns with modern infrastructure
- Reduced operational overhead compared to manual configurations
- Better security with automatic updates and proper isolation
The Traefik + multiple ShinyProxy approach provides the automation, reliability, and scalability required for enterprise financial services environments.
Conclusion
ShinyProxy represents a significant evolution in enterprise Shiny deployment, providing the container orchestration, security, and scalability required for production analytical applications. By leveraging Docker containers for complete user isolation and integrating seamlessly with enterprise authentication systems, ShinyProxy bridges the gap between prototype applications and enterprise-grade analytical platforms.
The container-per-user architecture ensures that applications remain performant and secure even under heavy concurrent usage, while features like dynamic resource allocation and automatic container lifecycle management optimize infrastructure utilization. Combined with Traefik’s modern reverse proxy capabilities, ShinyProxy deployments can achieve enterprise-grade reliability with minimal operational overhead.
Whether you’re building internal analytical tools for your organization or deploying client-facing dashboards that require enterprise security and compliance, ShinyProxy provides the foundation for scalable, maintainable, and secure Shiny application deployment that grows with your needs.
Next Steps
Based on what you’ve learned in this tutorial, here are the recommended paths for continuing your ShinyProxy mastery:
Immediate Next Steps (Complete These First)
- Docker Containerization for Shiny Apps - Master container optimization techniques that complement ShinyProxy deployment
- Production Deployment and Monitoring - Implement comprehensive monitoring for your ShinyProxy infrastructure
- Practice Exercise: Deploy a test ShinyProxy environment with multiple applications and practice scaling, authentication, and monitoring
Building on Your Foundation (Choose Your Path)
For Enterprise Integration Focus:
For Cloud Deployment Focus:
For Production Operations:
Long-term Goals (2-4 Weeks)
- Deploy a production ShinyProxy environment serving multiple applications
- Implement automated CI/CD pipelines for container deployment
- Integrate with organizational monitoring and alerting systems
- Create standardized deployment templates for your organization
Explore More Articles
Here are more articles from the same category to help you dive deeper into production deployment.
Reuse
Citation
@online{kassambara2025,
author = {Kassambara, Alboukadel},
title = {ShinyProxy: {Enterprise} {Container} {Orchestration} for
{Shiny} {Applications}},
date = {2025-05-23},
url = {https://www.datanovia.com/learn/tools/shiny-apps/production-deployment/shinyproxy-deployment.html},
langid = {en}
}