Skip to content

Deployment

The supported examples are a single GPU container and a portal connected to an SSH GPU worker. Keep uploads and outputs on controlled storage. Put the portal behind authenticated TLS; the code has no user authentication or API-key enforcement.

Single host with local GPU

Use compose.yaml. Supply a compatible NVIDIA driver/container runtime, a model bundle and isolated runtime environments. Mount models at /models, runtimes at /runtimes, persistent jobs at /data, and writable temporary files at /scratch. Model and runtime mounts are read-only. Preserve bundle symlinks and interpreter dependencies when transferring runtimes; copying a virtual environment alone may not make it relocatable.

cp .env.example .env
# Edit the bundle locations and ENABLED_EXTERNAL_MODELS.
docker compose build
docker compose up -d
curl -fsS http://localhost:8080/api/v1/health
curl -fsS http://localhost:8080/api/v1/models

The source Dockerfile is a generic CUDA image, not an export of a site-specific production image. The image installs the GL/X11/glib runtime libraries required by OpenCV and checks cv2 and rt_utils imports during the build. Check the built image's drivers and model runtimes before use. See the model ledger for retained input and licensing restrictions.

Portal and SSH GPU worker

Run compose.remote-worker.yaml on the GPU host. Supply SSH_AUTHORIZED_KEY from an external environment. The example publishes SSH on port 2222. Install the portal's private SSH key and known-hosts file outside the repository. Configure the portal account's SSH alias so ordinary ssh worker@rtsegmentator-worker and scp use the correct host, port and identity; there is no separate application port variable. Mount config alongside the private key and known_hosts in the portal account's SSH directory. For example:

Host rtsegmentator-worker
    HostName gpu-worker.example.org
    User worker
    Port 2222
    IdentityFile ~/.ssh/rtsegmentator_worker
    UserKnownHostsFile ~/.ssh/known_hosts
    IdentitiesOnly yes

The alias applies to every SSH, SCP and rsync transfer. This avoids separate port settings drifting between those clients.

On the portal, set:

WORKER_MODE=ssh
SEGMENTATION_WORKER=worker@rtsegmentator-worker
REMOTE_JOB_ROOT=/home/worker/jobs
REMOTE_PYTHON=/opt/rtsegmentator/venv/bin/python
REMOTE_RUNNER=/app/run_task.py
REMOTE_APP_ROOT=/app
REMOTE_MODEL_ROOT=/models
REMOTE_RUNTIME_ROOT=/runtimes
PORTAL_DATA=/data

Start a single application process with uvicorn app:app --host 0.0.0.0 --port 8080 --workers 1. Generic systemd and Supervisor examples are in dicom-rt-portal.service and deploy/. The worker receives only the selected series and required paired inputs/prompts. Configure writable scratch storage on the worker. Whole-job SSH mode and per-task SSH routing explicitly pass REMOTE_SCRATCH_ROOT as RTSEG_SCRATCH_ROOT, REMOTE_NV_REASON_CT_CACHE as RTSEG_NV_REASON_CT_CACHE, and REMOTE_TOTALSEG_WEIGHTS_PATH as TOTALSEG_WEIGHTS_PATH when set. They also pass application/model/runtime roots and the coverage mode. These settings do not require SSH AcceptEnv.

Configuration

