Ray is a unified framework for scaling AI and Python applications from a laptop to a cluster. It pairs a general-purpose distributed runtime (Ray Core) with a set of AI libraries — Data, Train, Tune, Serve, and RLlib — that build on top of it. If your workload is written in Python, you can scale it with Ray without standing up any other infrastructure.
This guide shows you how to install Ray, write your first distributed program,
use each of the libraries, drive the ray command-line tool, submit jobs to a
cluster, and troubleshoot the problems you are most likely to hit. Deep
architecture, the full configuration reference, deployment, and security
hardening live in the companion Admin Guide.
Note
Ray's three core abstractions are Tasks (stateless remote functions), Actors (stateful remote worker processes), and Objects (immutable values stored in a distributed object store). Almost everything else in Ray — including all five libraries — is built from these three primitives.
1. Quick Start
Install Ray and run a small program that distributes work across all the cores on your machine.
1.1 Install
pip install -U "ray[default]"
Successfully installed ray-2.x.x ...
Tip
ray[default]includes the dashboard and cluster launcher, which you almost always want during development. The barepip install rayomits them. See Installation for the full list of install options.
1.2 Your first distributed program
Create quickstart.py:
import ray
# Start a local Ray instance (or connect to an existing one).
ray.init()
# A "task": a stateless function that runs somewhere in the cluster.
@ray.remote
def square(x):
return x * x
# Launch four tasks in parallel. Each call returns immediately with an
# ObjectRef (a future), not the result.
futures = [square.remote(i) for i in range(4)]
# Block until all results are ready, then fetch them.
print(ray.get(futures))
Run it:
python quickstart.py
[0, 1, 4, 9]
The four square.remote(i) calls are scheduled across your CPU cores and run
concurrently. ray.get() blocks until the results are available and returns
them in input order.
Important
@ray.remotedoes not run the function. Callingsquare.remote(i)schedules it and instantly returns anObjectRef. The work happens asynchronously; you retrieve the value withray.get().
1.3 Add a stateful actor
Actors are classes whose instances live as long-running processes in the cluster and keep state between calls:
import ray
ray.init()
@ray.remote
class Counter:
def __init__(self):
self.n = 0
def increment(self):
self.n += 1
return self.n
# Create one actor process in the cluster.
counter = Counter.remote()
# Method calls are remote and return ObjectRefs.
results = ray.get([counter.increment.remote() for _ in range(5)])
print(results)
[1, 2, 3, 4, 5]
Because the calls all target the same actor, state (self.n) accumulates.
2. Installation
Ray ships as a single PyPI package with optional extras that pull in the dependencies for each library. Install only what you need.
Core + dashboard
pip install -U "ray[default]"
A specific library
pip install -U "ray[data]" # Ray Data
pip install -U "ray[train]" # Ray Train
pip install -U "ray[tune]" # Ray Tune
pip install -U "ray[serve]" # Ray Serve
pip install -U "ray[rllib]" # RLlib
pip install -U "ray[llm]" # Ray LLM (serving + batch inference)
Everything
pip install -U "ray[all]"
2.1 Install options (extras)
Every extra name below is defined in Ray's setup.py and can be combined,
e.g. pip install "ray[data,serve]".
| Extra | Installs |
|---|---|
| (none) | Ray Core runtime + ray CLI only |
default |
Core + dashboard + cluster launcher + job submission |
data |
Ray Data (datasets, batch processing) |
train |
Ray Train (distributed training); includes tune |
tune |
Ray Tune (hyperparameter tuning) |
serve |
Ray Serve (online model/app serving) |
serve-grpc |
Ray Serve with gRPC ingress |
serve-async-inference |
Ray Serve with async (Celery/Redis) inference |
rllib |
RLlib (reinforcement learning); includes tune |
air |
Ray AI Runtime meta-extra (tune + data + train + serve) |
llm |
Ray LLM (vLLM/SGLang serving + batch); includes data + serve |
client |
Ray Client (ray:// remote connections) |
cpp |
Ray C++ API |
all |
All Python libraries above |
all-cpp |
all plus the C++ API |
Note
The base dependencies of every install include
click,msgpack,protobuf,pyyaml,requests,filelock,jsonschema,watchfiles, andgrpcio. Library extras add the heavier dependencies (pyarrow,numpy,pandas,fastapi,gymnasium, and so on).
2.2 Requirements
- Python: a currently supported CPython release (3.9+).
- Operating system: Linux and macOS are fully supported. Windows is
supported with some limitations (for example,
memray-based profiling is unavailable on Windows). - GPUs are optional. Ray detects GPUs automatically; use them by requesting
num_gpuson tasks and actors (see Section 5.4).
2.3 Verify the installation
python -c "import ray; ray.init(); print(ray.cluster_resources()); ray.shutdown()"
{'CPU': 8.0, 'node:127.0.0.1': 1.0, 'object_store_memory': 2147483648.0, 'memory': 4294967296.0}
Tip
There is also a hidden self-check,
ray sanity-check, that confirms the installation can start and stop a local instance.
3. Core Concepts
These three abstractions are the foundation of everything in Ray.
3.1 Tasks — stateless remote functions
Decorate a function with @ray.remote and call it with .remote():
@ray.remote
def add(a, b):
return a + b
ref = add.remote(2, 3) # returns an ObjectRef immediately
print(ray.get(ref)) # 5
Tasks are stateless: each invocation is independent and may run on any worker in the cluster.
3.2 Actors — stateful remote classes
Decorate a class with @ray.remote. Each .remote() constructor call creates a
dedicated worker process that holds the instance's state:
@ray.remote
class Accumulator:
def __init__(self):
self.total = 0
def add(self, x):
self.total += x
return self.total
acc = Accumulator.remote()
ray.get(acc.add.remote(10)) # 10
ray.get(acc.add.remote(5)) # 15
Tip
Use a task when work is independent and stateless (embarrassingly parallel jobs, map operations). Use an actor when you need to keep state, hold a resource (a model in GPU memory, a database connection), or serialize access to something.
3.3 Objects — the distributed object store
Every .remote() call returns an ObjectRef, a reference to a value that lives
in Ray's distributed, shared-memory object store. You can also place a value
into the store directly with ray.put():
import numpy as np
big_array = np.zeros(10_000_000)
ref = ray.put(big_array) # store once
@ray.remote
def total(arr):
return arr.sum()
# Pass the ref to many tasks; the array is shared, not re-serialized per call.
print(ray.get([total.remote(ref) for _ in range(8)]))
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
Important
Objects in the store are immutable. Sharing a single
ObjectRefacross many tasks avoids repeatedly copying large arguments — this is the single most effective Ray performance pattern for large data.
3.4 Waiting for results: ray.get and ray.wait
ray.get(refs) blocks until all refs are ready. When you want to process
results as they finish, use ray.wait():
refs = [slow_task.remote(i) for i in range(100)]
while refs:
# Return as soon as one task is done.
ready, refs = ray.wait(refs, num_returns=1)
result = ray.get(ready[0])
handle(result)
| Function | Signature (key arguments) | Behavior |
|---|---|---|
ray.get |
ray.get(refs, *, timeout=None) |
Block for all refs; raises GetTimeoutError if timeout elapses |
ray.wait |
ray.wait(refs, *, num_returns=1, timeout=None, fetch_local=True) |
Return (ready, not_ready) once num_returns are done or timeout elapses |
ray.put |
ray.put(value) |
Store a value, return its ObjectRef |
Warning
Avoid calling
ray.get()inside a loop that also submits work — it serializes your program by blocking on each result. Submit all the tasks first, thenray.get()the list (or drain withray.wait()).
4. Everyday Usage
4.1 Starting and connecting
ray.init() is the entry point. With no arguments it auto-detects a running
cluster (via the RAY_ADDRESS environment variable or a local instance) and
otherwise starts a new local instance.
ray.init() # local instance or auto-detect
ray.init(address="auto") # attach to local cluster, else error
ray.init(address="ray://1.2.3.4:10001") # connect to a remote cluster (needs ray[client])
ray.init(num_cpus=4, num_gpus=1) # cap resources for a local instance
ray.init(ignore_reinit_error=True) # don't error if already initialized
Common ray.init() arguments:
| Argument | Purpose |
|---|---|
address |
"auto", "local", host:port, or ray://host:port |
num_cpus, num_gpus |
Resources to advertise for a local instance |
resources |
Dict of custom resource → quantity |
object_store_memory |
Bytes for the local object store |
runtime_env |
Per-job dependencies/working directory (see Section 6) |
namespace |
Logical grouping for named actors |
ignore_reinit_error |
Suppress the error when ray.init() is called twice |
include_dashboard, dashboard_host, dashboard_port |
Dashboard controls |
logging_level, logging_config |
Driver/worker logging |
Always release resources when you are done (especially in notebooks/tests):
ray.shutdown()
4.2 Inspecting the cluster
ray.is_initialized() # True/False
ray.cluster_resources() # total CPUs/GPUs/memory across all nodes
ray.available_resources() # currently free resources
ray.nodes() # list of node records (alive/dead, resources, address)
ray.get_gpu_ids() # GPU IDs assigned to the current worker
4.3 Chaining tasks
ObjectRefs can be passed directly as arguments to other remote calls. Ray
resolves the dependency automatically and only runs the dependent task once its
inputs are ready:
@ray.remote
def load():
return list(range(100))
@ray.remote
def process(data):
return sum(data)
# No ray.get() needed in between — Ray wires up the dependency.
result_ref = process.remote(load.remote())
print(ray.get(result_ref)) # 4950
4.4 Nested tasks and the driver
Tasks and actors can themselves submit more tasks. A common pattern is a "driver → workers" tree where one task fans out work and aggregates results.
Caution
Do not call
ray.get()on a result you are also waiting on from within the same task in a way that blocks a worker indefinitely — this can deadlock when the cluster runs out of workers. Prefer returningObjectRefs and letting the caller resolve them.
5. Configuring Tasks and Actors
Pass resource and behavior options either to the decorator or, per call, with
.options().
5.1 Decorator vs. .options()
# Set defaults on the decorator:
@ray.remote(num_cpus=2, num_gpus=1, max_retries=3)
def train_shard(shard):
...
# Override per invocation:
train_shard.options(num_gpus=0, name="cpu_fallback").remote(shard)
5.2 Options reference
All options below are accepted by both @ray.remote(...) and .options(...).
Common (tasks and actors):
| Option | Meaning |
|---|---|
num_cpus |
CPU cores to reserve (default 1 for tasks, 0 for actors) |
num_gpus |
GPUs to reserve |
memory |
Heap memory (bytes) to reserve |
object_store_memory |
Object-store memory to reserve |
resources |
Dict of custom resource requirements |
accelerator_type |
Require a specific accelerator (e.g. an NVIDIA A100) |
runtime_env |
Per-task/actor runtime environment |
scheduling_strategy |
"DEFAULT", "SPREAD", or a placement-group strategy |
placement_group, placement_group_bundle_index, placement_group_capture_child_tasks |
Gang-scheduling placement |
enable_task_events |
Emit task events for observability (default on) |
label_selector |
Schedule onto nodes matching labels |
Tasks only:
| Option | Meaning |
|---|---|
num_returns |
Number of return values (or "dynamic" / a generator) |
max_retries |
Times to retry on worker failure (default 3) |
retry_exceptions |
Retry on application exceptions, or a list of exception types |
max_calls |
Restart the worker after this many calls (frees leaked memory) |
Actors only:
| Option | Meaning |
|---|---|
max_restarts |
Times to restart the actor on failure (-1 = unlimited) |
max_task_retries |
Retries for the actor's method calls |
max_concurrency |
Concurrent method executions (threads/async) |
max_pending_calls |
Back-pressure limit on queued calls |
name, namespace |
Register a named actor for later lookup |
lifetime |
"detached" keeps the actor alive beyond the driver |
get_if_exists |
Return the existing named actor instead of erroring |
concurrency_groups |
Per-method concurrency pools |
5.3 Named and detached actors
Register an actor by name and retrieve it from anywhere in the cluster:
Counter.options(name="global_counter", lifetime="detached").remote()
# Elsewhere — even in another driver/job:
counter = ray.get_actor("global_counter")
ray.get(counter.increment.remote())
Note
A
"detached"actor survives after the script that created it exits. You are responsible for cleaning it up withray.kill(handle)when it is no longer needed.
5.4 Request CPUs, GPUs, and custom resources
@ray.remote(num_gpus=1)
def infer(batch):
import torch
assert torch.cuda.is_available()
...
# Custom resources let you pin work to specific hardware:
@ray.remote(resources={"tpu": 1})
def tpu_job():
...
Custom resources are advertised when you start a node (ray start --resources)
or call ray.init(resources=...).
5.5 Cancelling and killing
ref = long_task.remote()
ray.cancel(ref) # cancel a task
actor = MyActor.remote()
ray.kill(actor) # terminate an actor immediately
6. Runtime Environments
A runtime environment specifies the dependencies and working directory a
job, task, or actor needs — installed on the fly, without baking a custom
container. Pass it to ray.init(), the decorator, or .options().
ray.init(runtime_env={
"working_dir": "./my_project", # upload this directory to workers
"pip": ["requests==2.31.0", "pandas"], # install these packages
"env_vars": {"MY_FLAG": "1"},
})
@ray.remote(runtime_env={"pip": ["scipy"]})
def needs_scipy():
import scipy
return scipy.__version__
Common runtime_env keys:
| Key | Purpose |
|---|---|
working_dir |
Local dir or remote .zip URI uploaded to every worker |
py_modules |
Local Python modules to ship to workers |
pip / conda |
Packages or a requirements/environment file to install |
uv |
Dependencies resolved/installed with uv |
env_vars |
Environment variables to set in workers |
excludes |
Glob patterns to omit when uploading working_dir |
Tip
Runtime environments are the recommended way to ship your code and dependencies to a remote cluster during development. For production, bake heavy dependencies into the container image and use
runtime_envonly for your application code and light packages.
7. The Ray Command-Line Tool
Installing Ray puts the ray command on your PATH. Run ray --help or
ray <command> --help for full details. The commands below are grouped by what
you use them for.
7.1 Running Ray on the local machine
| Command | What it does |
|---|---|
ray start --head |
Start a head node (a one-machine cluster) |
ray start --address=<head_ip:port> |
Join this machine to an existing cluster as a worker |
ray stop |
Stop all Ray processes on this machine |
ray status |
Show cluster status and autoscaling info |
ray memory |
List object references held across the cluster |
ray stack |
Dump Python stacks of all local workers (debugging) |
ray timeline |
Export a Chrome tracing timeline of cluster events |
ray microbenchmark |
Run Ray's built-in performance benchmark |
Start a local multi-node cluster by hand:
# On the head machine:
ray start --head --port=6379
# On each worker machine:
ray start --address='<head-ip>:6379'
Local node IP: <head-ip>
--------------------
Ray runtime started.
--------------------
To connect to this Ray cluster: import ray; ray.init()
Useful ray start flags (run ray start --help for the full ~45):
| Flag | Purpose |
|---|---|
--head |
Make this the head node |
--address |
Address of the head to join |
--port |
Head (GCS) port; default 6379, 0 = auto |
--num-cpus, --num-gpus |
Override detected resources |
--resources |
JSON dict of custom resources |
--object-store-memory |
Object-store size in bytes |
--include-dashboard, --dashboard-host, --dashboard-port |
Dashboard controls (default port 8265) |
--temp-dir |
Root temp/log directory |
--block |
Run in the foreground (useful in containers/systemd) |
--disable-usage-stats |
Opt out of anonymous usage telemetry |
Warning
--dashboard-hostdefaults tolocalhost. Setting it to0.0.0.0exposes the dashboard on all network interfaces. Only do this behind a firewall or VPN — the dashboard and Ray ports give code-execution access to the cluster. See the Admin Guide's security section.
7.2 Stopping Ray
ray stop # graceful stop
ray stop --force # SIGKILL everything immediately
ray stop --grace-period 30 # wait up to 30s before force-killing
7.3 Launching cloud/VM clusters
The cluster launcher provisions and manages clusters from a YAML config (for AWS, GCP, Azure, vSphere, or on-prem). All of these take the cluster config file as their first argument.
| Command | What it does |
|---|---|
ray up <config.yaml> |
Create or update a cluster |
ray down <config.yaml> |
Tear down the cluster |
ray attach <config.yaml> |
Open an SSH session to the head node |
ray exec <config.yaml> <cmd> |
Run a shell command on the cluster |
ray submit <config.yaml> <script.py> |
Upload and run a Python script on the cluster |
ray monitor <config.yaml> |
Tail the autoscaler logs |
ray rsync-up / ray rsync-down |
Sync files to/from the cluster |
ray get-head-ip / ray get-worker-ips |
Print node IPs |
ray up cluster.yaml --yes
ray submit cluster.yaml train.py --start
ray down cluster.yaml --yes
Tip
ray submit ... --startbrings the cluster up first if it isn't already running. Add--tmuxor--screento keep the job running after you disconnect.
7.4 Observability commands (state CLI)
These query the live state of the cluster. They require ray[default].
| Command | What it does |
|---|---|
ray list <resource> |
List tasks, actors, nodes, jobs, objects, placement-groups, etc. |
ray get <resource> <id> |
Fetch one entity by ID |
ray summary tasks|actors|objects |
Aggregated summary view |
ray logs ... |
Stream cluster/actor/worker/job/task logs |
ray list actors --filter "state=ALIVE" --limit 20
ray list tasks --detail
ray summary tasks
ray logs actor --id <ACTOR_ID>
ray logs job --id <SUBMISSION_ID>
ray list supports --format, --filter, --limit, and --detail.
7.5 Diagnostics and dumps
| Command | What it does |
|---|---|
ray debug |
Open the interactive breakpoint debugger |
ray timeline |
Export a Chrome tracing file |
ray cluster-dump |
Collect logs/state from one or more nodes into an archive |
ray disable-usage-stats / ray enable-usage-stats |
Toggle anonymous telemetry |
8. Submitting Jobs
The Ray Jobs API is the recommended way to run a workload on a cluster without holding an interactive connection. A job packages your code and dependencies, runs an entrypoint command on the cluster, and lets you query its status and logs by ID.
8.1 From the command line
# Submit (uploads ./ as the working dir and runs the command on the cluster):
ray job submit --working-dir . -- python my_script.py
Job submission server address: http://127.0.0.1:8265
-------------------------------------------------------
Job 'raysubmit_abc123' submitted successfully
-------------------------------------------------------
Query the logs of the job: ray job logs raysubmit_abc123
Query the status of the job: ray job status raysubmit_abc123
ray job subcommands:
| Command | Purpose |
|---|---|
ray job submit [OPTIONS] -- <entrypoint> |
Submit a job |
ray job status <id> |
Current status (PENDING/RUNNING/SUCCEEDED/FAILED/STOPPED) |
ray job logs <id> [--follow] |
Print/stream job logs |
ray job stop <id> |
Request the job to stop |
ray job delete <id> |
Delete a stopped job's metadata |
ray job list |
List all jobs |
Key ray job submit options:
| Option | Purpose |
|---|---|
--address |
Job server address (default http://localhost:8265, or RAY_ADDRESS) |
--working-dir |
Directory to upload as the job's working dir |
--runtime-env / --runtime-env-json |
Runtime env file or inline JSON |
--submission-id |
Choose your own job ID |
--metadata-json |
Attach metadata to the job |
--entrypoint-num-cpus / --entrypoint-num-gpus / --entrypoint-memory / --entrypoint-resources |
Resources for the entrypoint command itself |
--no-wait |
Return immediately instead of tailing logs |
Important
The job server is the Ray dashboard address (default port
8265), not the GCS port6379. SetRAY_ADDRESS=http://<head>:8265to avoid passing--addressevery time.
8.2 From Python
from ray.job_submission import JobSubmissionClient
client = JobSubmissionClient("http://127.0.0.1:8265")
job_id = client.submit_job(
entrypoint="python my_script.py",
runtime_env={"working_dir": "./", "pip": ["requests"]},
)
print(client.get_job_status(job_id))
print(client.get_job_logs(job_id))
9. The AI Libraries
Each library is a Pythonic API that builds on Ray Core. Install the matching extra (see Section 2.1).
9.1 Ray Data — scalable datasets
Distributed, streaming data loading and transformation for ML.
import ray
ds = ray.data.read_parquet("s3://bucket/data/")
ds = ds.map_batches(lambda batch: preprocess(batch), batch_size=1024)
ds.write_parquet("s3://bucket/out/")
# Stream batches into training:
for batch in ds.iter_batches(batch_size=256):
train_step(batch)
Use it for: ETL, batch inference, and feeding distributed training. It reads from Parquet, CSV, JSON, images, Lance, Hudi, and many other sources, and executes lazily as a streaming pipeline.
9.2 Ray Train — distributed training
Scale PyTorch, Lightning, Hugging Face Transformers, XGBoost, and more across a cluster.
from ray.train import ScalingConfig
from ray.train.torch import TorchTrainer
def train_loop_per_worker(config):
# Standard PyTorch loop; Ray Train handles distribution.
...
trainer = TorchTrainer(
train_loop_per_worker,
scaling_config=ScalingConfig(num_workers=4, use_gpu=True),
)
result = trainer.fit()
print(result.metrics)
Ray Train manages worker groups, fault tolerance and checkpointing, and integrates with Ray Data for sharded input.
9.3 Ray Tune — hyperparameter tuning
Run and orchestrate large hyperparameter searches.
from ray import tune
def objective(config):
score = train_model(lr=config["lr"])
tune.report({"score": score})
tuner = tune.Tuner(
objective,
param_space={"lr": tune.grid_search([0.01, 0.001, 0.0001])},
)
results = tuner.fit()
print(results.get_best_result(metric="score", mode="max").config)
Tune provides search algorithms (Optuna, HyperOpt, BayesOpt, Ax, and more) and schedulers (ASHA, PBT) that stop or mutate trials during the search.
The standalone tune CLI is also installed:
| Command | Purpose |
|---|---|
tune list-trials <experiment_path> |
List trials in a results directory |
tune list-experiments <project_path> |
List experiments under a directory |
tune add-note <path> |
Attach a notes file to an experiment |
9.4 Ray Serve — online serving
Build and scale online inference services and multi-model pipelines from Python.
from ray import serve
from starlette.requests import Request
@serve.deployment(num_replicas=2, ray_actor_options={"num_gpus": 1})
class Model:
def __init__(self):
self.model = load_model()
async def __call__(self, request: Request):
data = await request.json()
return self.model.predict(data["input"])
serve.run(Model.bind())
Drive Serve with the serve CLI (also installed as ray serve):
| Command | Purpose |
|---|---|
serve run <import_path> |
Run an application locally (supports --reload, --route-prefix) |
serve deploy <config.yaml> |
Deploy from a config to a running cluster |
serve status |
Show application/deployment status |
serve config |
Print the running configuration |
serve build <import_paths> -o config.yaml |
Generate a deployable config schema |
serve shutdown --yes |
Shut down all Serve applications |
9.5 RLlib — reinforcement learning
A scalable RL library with implementations of PPO, DQN, SAC, IMPALA, APPO, DreamerV3, and more.
from ray.rllib.algorithms.ppo import PPOConfig
config = (
PPOConfig()
.environment("CartPole-v1")
.env_runners(num_env_runners=4)
)
algo = config.build()
for _ in range(5):
print(algo.train()["env_runners"]["episode_return_mean"])
9.6 Ray LLM — LLM serving and batch inference
ray[llm] adds first-class LLM support: deploy LLMs with Ray Serve (backed by
vLLM or SGLang) via ray.serve.llm, and run large-scale batch inference with
ray.data.llm.
from ray import serve
from ray.serve.llm import LLMConfig, build_openai_app
llm_config = LLMConfig(
model_loading_config={"model_id": "meta-llama/Llama-3.1-8B-Instruct"},
)
app = build_openai_app({"llm_configs": [llm_config]})
serve.run(app) # serves an OpenAI-compatible endpoint
10. The Ray Dashboard
When you start Ray with ray[default], a web dashboard runs on the head node
(default http://127.0.0.1:8265).
It shows:
- Cluster view — nodes, CPU/GPU/memory utilization, autoscaler activity.
- Jobs view — submitted jobs, their status, and logs.
- Actors / Tasks — live and historical task and actor state.
- Logs — searchable logs across all nodes.
- Metrics — time-series charts (with Prometheus/Grafana integration).
- Serve / Data / Train — per-library dashboards.
Open it locally:
ray start --head # then browse to http://127.0.0.1:8265
For a remote cluster, forward the port over SSH (the cluster launcher's
ray dashboard <config.yaml> does this for you).
Caution
The dashboard provides job submission and log access. Never expose port
8265to the public internet. Keep it bound tolocalhostand reach it through SSH port-forwarding or a private network.
11. Troubleshooting
Format: the symptom, the cause, the fix.
Error: ConnectionError: Could not find any running Ray instance
Cause: You called ray.init(address="auto") (or set RAY_ADDRESS=auto) but
no cluster is running.
Solution: Start one with ray start --head, or call plain ray.init() to
start a local instance.
Prevention: Use ray.init() (no address) for local development; reserve
address="auto" for code meant to run on an existing cluster.
Error: RuntimeError: Maybe you called ray.init twice by accident?
Cause: ray.init() was called when Ray was already initialized (common in
notebooks and pytest).
Solution: Pass ray.init(ignore_reinit_error=True), or call
ray.shutdown() before re-initializing.
Error: ObjectStoreFullError: Failed to put object ... object store is full
Cause: The object store (shared memory) filled with in-use objects.
Solution: Hold fewer ObjectRefs at once, process results with ray.wait()
and let refs go out of scope, increase --object-store-memory, or configure
object spilling to disk (see Admin Guide).
Prevention: Don't accumulate large results in a list; stream them.
Error: GetTimeoutError
Cause: ray.get(refs, timeout=...) elapsed before the objects were ready.
Solution: Increase the timeout, or check the dashboard/ray status to see
whether tasks are pending for lack of resources.
Symptom: tasks never run / stay PENDING_NODE_ASSIGNMENT
Cause: No node satisfies the requested resources (e.g. you asked for
num_gpus=1 on a CPU-only cluster, or a custom resource that isn't advertised).
Solution: Run ray status to see available resources; lower the request or
start a node with the needed resources (ray start --num-gpus=...,
--resources=...).
Symptom: ray job submit cannot connect
Cause: Wrong address — the jobs API uses the dashboard port (8265),
not the GCS port (6379).
Solution: Use --address http://<head>:8265 or set
RAY_ADDRESS=http://<head>:8265.
Symptom: serialization error when calling .remote()
Cause: An argument or return value cannot be pickled (e.g. an open file handle, a thread lock, or a non-serializable client). Solution: Construct non-serializable resources inside the task/actor, not in the driver; pass plain data across the boundary.
Symptom: Ray Serve controller memory grows over time
Cause: A known controller RSS memory growth after repeated autoscaling cycles (community issue [#58815]). Solution: Track the upstream issue; as a mitigation, restart the Serve controller / reduce autoscaling churn until a fixed release is available.
Tip
The dashboard (
http://localhost:8265) and the state CLI (ray list tasks --filter "state=FAILED",ray logs ...) are the fastest way to find why a task failed — they surface the worker traceback directly.
12. Frequently Asked Questions
Q: Do I need a cluster to use Ray?
No. ray.init() starts a local instance that parallelizes across the cores of a
single machine. The same code scales to a multi-node cluster unchanged.
Q: What is the difference between a task and an actor? A task is a stateless function invocation that can run anywhere. An actor is a stateful, long-lived process. Use actors when you must keep state or hold a resource (a loaded model, a connection).
Q: Why does square.remote(2) return an ObjectRef instead of 4?
.remote() is asynchronous — it schedules the work and returns a future. Call
ray.get(ref) to block for the actual value.
Q: How do I run on more than one machine?
Start a head node (ray start --head), join workers
(ray start --address=<head:port>), then ray.init(address="auto") from the
head — or use the cluster launcher (ray up) on a cloud provider.
Q: How do I ship my code and dependencies to a remote cluster?
Use a runtime_env with working_dir and pip/conda/uv. For production,
bake heavy dependencies into the container image.
Q: How do I limit how much CPU/GPU a task uses?
Set num_cpus/num_gpus (and memory/resources) on @ray.remote or
.options(). These are scheduling reservations, not hard OS limits.
Q: Which library do I use for what? Data → loading/transforming/batch inference. Train → distributed model training. Tune → hyperparameter search. Serve → online inference/APIs. RLlib → reinforcement learning. LLM → serving and batch inference of large language models.
Q: How do I connect to a remote cluster from my laptop?
ray.init(address="ray://<head>:10001") with ray[client] installed, or submit
work as a job with ray job submit. Jobs are preferred for anything long-running.
Q: Is Ray secure to expose on the internet? No. Ray clusters assume a trusted network. Anyone who can reach the Ray ports or dashboard can run code on the cluster. Keep them on a private network. See the Admin Guide.
Q: How do I turn off the anonymous usage stats?
Run ray disable-usage-stats, pass ray start --disable-usage-stats, or set the
environment variable before starting Ray.
Q: How do I see why my job failed?
Open the dashboard's Jobs view, or use ray job logs <id>, ray list tasks --filter "state=FAILED" --detail, and ray logs ....