Cloud targets

Group accounts into an organization Cloud, name targets, grant them to projects, and place services by name or tag.

Create an organization Cloud before you create a project. Add named targets, grant a project access, then assign the Cloud to an environment. A Cloud belongs to one organization. A target selects either a managed provider account or a server pool. Your manifest can then say frontend without carrying an account ID.

version: 1
services:
  web:
    source: node:apps/web
    cloud_target: frontend

Create and grant

Use the IDs these commands return:

loomctl clouds create --organization --name Production
loomctl cloud-targets create cloud_... --organization \
  --name frontend --provider vercel --connection conn_...
# Once your project exists, grant all targets or select them with --targets.
loomctl cloud-grants grant cloud_... project_... --targets target_...
loomctl clouds assignment my-project env_...
loomctl clouds assign my-project env_... --cloud cloud_... --revision 0
loomctl clouds assign my-project env_... --cloud cloud_... --revision 0 \
  --yes --request-id assign-production --wait --timeout 10m

You can list, create, rename and delete organization Clouds with --organization and no project argument. The same flag works on cloud-targets and cloud-syncs. The project forms still work too, such as clouds create my-project --name Production. Assignment always needs a project.

loomctl clouds --organization
loomctl clouds show cloud_... --organization
loomctl cloud-grants cloud_...
loomctl cloud-grants revoke cloud_... project_...
loomctl cloud-syncs --organization

An empty --targets grants the whole Cloud, including its default placement. A target list grants only those. Revoking prevents fresh placement but keeps the authority needed to clean up work you already accepted. Sharing capacity doesn't grant another project's releases, variables, logs or routes.

The first assignment uses revision 0. Read clouds assignment before later changes, because unassigning keeps the generation. An unassigned environment can have a nonzero revision, and you need it to assign again. An environment from another project returns a scoped error, never an empty assignment.

Pick a target by tags

You don't have to name a target. Give your targets tags, then ask for the tags you need:

loomctl cloud-targets create my-project cloud_... \
  --name eu-gpu --provider cloud-run --connection conn_... --tags eu,gpu
loomctl cloud-targets update my-project cloud_... target_... \
  --display-name "EU GPU" --tags eu,gpu,large --revision 1
version: 1
defaults:
  cloud_tags: [eu]
services:
  web:
    source: node:apps/web
    cloud_tags: [frontend, eu]
  trainer:
    source: python:trainer
    cloud_tags: [gpu]
    environments:
      staging: { cloud_target: shared-gpu }

A tag is a lowercase slug of up to 32 characters, and a target carries at most 16. A service resolves to the one target that carries every tag it lists, looking only at the targets your project's grant allows. If none match, the plan fails with cloud-tag-unmatched. If several do, it fails with cloud-tag-ambiguous and names each, so you can add a tag only one carries or name the target with cloud_target.

cloud_target and cloud_tags can't sit together at the same level. An environment override that sets one replaces whichever the service set, so staging above uses shared-gpu by name. defaults.cloud_tags applies to every service that sets no cloud_target, cloud_tags, provider or target of its own, and it can't be combined with defaults.provider. Like cloud_target, tags conflict with an effective direct provider or target, including an inherited one.

Over HTTP, send tags when you create or update a target. Leave tags out of an update and they stay. Send [] and they're removed (--clear-tags in loomctl). Changing a target's tags advances its revision, so a plan approved against the old ones has to be planned again.

A default placement without targets

A Cloud can have a default placement: one provider, one connection and an optional region. While a Cloud has no targets, that default is the only place its services go. Its connection has to be ready and belong to that provider's cloud.

curl -X PUT .../api/v1/clouds/cloud_.../default-placement \
  -d '{"default_placement":{"provider":"vercel","connection_id":"conn_...","region":"iad1"},"revision":1}'

You can also pass default_placement when you create the Cloud, and send "default_placement": null to clear it. A service uses the default when it has no placement of its own. That keeps working after you add targets: adding a target only places the services that name or tag it. A service with its own provider keeps its direct placement.

The default reaches a project only when its grant covers the whole Cloud. If an admin granted a chosen set of targets, your services can use those targets but not the default.

You can set a first default at any time. Once an environment uses the Cloud, default-placement refuses to replace or clear it (HTTP 409). Change it with a reviewed switch, described in Adopting and moving services.

On this page