Deploying from the CLI when ShinyHub is behind an auth proxy¶
When ShinyHub sits behind an authentication proxy (Authelia, oauth2-proxy, Cloudflare Access, or any other forward-auth solution), browser users authenticate at the edge before their requests ever reach ShinyHub. That flow is browser-only: the proxy redirects unauthenticated requests to a login page and exchanges cookies or tokens through the browser.
The CLI (shinyhub deploy, shinyhub env, shinyhub apps, and every other
subcommand) is not a browser. It cannot follow an interactive redirect, complete
an OAuth dance, or satisfy a CAPTCHA. A CLI request to the proxied hostname gets
bounced to the auth provider's login page and returns an unexpected HTML response
instead of the JSON the CLI expects.
The fix: target the app port directly¶
For CLI and CI operations, bypass the proxy entirely. Talk directly to the
ShinyHub port (default :8080) and authenticate with a pre-shared deploy token.
Browser --> Proxy (auth) --> ShinyHub :8080
^
|
CLI / CI --------+ (bypasses the proxy; uses deploy token)
The two pieces are:
-
Server side: set
SHINYHUB_DEPLOY_TOKEN(at least 32 characters) on the server process. Optionally setSHINYHUB_DEPLOY_TOKEN_ROLEto control what the synthetic deploy user can do (default:developer), andSHINYHUB_DEPLOY_TOKEN_APPS(comma-separated slugs) to restrict the token to specific apps. -
Client side: set
SHINYHUB_HOSTto the direct URL of the ShinyHub port (not the proxied hostname) andSHINYHUB_TOKENto the same value you set forSHINYHUB_DEPLOY_TOKEN.
Server configuration¶
In the environment of the shinyhub serve process:
SHINYHUB_DEPLOY_TOKEN="$(openssl rand -hex 32)" # generate once; store in your secrets manager
SHINYHUB_DEPLOY_TOKEN_ROLE=developer # optional; default is developer
SHINYHUB_DEPLOY_TOKEN_APPS=sales,hr-dashboard # optional; restrict the token to these apps
The token is never persisted to disk. To rotate it, change the env var and restart the server.
Scope the token to what CI actually deploys. With
SHINYHUB_DEPLOY_TOKEN_APPS set, the token can only see, deploy, and manage
the listed slugs (it may create them if they do not exist yet); every other
app returns 404, regardless of the token's role. This caps the blast radius of
a leaked CI secret. Avoid SHINYHUB_DEPLOY_TOKEN_ROLE=admin: it grants the
token user management on top of every app, and the server logs a warning at
startup when it sees that configuration.
Worked example¶
Assume ShinyHub listens on 10.0.1.5:8080 (reachable on your LAN or VPN), and
the proxied public hostname is https://shiny.example.com.
Deploy an app:
SHINYHUB_HOST=http://10.0.1.5:8080 \
SHINYHUB_TOKEN=your-deploy-token-here \
shinyhub deploy ./my-app --slug myapp
Set an environment variable:
SHINYHUB_HOST=http://10.0.1.5:8080 \
SHINYHUB_TOKEN=your-deploy-token-here \
shinyhub env set myapp KEY=value
The two env vars can also be written to the CLI credentials file
(shinyhub connect http://10.0.1.5:8080), but for CI pipelines injecting
them as environment variables is simpler and avoids storing credentials on disk.
CI pipeline example¶
# GitHub Actions excerpt
- name: Deploy to ShinyHub
env:
SHINYHUB_HOST: http://10.0.1.5:8080
SHINYHUB_TOKEN: ${{ secrets.SHINYHUB_DEPLOY_TOKEN }}
run: shinyhub deploy . --slug myapp --wait
Security notes¶
Restrict direct access to the app port. If SHINYHUB_DEPLOY_TOKEN is set,
anyone who can reach the app port and knows the token can deploy apps or change
settings. Restrict reachability to operators: bind the app port to a private
network interface, or protect it behind a firewall or VPN so only the proxy (for
browser traffic) and authorised operators (for CLI/CI) can connect.
trusted_proxies and the direct CLI path. server.trusted_proxies
controls which peer IP addresses may inject forward-auth identity headers
(X-Forwarded-User, etc.). A direct CLI connection from an address that is NOT
in trusted_proxies is treated as an ordinary authenticated client: the deploy
token is validated, but no forward-auth identity is accepted. This is the
correct behaviour - the CLI is not acting as a proxy and should not be able to
claim an arbitrary identity.
App visibility and the app port. When defaults.app_visibility is set to
public and the app port is directly reachable (LAN, VPN, or any other
interface), apps are accessible with no ShinyHub-level authentication from that
path. See the note in the configuration file and in the App visibility and auth
proxy section below for how to handle this.
App visibility and the auth proxy¶
A common pattern behind an auth proxy is to set defaults.app_visibility:
public so the proxy handles all authentication and ShinyHub does not add a
second login prompt. This is safe only when the auth proxy is the EXCLUSIVE
ingress to ShinyHub.
If the ShinyHub app port is also reachable directly (LAN, VPN, Tailscale, or
a second network interface), public means apps are accessible with no auth at
all from that path - the proxy is bypassed and ShinyHub grants access freely.
In that case, prefer one of these approaches:
-
Set
defaults.app_visibility: private. Users who reach ShinyHub through the auth proxy still get a valid identity (forward-auth), so they are authenticated and see apps they have access to. Admins who connect directly with the deploy token can access everything. No app is exposed without auth. -
Keep
publicand firewall the app port so only the auth proxy host can reach it. Direct CLI access then goes through the proxy or via a side-channel (such as an SSH tunnel or a separate internal-only port).
Config file vs. credentials file¶
ShinyHub has two distinct config concepts:
-
Server config file (
shinyhub.yaml): containsauth,server,storage, and other server settings. Selected withshinyhub serve --config <file>or theSHINYHUB_CONFIGenv var on the SERVER process. -
Client credentials file (
~/.config/shinyhub/config.json): stores the host URL and API token written byshinyhub connectorshinyhub login. Selected with the--configflag on CLIENT commands, or theSHINYHUB_CONFIGenv var on the CLIENT side. When usingSHINYHUB_HOSTandSHINYHUB_TOKENdirectly, the credentials file is not read.
The SHINYHUB_CONFIG name is shared by both roles (server config path on the
server, client credentials path on the client), which can be confusing. When
operating a server, set SHINYHUB_CONFIG in the server process environment. In
a CI job that runs only client commands, SHINYHUB_CONFIG points to the
credentials file - but for the token-based flow described in this guide,
SHINYHUB_HOST and SHINYHUB_TOKEN are simpler and do not require a file.