Exposing Web Services with frp

Inbound connections to compute nodes are not supported, so you cannot directly publish a listening port from a job to the internet. If you need to access a web service running inside a job (e.g. JupyterLab), you can use frp (Fast Reverse Proxy).

With this approach, the frpc client runs inside your Slurm job and establishes an outbound connection to a proxy server (frps). The proxy server then exposes your local HTTP service under a public HTTPS URL.

Note

This is an experimental service. Ask hpc-support@dkf.hu for: the proxy server address/port, an authentication token, and the allowed domain(s).

Download frpc (client)

The upstream project publishes binaries on GitHub Releases.

Because the release assets are versioned (for example frp_<VERSION>_linux_amd64.tar.gz), there is no single, permanent URL that always downloads “the latest tarball” without first resolving the current version. For automation, resolve the latest asset URL via the GitHub API:

# Download the latest Linux x86_64 client tarball (requires: curl, jq)
url="$(curl -fsSL https://api.github.com/repos/fatedier/frp/releases/latest \
  | jq -r '.assets[] | select(.name | test("linux_amd64\\.tar\\.gz$")) | .browser_download_url' \
  | head -n1)"
curl -fL -O "$url"

tar -xzf frp_*_linux_amd64.tar.gz
cd frp_*_linux_amd64
chmod +x frpc

Example frpc.toml configuration (HTTP)

Create a configuration file (TOML format), for example frpc.toml:

We recommend enabling HTTP Basic Authentication on the proxy by setting httpUser and httpPassword for the HTTP proxy.

serverAddr = "<FRPS_HOST_OR_IP>"
serverPort = 7000

auth.method = "token"
auth.token = "<FRP_TOKEN>"

[[proxies]]
name = "my-jupyter"
type = "http"
localIP = "127.0.0.1"
localPort = 8888

# Optional (recommended): protect access with HTTP Basic Auth
httpUser = "<HTTP_USER>"
httpPassword = "<HTTP_PASSWORD>"

# Choose a unique subdomain
subdomain = "<YOUR_SUBDOMAIN>"

# Domain suffix provided by DKF (example)
customDomains = ["guest.komondor.hinfra.hu"]

# Optional: basic health check to avoid routing to a dead service
healthCheck.type = "http"
healthCheck.path = "/"
healthCheck.intervalSeconds = 10
healthCheck.maxFailed = 3
healthCheck.timeoutSeconds = 3
healthCheck.httpHeaders = [
  { name = "x-from-where", value = "frp" }
]

After starting frpc with this config, the service is typically available at:

https://<YOUR_SUBDOMAIN>.guest.komondor.hinfra.hu/

Slurm example: publish a Jupyter notebook from a job

The key idea is to start both:

  1. your local web service (listening on 127.0.0.1:<PORT> inside the allocated node), and

  2. frpc (routing HTTP traffic from the proxy to that local port)

in the same Slurm job allocation.

Example frpc.sbatch:

#!/bin/bash
#SBATCH --partition=cpu
#SBATCH --cpus-per-task=16
#SBATCH --mem-per-cpu=2000
#SBATCH --account=<ACCOUNT>
#SBATCH --job-name=frpc
#SBATCH --time=01:00:00
#SBATCH --output=slurm-%j.out

set -euo pipefail

# Start frpc client (keep token/config in a protected location)
./frpc --config ./frpc.toml &
frpc_pid=$!
trap 'kill ${frpc_pid} 2>/dev/null || true' EXIT

# Start a local web service on the same node.
# Example: Jupyter notebook from a container.
module load singularity
singularity run --writable-tmpfs docker://jupyter/datascience-notebook \
  start-notebook.py --IdentityProvider.token="<JUPYTER_TOKEN>"

Troubleshooting

  • Check the Slurm output (slurm-%j.out) for frpc logs and for the web service startup messages.

  • Verify locally on the compute node that the service responds (example): curl -I http://127.0.0.1:8888/.

  • If you enabled health checks, ensure the healthCheck.path returns a 2xx status code.

  • If the public URL loads but notebooks fail to connect, ensure the service supports WebSockets (JupyterLab does) and that it is published via an HTTP proxy (not a raw TCP proxy).

Security notes

  • Treat the FRP auth token as a secret. Do not commit it into Git repositories.

  • Consider enabling HTTP Basic Auth in the proxy config (httpUser/httpPassword) in addition to your application’s own authentication.

  • Protect your application as well (e.g. use a strong Jupyter token) because it becomes internet-accessible.

  • Stop the job when you no longer need the service; the URL should be considered public for the duration of the job.