Run your own container
Run an external Docker image on its own Nebius GPU machine, as a batch job or as an HTTP service. The SDK and CLI call the same server API as Studio; they never run Docker or the Nebius CLI on your computer.
You need a key with the read and run scopes, an image pinned by digest, and prepaid credit. First check that containers are available on your server:
gft --json container capabilitiesPick a tariff ID from rates. If runtime_available is false, you cannot launch containers on this server yet.
How it works#
- Register an image. Use a full
registry/repository@sha256:<digest>reference. For Docker Hub, usedocker.io/library/ubuntu@sha256:..., notubuntu:latest. Registering rents no GPU and does not contact the registry. Private registry credentials are encrypted in the vault. - Start a run. Choose a tariff, a command, environment values, job or service, a maximum runtime and a budget. The server checks permissions, workspace, credit and quotas.
- Track the run. You get a durable run ID.
QUEUEDonly means the request was accepted, so check readiness before you send requests. After a timeout, retry with the same request ID and the same settings. Changed settings return a conflict, not a second machine.
If the region of your machine has no free GPU, Studio moves the queued run to the same machine in another connected region at the same or lower price, before any VM starts. Runs started with an explicit connection_id stay where you put them.
Jobs and services#
| Job | Service | |
|---|---|---|
| Use it for | Batch work that finishes | A long-running HTTP server |
| Ends when | The process exits and the machine is released | You stop it, or it reaches its runtime limit |
| Ready when | — | Its readiness path returns a 2xx response |
| Results | Logs and the /outputs archive | Logs and authenticated HTTP requests through GFT |
CLI#
export GFT_API_URL=https://studio.example.com
# Authenticate using the existing gft auth flow / GFT_API_KEY.
gft --json container capabilities # choose an id from rates; runtime_available must be true
gft --json image add "ghcr.io/my-org/llm@$IMAGE_DIGEST" \
--label llm --private --request-id register-llm-v1
gft --json image list
# Omit --private for a public image. Save the returned image ID. env.json is a private JSON object, e.g. HF_TOKEN.
gft --json container run "$IMAGE_ID" --rate "$CONTAINER_RATE_ID" \
--mode service --port 8000 --readiness /health \
--env-file ./env.json --runtime 14400 --budget 20 \
--request-id llm-service-v1 --yes
gft --json container list
gft --json container get "$RUN_ID"
gft container logs "$RUN_ID"
gft --json container request "$RUN_ID" --method POST \
--path /v1/chat/completions --body-file ./completion.json
# For a completed job with mirrored outputs:
gft --json container outputs "$COMPLETED_JOB_RUN_ID"
gft --json container stop "$RUN_ID"Leave out --mode service --port ... to run a batch job. Repeat --command=VALUE for each command argument, including flags that start with -. The command replaces the image's CMD and keeps its ENTRYPOINT.
Python#
from gft_client import Client
with Client() as client:
capabilities = client.containers.capabilities()
print(capabilities["rates"]) # choose a configured Nebius container tariff
images = client.images.list()
image = client.images.register(
"docker.io/my-org/llm@sha256:" + digest_hex,
label="LLM", request_id="image-v1",
# Omit both registry fields for public images.
registry_username=username, registry_password=registry_token,
)
run = client.containers.run(
image["id"], rate_id=container_rate_id, mode="service", port=8000,
env={"HF_TOKEN": hf_token}, max_runtime_seconds=14400,
max_budget_micros=20_000_000, request_id="service-v1",
)
runs = client.containers.list()
status = client.containers.get(run["id"])
logs = client.containers.logs(run["id"])
if status.get("ready"):
response = client.containers.request(run["id"], method="POST",
path="/v1/chat/completions", body=completion_json,
headers={"Content-Type": "application/json"})
# A completed job may have a mirrored output archive:
if status.get("process_state") == "COMPLETED":
archive = client.containers.outputs(run["id"])
client.containers.stop(run["id"])Pass credentials from a secret manager or a hidden prompt, never as shell arguments or in source code. The CLI reads environment values from a private JSON file. The SDK requires HTTPS when it sends credentials or secrets. Multiline values are not supported.
Limits#
| Item | Limit |
|---|---|
| Logs | A mirrored tail of up to 64 KiB or 1,000 lines |
| Outputs | Files written to /outputs, archived up to 64 MiB uncompressed |
| Service request body | 1 MiB |
| Service response | 2 MiB |
| Upstream timeout | 25 seconds |
| Concurrent service requests | 8 per workspace |
| Runtime limit | At least 1 hour; you can stop earlier |
Service access is a bounded request API, not a public website URL. Streaming, SSE and WebSockets are not supported. HTTP mutations are not retried automatically: a timeout can mean that the request ran but its response was lost.
Network and security#
Containers can download weights and call public APIs. Cloud metadata, the host and private networks are blocked. Known environment and registry values are redacted from logs.
Stop and clean up#
gft container stop deletes the machine and its temporary disk. STOPPING means cleanup is still in progress. Saved outputs remain after cleanup, but a forced stop can prevent the final upload.
Pricing#
A machine tariff includes all of its GPUs, CPU, RAM and disk. The cost review includes the GFT fee and a shutdown reserve.
Next steps#
- Open a terminal in a container
- Download the full container contract, including operator prerequisites