Skip to content

Nebius account onboarding with CTL

Use this guide when your hosted plan or BYOA plan deploys to Nebius.

Why Nebius is different

Nebius account configs are tenant-scoped. Instead of a single account-wide identity, Omnistrate expects one or more bindings inside the account config.

A binding is the combination of:

  • a Nebius project ID,
  • a Nebius service account ID,
  • a Nebius public key ID, and
  • the matching private key PEM.

Each binding enables a specific region in your Nebius tenant for deployment after Omnistrate verifies it and confirms the service-account key belongs to the configured tenant. In practice this means:

  • use one binding per Nebius project/region,
  • keep all existing bindings in the file when adding a new region, and
  • only deploy into regions whose bindings show READY in account describe.

One binding also acts as the artifact bucket owner for the account config. Omnistrate uses that binding's project for Nebius deployment artifacts (Helm charts, Terraform files, etc.). When an account config contains more than one binding, exactly one binding must own the artifact bucket.

Prerequisites

  • A Nebius tenant.
  • One Nebius project for each region you want to enable.
  • A human operator with enough Nebius IAM access to create service accounts and service-account authorized keys.
  • omnistrate-ctl installed and logged in.

Useful Nebius references:

Create the Nebius service account and key

Repeat this process for every Nebius project that you want to enable as a binding.

1. Create or choose the target project

Create one Nebius project per deployment region you want Omnistrate to use. Keep the project ID for the binding file.

2. Create a service account in that project

In Nebius Console:

  1. Open the target project.
  2. Go to IAM or Service Accounts.
  3. Create a service account such as omnistrate-eu-north1.
  4. Save the service account ID.

nebius service accounts

3. Grant the service account project permissions

Add the service account to a project-level group or role set that can create and manage the infrastructure Omnistrate needs in that project. A project-scoped admin-style role is required to allow Omnistrate to manage your infrastructure.

nebius sa group

4. Create an authorized key for the service account

Upload a public key and save the matching private key PEM for the binding file.

nebius authorized key

Save these values for the binding:

  • project ID
  • service account ID
  • public key ID
  • private key PEM

Warning

Treat the private key PEM like any other long-lived secret. Store it outside source control. The recommended pattern in this guide is to reference a local file from the bindings YAML.

Artifact bucket owner behavior

For Nebius, Omnistrate stores deployment artifacts in the project owned by one binding.

  • On the first account onboarding, if the bindings file does not specify an owner, Omnistrate picks the first binding in the file as the artifact-bucket owner.
  • On later full-replacement binding updates, Omnistrate preserves the existing artifact-bucket owner as long as that binding stays in the set.
  • If you remove the current artifact-owner binding from a later update, the update is rejected because Omnistrate would no longer know which Nebius project should keep artifact ownership.
  • In practice, put your most stable Nebius project first when you onboard the account for the first time.

Create the Nebius bindings file

A bindings file can carry one or more Nebius bindings. Omnistrate resolves the region during verification, so you do not specify a region in the YAML.

bindings:
  - projectID: project-e00project
    serviceAccountID: serviceaccount-e00serviceacct
    publicKeyID: publickey-e00publickey
    privateKeyPEMFile: /path/to/private-key.pem

  - projectID: project-e00anotherproject
    serviceAccountID: serviceaccount-e00anotherserviceacct
    publicKeyID: publickey-e00anotherpublickey
    privateKeyPEMFile: /path/to/another-private-key.pem

Notes:

  • privateKeyPEMFile is recommended.
  • privateKeyPEM also works if you must inline the key material.
  • On first onboarding, Omnistrate chooses the first binding in the file as the artifact-bucket owner.
  • Keep the file as the full desired binding set. Updates replace the complete set, not just one entry.
  • Keep the originally selected artifact-owner binding in later replacement files so artifact storage stays attached to the same Nebius project.

If your plan also provisions Nebius resources through Terraform, see Nebius Terraform authentication.

Hosted flow

1. Create the hosted account config

omnistrate-ctl account create nebius-hosted \
  --nebius-tenant-id tenant-id \
  --nebius-bindings-file ./nebius-bindings.yaml

Because Nebius verification is direct, you can let the command wait for READY. Add --skip-wait only if you want to return immediately.

2. Inspect binding verification

omnistrate-ctl account describe nebius-hosted
omnistrate-ctl account describe nebius-hosted -o json

nebius account describe

Only use regions whose bindings are READY.

3. Create the first Hosted SaaS deployment

