Services
A service is a long-running process — a web server, notebook, API, or viewer — that runs until the user explicitly stops it. Services are declared with type: service on the runnable:
runnables: - id: notebook name: JupyterLab type: service command: jupyter lab --no-browser --ip=0.0.0.0 --port=0 resources: cpus: 4 memory: "32 GB" walltime: "08:00"How It Works
Section titled “How It Works”From the cluster’s perspective, a service is just a long-running batch job. The difference is in how Fileglancer communicates the service URL to the user:
- The user launches a service-type runnable → the job enters PENDING.
- The cluster picks it up → RUNNING.
- The service starts and publishes its URL: either Fileglancer writes
SERVICE_URL_PATHforauto_urlservices, or the service writes that file itself. - On the next poll (every few seconds), Fileglancer reads the file and displays the URL in the UI — rewritten to an HTTPS address if the server has a service proxy configured (see Access Over HTTPS).
- The user clicks Open Service → the service opens in a new browser tab.
- When done, the user clicks Stop Service → the job is killed and the URL disappears.
The Easy Path: Let Fileglancer Manage the Port and URL
Section titled “The Easy Path: Let Fileglancer Manage the Port and URL”For most services you don’t need to write any port-discovery or URL-writing code. For every service-type job Fileglancer exports these helper variables into the job environment:
FG_SERVICE_PORT— a free TCP port picked on the compute node at job start.FG_HOSTNAME— the compute node’s hostname.FG_SERVICE_TOKEN— a URL-safe random secret you can use for auth.
Bind your service to $FG_SERVICE_PORT and set auto_url: true. Fileglancer then waits until that port is accepting connections and only then writes http://$FG_HOSTNAME:$FG_SERVICE_PORT to SERVICE_URL_PATH — so the Open Service link never appears before your service (or a still-pulling container image) is ready. While a first-launch container image is still downloading, the job page shows a “Downloading container image…” message so the wait is explained rather than mysterious.
Because the port is chosen before your command runs and substituted straight into the command line, this works even for container runnables — no wrapper script inside the image is needed:
runnables: - id: serve name: Server type: service auto_url: true container: ghcr.io/coder/code-server:latest command: code-server --bind-addr 0.0.0.0:$FG_SERVICE_PORT --auth none requirements: - apptainer resources: cpus: 2 memory: "8 GB" walltime: "08:00"One-click access with a token
Section titled “One-click access with a token”Most servers can take an auth token in the URL. Pass $FG_SERVICE_TOKEN to your server, and add a service_url_suffix that splices the token into the published URL — clicking Open Service then logs straight in, no password prompt, while the session stays protected by the secret:
runnables: - id: serve name: Server type: service auto_url: true service_url_suffix: "/?access_token=${FG_SERVICE_TOKEN}" command: marimo edit --headless --host 0.0.0.0 --port $FG_SERVICE_PORT --token-password="$FG_SERVICE_TOKEN" requirements: - pixiservice_url_suffix is a small template: literal URL text plus the placeholders ${FG_SERVICE_TOKEN}, ${FG_SERVICE_PORT}, and ${FG_HOSTNAME} (braces required). It’s appended to http://$FG_HOSTNAME:$FG_SERVICE_PORT, so it can carry a path as well as a query — e.g. JupyterLab uses "/lab?token=${FG_SERVICE_TOKEN}".
Writing the Service URL
Section titled “Writing the Service URL”If your service picks its own port, needs a readiness check more specific than “port is open” (e.g. an HTTP health path), or serves an https/otherwise non-standard URL, write SERVICE_URL_PATH yourself instead of using auto_url (the two are mutually exclusive). Fileglancer exports SERVICE_URL_PATH — the absolute path to a file where your service should write its URL once it is ready to accept connections.
In Python:
import os, socket
url = f"http://{socket.gethostname()}:{port}"service_url_path = os.environ.get("SERVICE_URL_PATH")if service_url_path: with open(service_url_path, "w") as f: f.write(url)In Bash:
echo "http://$(hostname):${PORT}" > "$SERVICE_URL_PATH"Access Over HTTPS
Section titled “Access Over HTTPS”Your service publishes a plain http://host:port URL, but that is not always what the user’s browser opens. When the Fileglancer server has a service proxy configured, it republishes each running service at a per-job HTTPS subdomain:
https://job-42-k7m2qhxr.services.example.org/lab?token=abc123The path, query, and fragment of the URL you published are carried across unchanged, so a service_url_suffix that splices in ${FG_SERVICE_TOKEN} keeps working exactly as written. Nothing in your manifest needs to change, and your job cannot tell whether the server does this — always publish the direct URL and let Fileglancer rewrite it.
Two things are worth knowing when you test an app on a server with the proxy enabled:
- Your service sees the proxy subdomain in the
Hostheader, not the compute node’s name. Servers that check the request origin on WebSocket connections generally accept this, because the proxy passesHostandOriginthrough as the same value. If yours rejects it, look for an allowed-origin or base-URL option rather than turning the check off. - A service that pins an absolute URL — an OAuth callback registered against one fixed host and port, say — may not work at a rewritten address at all.
Opting out of the proxy
Section titled “Opting out of the proxy”If your service genuinely cannot work at a rewritten URL, set service_proxy: false on the runnable:
runnables: - id: serve name: Start Server type: service service_proxy: false command: ./serve.shFileglancer then publishes that runnable’s direct http://host:port URL even on a server where every other service is republished, and the proxy refuses to serve it.
Service Lifecycle
Section titled “Service Lifecycle”- Startup: with
auto_url, Fileglancer writesSERVICE_URL_PATHonce$FG_SERVICE_PORTaccepts connections. Withoutauto_url, write the URL toSERVICE_URL_PATHyourself as soon as the service is ready. Until the file exists, the UI shows “Service is starting up…”. - Running: Fileglancer reads the URL file on each poll. If the URL changes (e.g. a port rebind), the UI updates automatically.
- Shutdown: when the user clicks Stop Service, Fileglancer sends a SIGTERM to the job. Handle this signal for graceful shutdown. Cleaning up the URL file on exit is good practice but not required — Fileglancer only reads it while the job status is RUNNING.
- Port selection: use
$FG_SERVICE_PORTwithauto_url, or port0if your service writes the actual chosen URL toSERVICE_URL_PATHitself. - Walltime: set a generous walltime — services run until stopped, but the cluster will kill them when walltime expires. Consider
"08:00"or longer for interactive sessions. - Flush output: under a batch scheduler like LSF, Python’s stdout may be buffered. Use
flush=Trueon print statements or setPYTHONUNBUFFERED=1so logs appear in real time.
Full Service Example
Section titled “Full Service Example”name: My Viewerdescription: Interactive data viewer
runnables: - id: view name: Start Viewer type: service auto_url: true description: Launch an interactive viewer for browsing datasets command: pixi run python start_viewer.py --host 0.0.0.0 --port $FG_SERVICE_PORT parameters: - flag: --data-dir name: Data Directory type: directory description: Directory containing datasets to view required: true
resources: cpus: 2 memory: "8 GB" walltime: "08:00"