diff --git a/docs/docs/concepts/backends.md b/docs/docs/concepts/backends.md
index 620d5723cb..dcb43387ed 100644
--- a/docs/docs/concepts/backends.md
+++ b/docs/docs/concepts/backends.md
@@ -1211,4 +1211,23 @@ projects:
+??? info "Community Cloud"
+ By default, `dstack` includes both Server Cloud (datacenter) and Community Cloud offers.
+ To restrict offers to Server Cloud only, set `community_cloud: false` in the backend settings.
+
+
+
+ ```yaml
+ projects:
+ - name: main
+ backends:
+ - type: vastai
+ creds:
+ type: api_key
+ api_key: d75789f22f1908e0527c78a283b523dd73051c8c7d05456516fc91e9d4efd8c5
+ community_cloud: false
+ ```
+
+
+
Also, the `vastai` backend supports on-demand instances only. Spot instance support coming soon.
diff --git a/pyproject.toml b/pyproject.toml
index 259cbf7b25..55df6aa3d8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -80,6 +80,7 @@ ignore-case = true
[tool.uv.sources]
dstack-plugin-server = { path = "examples/plugins/example_plugin_server", editable = true }
+gpuhunt = { git = "https://github.com/dstackai/gpuhunt.git", branch = "feat/vastai-community-default" }
[tool.ruff]
target-version = "py39"
diff --git a/src/dstack/_internal/core/backends/vastai/compute.py b/src/dstack/_internal/core/backends/vastai/compute.py
index abd0ee5bb6..254da38e39 100644
--- a/src/dstack/_internal/core/backends/vastai/compute.py
+++ b/src/dstack/_internal/core/backends/vastai/compute.py
@@ -43,6 +43,7 @@ def __init__(self, config: VastAIConfig):
self.catalog = gpuhunt.Catalog(balance_resources=False, auto_reload=False)
self.catalog.add_provider(
VastAIProvider(
+ community_cloud=config.allow_community_cloud,
extra_filters={
"direct_port_count": {"gte": 1},
"reliability2": {"gte": 0.9},
@@ -50,7 +51,7 @@ def __init__(self, config: VastAIConfig):
"verified": {"eq": True},
"cuda_max_good": {"gte": 12.8},
"compute_cap": {"gte": 600},
- }
+ },
)
)
diff --git a/src/dstack/_internal/core/backends/vastai/models.py b/src/dstack/_internal/core/backends/vastai/models.py
index a9df8c9c5c..c712bce51b 100644
--- a/src/dstack/_internal/core/backends/vastai/models.py
+++ b/src/dstack/_internal/core/backends/vastai/models.py
@@ -4,6 +4,10 @@
from dstack._internal.core.models.common import CoreModel
+# TODO: Re-evaluate this default once Vast Server Cloud inventory improves for
+# CUDA-sensitive GPU families (e.g. H100 with strict cuda_max_good filtering).
+VASTAI_COMMUNITY_CLOUD_DEFAULT = True
+
class VastAIAPIKeyCreds(CoreModel):
type: Annotated[Literal["api_key"], Field(description="The type of credentials")] = "api_key"
@@ -20,6 +24,15 @@ class VastAIBackendConfig(CoreModel):
Optional[List[str]],
Field(description="The list of VastAI regions. Omit to use all regions"),
] = None
+ community_cloud: Annotated[
+ Optional[bool],
+ Field(
+ description=(
+ "Whether Community Cloud offers can be suggested in addition to Server Cloud."
+ f" Defaults to `{str(VASTAI_COMMUNITY_CLOUD_DEFAULT).lower()}`"
+ )
+ ),
+ ] = None
class VastAIBackendConfigWithCreds(VastAIBackendConfig):
@@ -35,3 +48,9 @@ class VastAIStoredConfig(VastAIBackendConfig):
class VastAIConfig(VastAIStoredConfig):
creds: AnyVastAICreds
+
+ @property
+ def allow_community_cloud(self) -> bool:
+ if self.community_cloud is not None:
+ return self.community_cloud
+ return VASTAI_COMMUNITY_CLOUD_DEFAULT
diff --git a/src/tests/_internal/core/backends/vastai/test_compute.py b/src/tests/_internal/core/backends/vastai/test_compute.py
new file mode 100644
index 0000000000..e6f5c0a5c8
--- /dev/null
+++ b/src/tests/_internal/core/backends/vastai/test_compute.py
@@ -0,0 +1,44 @@
+from unittest.mock import patch
+
+from dstack._internal.core.backends.vastai.compute import VastAICompute
+from dstack._internal.core.backends.vastai.models import VastAIConfig, VastAICreds
+
+
+def _config(community_cloud=None) -> VastAIConfig:
+ return VastAIConfig(creds=VastAICreds(api_key="test"), community_cloud=community_cloud)
+
+
+def test_vastai_compute_enables_community_cloud_by_default():
+ with (
+ patch("dstack._internal.core.backends.vastai.compute.VastAIProvider") as vast_provider_cls,
+ patch("dstack._internal.core.backends.vastai.compute.gpuhunt.Catalog") as catalog_cls,
+ ):
+ catalog_instance = catalog_cls.return_value
+ VastAICompute(_config())
+ vast_provider_cls.assert_called_once()
+ assert vast_provider_cls.call_args.kwargs["community_cloud"] is True
+ catalog_instance.add_provider.assert_called_once()
+
+
+def test_vastai_compute_can_enable_community_cloud():
+ with (
+ patch("dstack._internal.core.backends.vastai.compute.VastAIProvider") as vast_provider_cls,
+ patch("dstack._internal.core.backends.vastai.compute.gpuhunt.Catalog") as catalog_cls,
+ ):
+ catalog_instance = catalog_cls.return_value
+ VastAICompute(_config(community_cloud=True))
+ vast_provider_cls.assert_called_once()
+ assert vast_provider_cls.call_args.kwargs["community_cloud"] is True
+ catalog_instance.add_provider.assert_called_once()
+
+
+def test_vastai_compute_can_disable_community_cloud():
+ with (
+ patch("dstack._internal.core.backends.vastai.compute.VastAIProvider") as vast_provider_cls,
+ patch("dstack._internal.core.backends.vastai.compute.gpuhunt.Catalog") as catalog_cls,
+ ):
+ catalog_instance = catalog_cls.return_value
+ VastAICompute(_config(community_cloud=False))
+ vast_provider_cls.assert_called_once()
+ assert vast_provider_cls.call_args.kwargs["community_cloud"] is False
+ catalog_instance.add_provider.assert_called_once()
diff --git a/src/tests/_internal/core/backends/vastai/test_configurator.py b/src/tests/_internal/core/backends/vastai/test_configurator.py
index 47c9a523ca..16c3c82edb 100644
--- a/src/tests/_internal/core/backends/vastai/test_configurator.py
+++ b/src/tests/_internal/core/backends/vastai/test_configurator.py
@@ -8,6 +8,20 @@
class TestVastAIConfigurator:
+ def test_allow_community_cloud_default(self):
+ config = VastAIBackendConfigWithCreds(creds=VastAICreds(api_key="valid"))
+ backend = VastAIConfigurator().create_backend(project_name="main", config=config)
+ loaded_config = VastAIConfigurator()._get_config(backend)
+ assert loaded_config.allow_community_cloud is True
+
+ def test_allow_community_cloud_enabled(self):
+ config = VastAIBackendConfigWithCreds(
+ creds=VastAICreds(api_key="valid"), community_cloud=True
+ )
+ backend = VastAIConfigurator().create_backend(project_name="main", config=config)
+ loaded_config = VastAIConfigurator()._get_config(backend)
+ assert loaded_config.allow_community_cloud is True
+
def test_validate_config_valid(self):
config = VastAIBackendConfigWithCreds(
creds=VastAICreds(api_key="valid"),