omnistrate-ctl instance create \
  --service <service> \
  --environment <environment> \
  --plan <hosted-plan> \
  --resource <resource> \
  --cloud-provider nebius \
  --region eu-north1 \
  --param-file ./params.json

The --region value must match one of the ready binding regions surfaced by account describe.

BYOA flow

1. Create the customer onboarding instance

omnistrate-ctl account customer create \
  --service <service> \
  --environment <environment> \
  --plan <byoa-plan> \
  --nebius-tenant-id tenant-e00ezh17k22wmwq5f0 \
  --nebius-bindings-file ./nebius-bindings.yaml

The command uses the calling user's subscription by default. If you need to onboard on behalf of another production subscription, add either --subscription-id <subscription-id> or --customer-email <email>.

2. Verify the customer onboarding instance

omnistrate-ctl account customer list --cloud-provider nebius
omnistrate-ctl account customer describe <customer-account-instance-id>
omnistrate-ctl account customer describe <customer-account-instance-id> -o json

Use the JSON output if you want the backing accountConfigID and the raw Nebius binding verification details.

3. Create the BYOA deployment

omnistrate-ctl instance create \
  --service <service> \
  --environment <environment> \
  --plan <byoa-plan> \
  --resource <resource> \
  --cloud-provider nebius \
  --region eu-north1 \
  --customer-account-id <customer-account-instance-id> \
  --param-file ./params.json

For BYOA plans, --customer-account-id is the onboarding instance ID returned by account customer create.

Author Nebius plans

Nebius compute options

services:
  - name: vllm
    compute:
      instanceTypes:
        - name: 8gpu-128vcpu-1600gb
          cloudProvider: nebius
          platform: gpu-h100-sxm
          configurationOverrides:
            GpuClusterID: computegpucluster-e00abcxfadyhcvpkdx

Notes:

  • The instance type name is the Nebius preset. If you use apiParam instead, that parameter must resolve to a Nebius preset at deploy time.
  • platform is required on Nebius instance-type entries.
  • configurationOverrides.GpuClusterID is optional and Nebius-only. Use it when the preset must land in a specific Nebius GPU cluster.
  • Nebius GPU cluster placement is validated against the selected platform and preset.

Nebius shared file systems

Nebius plans can also use Omnistrate-managed shared file systems.

In compose-based plans:

  • define the shared volume with driver: sharedFileSystem,
  • put the Nebius backend config on the root volume driver_opts,
  • and reference the matching Nebius clusterStorageType in each mounted Resource.

Example:

volumes:
  shared_models:
    driver: sharedFileSystem
    driver_opts:
      nebiusFilesystemType: NETWORK_SSD
      nebiusFilesystemSizeGi: 1024

services:
  api:
    volumes:
      - source: shared_models
        target: /models
        type: volume
        x-omnistrate-storage:
          nebius:
            clusterStorageType: NEBIUS::FILESYSTEM_NETWORK_SSD

Supported Nebius backend mappings:

  • NETWORK_SSD -> NEBIUS::FILESYSTEM_NETWORK_SSD
  • NETWORK_HDD -> NEBIUS::FILESYSTEM_NETWORK_HDD
  • WEKA -> NEBIUS::FILESYSTEM_WEKA
  • VAST -> NEBIUS::FILESYSTEM_VAST

Notes:

  • The filesystem size and backend type live on the shared volume driver_opts, not on the per-resource mount block.
  • Omnistrate rejects Nebius shared file systems for OMNISTRATE_MULTI_TENANCY plans. Use Nebius dedicated or custom tenancy instead.
  • See Shared File System and Compose Spec for the full schema.

Nebius Terraform authentication

terraformConfigurations:
  configurationPerCloudProvider:
    nebius:
      terraformPath: /terraform/nebius
      serviceAccountID: serviceaccount-e00vqdp9fskhmmaan8
      publicKeyID: publickey-e00h9scsyy9mbefrjf
      privateKeyPEM: $secret.nebiusTerraformPrivateKey
      gitConfiguration:
        reference: refs/heads/main
        repositoryUrl: https://github.com/your-org/infra-repo.git

Notes:

  • Prefer $secret.<name> for privateKeyPEM; Omnistrate resolves the secret before running Terraform.
  • When these fields are present, Omnistrate uses this service-account key for the Terraform resource instead of the default host-cluster Terraform identity.
  • See Building your SaaS Product using Terraform and Plan Spec for the full schema.

Add a new region later

To enable another Nebius region, create another project-level binding and replace the full bindings file.

Hosted account update