Variable Behavior
WORKER_MODE Public default local; also accepts ssh, colab, auto
PORTAL_DATA Job/upload root; default data beside app.py, container /data
RTSEG_APP_ROOT Runner scripts and catalogs; defaults to the script directory, examples /app
RTSEG_MODEL_ROOT Model assets; default /models
RTSEG_RUNTIME_ROOT Isolated runtimes; runner fallback is the model root, examples explicitly /runtimes
RTSEG_SCRATCH_ROOT Optional runner scratch; unset creates a per-job rtseg-* directory under system temp and removes it at exit. An unwritable setting falls back to short system temp. A writable path over 60 bytes retains scratch/cache placement but uses short system temp for TMPDIR/TEMP/TMP. Examples use /scratch
ENABLED_EXTERNAL_MODELS Comma-separated external tasks allowed in local mode
ENABLED_MODELS_FILE Additional task names, one per line; default <PORTAL_DATA>/enabled-models.txt; blank/comment lines ignored
CATALOG_TTL_SECONDS Cache lifetime, default 600; enabled-file changes also trigger refresh
PREFLIGHT_TIMEOUT_SECONDS Model preflight timeout, default 900
TASK_TIME_LIMIT_SECONDS Local/SSH task time limit, default 43200
TASK_STOP_GRACE_SECONDS Local process-tree termination grace, default 45
MAX_UPLOAD_BYTES Upload limit, default 8589934592 bytes
RETENTION_HOURS Job/upload retention, default 24
RTSEG_ORIENTATION_VALIDATION Portal fallback marker directory; default <RTSEG_MODEL_ROOT>/orientation-validation
RTSEG_CERR_MODERN_PYTHON Optional override of <RTSEG_RUNTIME_ROOT>/envs/segmentation-modern/bin/python
RTSEG_CERR_PROSTATE_PYTHON Optional override of <RTSEG_MODEL_ROOT>/runtime-homes/cerr/env6-prostate/bin/python

Check the actual catalog after deployment. A source manifest or historical validation record does not install a model, enable it, or establish clinical suitability.

Updates and rollback

Keep the preceding image and configuration until the replacement passes import, health, upload and synthetic geometry checks. Wait for active jobs and uploads to finish before recreation. Retain model/runtime checksums separately. If startup or verification fails, restore the previous image/configuration and inspect retained job state. Do not overwrite the model bundle while a job is using it.

Selected tasks on a separate SSH GPU

For a local portal whose GPU cannot run NV-Reason-CT, retain WORKER_MODE=local and set REMOTE_GPU_TASKS=nv_reason_ct_report. auto mode uses the same routing; local tasks retain their existing Colab fallback. Listed tasks always use SSH and do not enter local OOM fallback. Pure ssh mode still sends the whole job to its worker. The portable colab mode does not support this report task and keeps it disabled.

Use the existing SEGMENTATION_WORKER, REMOTE_JOB_ROOT, REMOTE_PYTHON, REMOTE_RUNNER, REMOTE_APP_ROOT, REMOTE_MODEL_ROOT and REMOTE_RUNTIME_ROOT settings above. Install the same application version and the isolated report runtime on the worker. Configure writable scratch with REMOTE_SCRATCH_ROOT=/scratch, an optional persistent cache with REMOTE_NV_REASON_CT_CACHE=/scratch/nv-reason-ct-cache, and, if needed, REMOTE_TOTALSEG_WEIGHTS_PATH for the worker's existing coverage weights. RTSEG_NV_REASON_CT_COVERAGE_MODE is forwarded explicitly over SSH. Other worker runtime configuration belongs in the worker environment.

The report transfer includes selected input/, paired input_ct/ and prompt.json when present, plus finished RTSTRUCTs. Retrieval copies output/report/ only for a routed report, then removes the remote job directory. Other routed tasks retrieve their named RTSTRUCT. Whole SSH jobs retrieve the complete output directory. The existing task time limit and remote runner stop apply; failed retrieval retains remote results for recovery. Local contour files survive report failure and retry.

Remote preflight runs during catalog refresh and is cached with the catalog. Local markers cannot enable a remote task. A failed remote check disables only routed tasks. Health reports remote reachability separately. Install and verify the remote report acceptance marker for the selected coverage mode before enabling the task.

The local container needs an SSH client, rsync, a private key and a verified known-hosts file. The Dockerfile supplies the clients. Keep credentials outside Git and mount the portal account's SSH directory read-only with an external Compose override:

services:
  rtsegmentator:
    volumes:
      - ${RTSEG_SSH_CONFIG_DIR}:/root/.ssh:ro

The directory must contain the private key (mode 0600), known_hosts, and an SSH config entry resolving the worker alias, port and identity. Populate host keys through your normal verification process; do not disable host-key checking. The application uses batch SSH and does not prompt for passwords. Restart the portal after changing route configuration. Enabling remote routing does not itself install assets or bypass acceptance.