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.
- 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"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"