omnistrate-ctl account update nebius-hosted \
  --nebius-bindings-file ./nebius-bindings.yaml

nebius account update

BYOA backing account update

omnistrate-ctl account customer update <customer-account-instance-id> \
  --nebius-bindings-file ./nebius-bindings.yaml

After the update completes:

  1. Rerun account describe or account customer describe.
  2. Confirm the new binding reaches READY.
  3. Start deploying into the newly discovered region.

Warning

Nebius binding updates are full replacements. If your current file has two working bindings and you add a third, keep all three in the replacement file.

Warning

The binding that was first in your original onboarding file becomes the artifact-bucket owner. Keep that binding in later replacement files unless you are intentionally migrating Nebius artifact storage.

Example: Deploy vLLM on Nebius

Use this example if you want to publish a Nebius-hosted vLLM service plan with omctl, create a GPU-backed instance, expose the model over the Omnistrate-managed DNS endpoint, and use that endpoint from Opencode.

Complete the Nebius account setup described above before running this example.

  • model: Jackrong/Qwen3.5-27B-Claude-4.6-Opus-Reasoning-Distilled
  • served model alias: qwen3.5-27b-claude-4.6-opus-reasoning-distilled
  • tokenizer override: Qwen/Qwen3.5-27B
  • public OpenAI-compatible vLLM API over HTTPS on the Omnistrate-managed DNS endpoint
  • tool-calling enabled so Opencode can use /v1/chat/completions
  • Omnistrate dashboards for both vLLM metrics and NVIDIA GPU metrics

1. Choose the spec

Clone the example from https://github.com/omnistrate-community/nebius-vllm-demo.

The repository contains two variants:

  • spec.yaml: single-node Nebius deployment on a single H200 GPU
  • spec-gpu-cluster.yaml: multi-GPU Nebius GPU-cluster deployment on H200 with tensor parallelism enabled

Use spec.yaml if you want the simplest path for Opencode.

Use spec-gpu-cluster.yaml only if you already have a Nebius GPU cluster and want to run the same model with 8 GPUs. Before building that spec, replace the placeholder GpuClusterID: compute-cluster-id with your real Nebius GPU cluster ID.

2. Build and release the service plan

Run the build from the directory that contains the spec you want to publish.

Single-GPU example:

omctl build -f spec.yaml \
  --product-name 'Nebius' \
  --spec-type ServicePlanSpec \
  --release-as-preferred

GPU-cluster example:

omctl build -f spec-gpu-cluster.yaml \
  --product-name 'Nebius' \
  --spec-type ServicePlanSpec \
  --release-as-preferred

╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ environment  plan_id        plan_name                  release_description          service_id    service_name      version  version_set_status │
│─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
│ Dev          pt-VRWPNLImF0  Nebius vLLM GPU Inference  Initial Release Version Set  s-GuWdEcfcP5  Nebius vLLM Demo  1.0      Preferred          │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Check the service plan result at: https://omnistrate.cloud/product-tier?serviceId=s-GuWdEcfcP5&environmentId=se-BGBkF9Nwqq
Access your SaaS offer at: https://saasportal.instance-w6vidhd14.hc-pelsk80ph.us-east-2.aws.f2e0a955bb84.cloud/service-plans?serviceId=s-GuWdEcfcP5&environmentId=se-BGBkF9Nwqq

This creates or updates the service named Nebius and releases the plan version as the preferred version in the target environment. In this example, the plan name inside the spec is Nebius vLLM GPU Inference.

3. Create a Nebius vLLM instance

Once the plan is released, create an instance in a region backed by a READY Nebius binding.

omctl instance create \
  --service=Nebius \
  --environment=dev \
  --plan='Nebius vLLM GPU Inference' \
  --resource=vllm \
  --cloud-provider=nebius \
  --region=<ready-nebius-region>

╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ cloud_provider  environment  instance_id         plan                       region       resource  service           status     subscription_id  tags  version │
│────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
│ nebius          Dev          <instance-id>       Nebius vLLM GPU Inference  us-central1  vllm      Nebius vLLM Demo  DEPLOYING  sub-6OR7umU3Ei         1.0     │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

If you are using a BYOA plan instead of a hosted plan, add --customer-account-id <customer-account-instance-id> to the create command.

Keep the returned instance ID. You will use it to inspect the endpoint, configure Opencode, and later delete the deployment.

If this is the first deployment in that Nebius tenant and region, expect the initial infrastructure bring-up to take longer than a normal redeploy.

You can track the instance status with:

omctl instance describe <instance-id>

