Skip to content

Networking

Scryer’s networking settings describe different sides of the same connection. Keeping those responsibilities separate makes direct, Docker, and reverse-proxy deployments much easier to reason about.

  • SCRYER_BIND controls the local address and port where the Scryer process accepts connections.
  • SCRYER_PUBLIC_URL is the browser-facing origin that OAuth discovery advertises. It does not change where Scryer listens.
  • SCRYER_BASE_PATH is the path prefix Scryer actually serves, such as /scryer.
  • SCRYER_TLS_CERT and SCRYER_TLS_KEY enable direct HTTPS termination inside Scryer. Leave both unset when a reverse proxy terminates TLS.
  • SCRYER_RATE_LIMIT_TRUSTED_PROXY_IPS identifies the controlled proxy addresses allowed to supply client identity through X-Forwarded-For.

The complete defaults and accepted values remain in the Configuration reference.

The native default listens only on loopback:

SCRYER_BIND=127.0.0.1:8080

Only software on the same machine can connect. Open http://127.0.0.1:8080. Homebrew uses the same loopback-only shape on port 8686 unless you override it.

Listen on all IPv4 interfaces when other trusted devices on the network need to connect directly:

SCRYER_BIND=0.0.0.0:8080
SCRYER_AUTH_ENABLED=true

Open http://<server-address>:8080 from another device. Restrict the port to the intended LAN or VPN with the host firewall. For IPv6, use a bracketed socket address such as [::]:8080.

Binding to a non-loopback address makes the service reachable; it does not authenticate clients or configure a firewall.

For a public hostname, terminate HTTPS at the reverse proxy and keep the Scryer connection private to the host or container network:

SCRYER_BIND=0.0.0.0:8080
SCRYER_AUTH_ENABLED=true
SCRYER_PUBLIC_URL=https://scryer.example.com
SCRYER_RATE_LIMIT_TRUSTED_PROXY_IPS=172.20.0.2

If the proxy runs on the same host as a native Scryer process, bind Scryer to 127.0.0.1 instead. If the proxy and Scryer run in separate containers, Scryer must listen on the container interface, normally 0.0.0.0, and both services should share a dedicated Docker network.

A minimal Caddy site is:

scryer.example.com {
reverse_proxy scryer:8080
}

Caddy preserves the public Host, adds the usual forwarding headers, and proxies WebSocket upgrades by default.

Set a base path when Scryer shares a hostname with another application:

SCRYER_BIND=0.0.0.0:8080
SCRYER_AUTH_ENABLED=true
SCRYER_BASE_PATH=/scryer
SCRYER_PUBLIC_URL=https://media.example.com
SCRYER_RATE_LIMIT_TRUSTED_PROXY_IPS=172.20.0.2

SCRYER_PUBLIC_URL is an origin only. Do not include /scryer in it. Scryer combines the public origin and base path when it advertises OAuth endpoints.

The proxy must preserve the prefix on the upstream request:

