Serve on One GPU
This page runs the ReactantServer container image on a GPU host, serves a model from a directory of bundles, and queries it. The image is the supported way to deploy: it carries Julia, every package with its precompiled caches, and the CUDA libraries Reactant needs, so the host needs only an NVIDIA driver and a container runtime.
What you need
- An NVIDIA GPU with its driver installed.
- podman or Docker, with the NVIDIA Container Toolkit so containers can see the GPU. Podman reads GPUs through CDI: generate the spec once with
sudo nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml. - A model repository: a directory whose subdirectories are bundles. Export a Model writes one,
models/mlp, in a few lines of Julia on any machine; copymodels/to the GPU host.
Run the container
The image is published as ghcr.io/enzymead/reactantserver, tagged with each release. Mount the model repository and publish the two ports:
podman run -d --name reactantserver \
--device nvidia.com/gpu=0 \
--ipc=host --pids-limit=-1 \
-p 8001:8001 -p 8002:8002 \
-v $PWD/models:/var/lib/reactantserver/models \
-v reactant-compile-cache:/var/cache/reactant-compile \
--stop-timeout 30 \
ghcr.io/enzymead/reactantserver:0.1With Docker, replace --device nvidia.com/gpu=0 with --gpus device=0; the other flags are the same. In order:
--device nvidia.com/gpu=0grants one GPU; the node runs one worker per GPU it is granted.--ipc=hostshares the host's/dev/shm, so a client on the host can use shared memory.--pids-limit=-1lifts podman's thread limit, which a worker can cross while loading models.- The model repository is mounted writable, because each bundle caches its compiled programs.
- The
reactant-compile-cachevolume keeps XLA's autotuning results across restarts. --stop-timeout 30gives the workers time to drain on stop.
Container settings in Deployment explains each one and the alternatives when --ipc=host is not an option.
Wait for it to be ready
On its first start the worker compiles every model before it accepts traffic, which takes from seconds for a small model to much longer for a large set. Follow the logs, and poll /readyz until it answers 200:
podman logs -f reactantserver
curl -sf http://127.0.0.1:8002/readyz && echo readyWith one GPU the node runs a single worker on the public ports and no gateway: KServe V2 gRPC on 8001, and /readyz, /healthz, and Prometheus /metrics on 8002.
Query it
Any KServe V2 or Triton gRPC client works. From Julia, with ReactantServerClient installed on any machine that can reach the host:
using ReactantServerClientkserve_init()mlp = KServeModel("grpc://127.0.0.1:8001", "mlp")x = reshape(Float32[1, 2, 3, 4], 4, 1) # (features, batch)y = InferOutput("output", infer_sync(mlp, [InferInput("input", x)]), Float32)kserve_shutdown()Run Inference covers batched inference over a dataset and the shared-memory transport.
Configure the node
The image runs /etc/reactantserver/node.yaml, a node file that serves everything in the model repository with one worker per visible GPU. To change it, mount your own file over that path:
# node.yaml
model_repo: /var/lib/reactantserver/models
base_port: 8080
metrics_base_port: 9100
gpus: auto
global:
runtime:
backend: cuda
endpoints:
host: 0.0.0.0podman run ... -v $PWD/node.yaml:/etc/reactantserver/node.yaml:ro ghcr.io/enzymead/reactantserver:0.1Any key can also be overridden from the environment with -e, for example -e INFERENCE_SERVER_SCHEDULER_DISCIPLINE=fifo. Node Configuration covers every key and its environment variable.
Stop
podman stop reactantserverThe supervisor drains its workers on SIGTERM before exiting.
Next
- Scale to Many GPUs: the same image and node file on a multi-GPU host.
- Deployment: the image's tags and provenance, every container setting, health checks, and building the image yourself.
- Monitoring: scraping the node's metrics.
- Security: what to know before exposing an endpoint.