# Filter using jq
omctl instance describe <instance-id> | jq '.consumptionResourceInstanceResult.status'
"RUNNING"

If something is not progressing, inspect the deployment with:

omctl instance debug <instance-id>

nebius instance debug nebius instance debug events

4. Get the generated inference endpoint

List the instance endpoints:

omctl instance list-endpoints <instance-id>

╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ endpoint_name  endpoint_type  network_type  ports  resource_name  status   url                                                                                │
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
│ api            additional     PUBLIC        443    vllm           HEALTHY  r-o3davudsia.<instance-id>.hc-xyxgpfjol.us-central1.nebius.f2e0a955bb84.cloud      │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

You should see a public api endpoint pointing at the Omnistrate-managed DNS name for the vLLM service.

5. Verify the endpoint before wiring in Opencode

Health check:

curl -i https://<endpoint>/health

List the served models:

curl https://<endpoint>/v1/models

You should see the served model ID:

qwen3.5-27b-claude-4.6-opus-reasoning-distilled

Run a basic chat completion:

curl -sS \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "qwen3.5-27b-claude-4.6-opus-reasoning-distilled",
    "messages": [
      {
        "role": "user",
        "content": "Write a Python function that reverses a linked list."
      }
    ],
    "max_tokens": 512,
    "temperature": 0
  }' \
  https://<endpoint>/v1/chat/completions

Because Opencode uses tool-enabled OpenAI-compatible chat requests, it is worth verifying tool calling too:

curl -sS \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "qwen3.5-27b-claude-4.6-opus-reasoning-distilled",
    "messages": [
      {
        "role": "user",
        "content": "What is 2 + 2? Use the tool if needed."
      }
    ],
    "tools": [
      {
        "type": "function",
        "function": {
          "name": "echo",
          "description": "Echo the provided text",
          "parameters": {
            "type": "object",
            "properties": {
              "text": {
                "type": "string"
              }
            },
            "required": ["text"]
          }
        }
      }
    ],
    "tool_choice": "auto",
    "max_tokens": 256
  }' \
  https://<endpoint>/v1/chat/completions

The shipped spec already enables the vLLM flags required for this flow, including --enable-auto-tool-choice, --tool-call-parser qwen3_coder, and --reasoning-parser qwen3.

If this request returns a 400 complaining that auto tool choice is not enabled, the instance is still running an older plan version or an older chart release.

6. Configure Opencode

Point Opencode at the Nebius vLLM endpoint by editing ~/.config/opencode/opencode.json.

{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "nebius-vllm": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Nebius vLLM Qwen 3.5 Distilled",
      "options": {
        "baseURL": "https://<endpoint>/v1"
      },
      "models": {
        "qwen3.5-27b-claude-4.6-opus-reasoning-distilled": {
          "name": "qwen3.5-27b-claude-4.6-opus-reasoning-distilled"
        }
      }
    }
  }
}

Notes:

  • Replace <endpoint> with the DNS name returned by omctl instance list-endpoints.
  • Do not append :443 or :8000; the ingress already exposes the service over HTTPS on the default port.
  • If your endpoint requires authentication, add options.apiKey or options.headers.
  • If you want Opencode to understand model limits more precisely, add a limit block under the model entry.

After saving the config, start Opencode and run:

/models

Select:

  • provider: nebius-vllm
  • model: qwen3.5-27b-claude-4.6-opus-reasoning-distilled

At that point Opencode will use your Nebius-hosted vLLM endpoint for chat and tool-enabled coding requests.

opencode integration

7. Inspect the Omnistrate dashboards

This example includes Omnistrate dashboard integrations for both vLLM and NVIDIA GPU telemetry.

Open the dashboards with:

omctl instance dashboard <instance-id>

Out of the box, the dashboards expose:

  • vLLM request concurrency, throughput, latency, KV cache usage, and prefix cache hit rate
  • NVIDIA GPU inventory, utilization, framebuffer usage, PCIe throughput, thermals, power, and error signals

nebius dashboards nebius gpu dashboard nebius vllm dashboard

8. Delete the instance

Delete the deployment when you are finished:

omctl instance delete <instance-id>

Add -y if you want to skip the confirmation prompt:

omctl instance delete <instance-id> -y

Delete and offboard

Useful lifecycle commands:

# List all Nebius customer onboarding instances
omnistrate-ctl account customer list --cloud-provider nebius

# Delete a customer onboarding instance
omnistrate-ctl account customer delete <customer-account-instance-id>

For the offboarding order and cleanup rules, follow Account Offboarding.