media.example.com {
@scryer path /scryer /scryer/*
reverse_proxy @scryer scryer:8080
}

Do not use a path-stripping rule. With this configuration, Scryer serves:

  • UI: /scryer/
  • GraphQL: /scryer/graphql
  • GraphQL WebSocket: /scryer/graphql/ws
  • Health: /scryer/health
  • Readiness: /scryer/health/ready

A request to /scryer redirects to /scryer/ so browser asset paths resolve correctly.

Scryer can terminate HTTPS itself when both PEM paths are available at startup:

SCRYER_BIND=0.0.0.0:8443
SCRYER_TLS_CERT=/config/tls/fullchain.pem
SCRYER_TLS_KEY=/config/tls/privkey.pem
SCRYER_PUBLIC_URL=https://scryer.example.com:8443
SCRYER_AUTH_ENABLED=true

Both TLS variables are required together. The paths must be readable by the Scryer process, or startup fails. Certificates are loaded at startup, so restart Scryer after renewal.

For most container deployments, terminating TLS at a dedicated reverse proxy keeps certificate management in one place. Do not set the Scryer TLS variables in that topology.

Any reverse proxy in front of Scryer must:

  1. Preserve the browser-facing Host header.
  2. Set X-Forwarded-Proto to one exact http or https value.
  3. Append the connecting client to X-Forwarded-For.
  4. Support WebSocket upgrades on /graphql/ws, including the configured base path when present.
  5. Preserve SCRYER_BASE_PATH instead of stripping it.

Scryer uses Host and X-Forwarded-Proto for same-origin WebSocket and OAuth behavior. Explicitly setting SCRYER_PUBLIC_URL is recommended behind a reverse proxy so OAuth metadata cannot advertise an internal hostname or HTTP scheme when headers are missing or rewritten.

Forwarding headers are untrusted by default. Without SCRYER_RATE_LIMIT_TRUSTED_PROXY_IPS, every user behind a proxy shares the proxy’s rate-limit bucket.

Trust only the exact address or narrow CIDR used by proxies you control:

SCRYER_RATE_LIMIT_TRUSTED_PROXY_IPS=172.20.0.2

Multiple values are comma-separated. Configure every controlled proxy hop that Scryer should remove from the right side of the chain. For rate-limit identity, Scryer uses only X-Forwarded-For; missing or malformed chains safely fall back to the socket peer.

This setting affects rate limiting only. It does not grant administrative access and does not make the proxy authoritative for WebAuthn, OAuth, or unauthenticated-access policy.

Passkeys need an explicit, stable HTTPS origin in addition to the general networking settings:

SCRYER_WEBAUTHN_RP_ID=scryer.example.com
SCRYER_WEBAUTHN_RP_ORIGIN=https://scryer.example.com
SCRYER_WEBAUTHN_RP_NAME=Scryer

The relying-party ID is a hostname. The relying-party origin is the exact scheme, host, and optional port seen by the browser. Neither value includes SCRYER_BASE_PATH. See Login and passkey setup for the full authentication flow.

Inbound browser traffic and Scryer’s outbound integration traffic use different addresses:

  • A browser uses the host port or public reverse-proxy URL.
  • Another Compose service uses the Scryer service name, such as http://scryer:8080.
  • Scryer reaches another container by that service’s Compose name, such as http://sabnzbd:8080.
  • localhost inside the Scryer container means the Scryer container itself, not the Docker host and not another container.

Configure download clients, indexers, metadata providers, and other integration URLs in the Scryer UI using addresses reachable from the Scryer process. SCRYER_OUTBOUND_HOST_RPS can lower Scryer’s request rate to public outbound hosts, but it does not change routing or proxy configuration.

Check the direct listener first, then the browser-facing URL:

Terminal window
curl --fail --silent --show-error http://127.0.0.1:8080/health
curl --fail --silent --show-error https://scryer.example.com/health

For a base-path deployment:

Terminal window
curl --fail --silent --show-error https://media.example.com/scryer/health
curl --fail --silent --show-error https://media.example.com/scryer/health/ready
curl --fail --silent --show-error https://media.example.com/scryer/.well-known/oauth-authorization-server

The OAuth discovery response should use the public HTTPS origin, include the base path when configured, and never advertise an internal container hostname. Finally, open the UI and confirm that live updates work; a page that loads but never updates often indicates a missing WebSocket upgrade.

  • Connection refused from another machine: Confirm SCRYER_BIND is not loopback-only, the host port is published, and the firewall allows the client network.
  • UI loads but live state does not update: Confirm the proxy supports WebSocket upgrades on /graphql/ws or <base-path>/graphql/ws.
  • OAuth discovery advertises HTTP or an internal hostname: Set SCRYER_PUBLIC_URL to the browser-facing origin and verify Host and X-Forwarded-Proto.
  • A subpath returns 404 or loads without assets: Set SCRYER_BASE_PATH and preserve that prefix on upstream requests. Do not strip it.
  • Everyone behind the proxy is rate-limited together: Add only the controlled proxy address or narrow CIDR to SCRYER_RATE_LIMIT_TRUSTED_PROXY_IPS.
  • Startup reports an invalid bind address: Use a complete socket address such as 127.0.0.1:8080, 0.0.0.0:8080, or [::]:8080.
  • Startup reports a TLS configuration error: Set both TLS paths, confirm they are readable inside the host or container, and verify the files contain PEM data.