Part 4
Run a first ax Task
Install ax, declare a Workspace, a Model, and a Task, then run, watch, and inspect it. Each step shows what success looks like.
After this page you can:
- Install the ax CLI and control plane at release v0.3.1 on the upstream Substrate cluster from Setup.
- Declare a Workspace, a Model, and a Task, and apply them to an atespace.
- Watch a Task reach
Readyand inspect it from inside the sandbox.
Before you start
Complete the shared setup first, and use the cluster with upstream Substrate v0.3.0 from that page. ax also needs Go, kubectl, ko, and a container registry your cluster can pull from, because the control plane is built from source.1 Commands below follow the ax README and docs at main ac23328, which match release v0.3.1 for everything used here, and both install commands are pinned to the v0.3.1 tag.14
Install ax
-
Install the CLI. The binary lands in
$(go env GOPATH)/bin, which must be on yourPATH. The README uses@latest; this lab pins@v0.3.1so you get the version the page was checked against.1go install github.com/google/ax/cmd/ax@v0.3.1 -
Deploy the control plane from a checkout of
google/ax. ReplaceREGISTRYwith a registry your cluster can pull from. This deploys Redis, then builds and deploys the control plane images withko, all into theax-systemnamespace.1git clone --branch v0.3.1 https://github.com/google/ax.git cd ax make deploy AX_IMAGE_REPO=REGISTRYExpected result:
kubectl get pods -n ax-systemshows the Redis and control plane pods Running.If it does not work: check that
kubectl get svc api -n ate-systemstill lists theapiService; ax cannot start without a reachable Substrate Control API.1
Declare a Workspace, a Model, and a Task
All three kinds use apiVersion: ax.io/v1alpha1 and live in an atespace, a Substrate tenancy unit that is separate from Kubernetes namespaces. The default atespace is named default.320 Names and atespaces must be lowercase RFC 1123 labels.4 Save the three blocks below into one file, task.yaml, separated by --- lines.
Workspace: what the sandbox starts with
apiVersion: ax.io/v1alpha1
kind: Workspace
metadata:
name: default-workspace
atespace: default
spec:
git:
- name: origin
repo: "https://github.com/chalk/chalk.git"
branch: "main"
mcp:
servers:
- name: git-tools
endpoint: "http://git-mcp.default.svc.cluster.local:8080"
spec.git clones repositories into the workspace path, and spec.mcp.servers lists MCP servers the agent may call. Replace the MCP endpoint with a server that exists in your cluster, or delete the mcp block.34
Model: which LLM, with which credential
Create the Secret first, then reference it. The ax docs pass the key with --from-literal; this lab reads it with read -rs, which does not echo it, and pipes it with the printf builtin, so the key never appears in the process list or your shell history and the Secret holds no trailing newline. Paste the key at the prompt and press Enter.4
read -rs KEY && printf %s "$KEY" | kubectl create secret generic anthropic-api-secret -n default --from-file=ANTHROPIC_API_KEY=/dev/stdin; unset KEY
apiVersion: ax.io/v1alpha1
kind: Model
metadata:
name: claude-model
atespace: default
spec:
provider: anthropic
model: claude-opus-5
secretKey:
name: anthropic-api-secret
key: ANTHROPIC_API_KEY
A Model keeps provider configuration in one place, and ax's own components read it too, for example when planning a workspace from a goal.3
Task: the unit of execution
apiVersion: ax.io/v1alpha1
kind: Task
metadata:
name: task123
atespace: default
spec:
# Placeholder: replace with your agent image and its entrypoint.
image: "ghcr.io/my-org/my-agent-image"
command:
- python
- agent.py
resources:
limits:
cpu: "2"
memory: "4Gi"
workspaces:
- name: default-workspace
debug: true
imageandcommandare your agent. ax does not supply a loop.3workspacesbinds the Workspace above; the first entry is the command's working directory and mounts at/workspace/<name>unless apathis set.4debug: trueserves Substrate guest services inside the sandbox, whichax sshneeds. It is off by default because it allows arbitrary process execution and file access.5
A binding may also carry a goal. On first boot the runner hands that goal to an Antigravity agent (Google's coding agent, bundled in the runner) to finish setup. That agent needs GEMINI_API_KEY in the container and gets 10 minutes by default, set by AX_BOOTSTRAP_TIMEOUT.5
Run, watch, and inspect
-
Apply the file and list Tasks.1
ax apply -f task.yaml ax get tasksExpected result (column layout from the ax README; IP and age vary):1
NAME ATESPACE PHASE ACTOR WORKER-IP AGE task123 default Running task123 10.20.3.67 1m -
Stream phase and condition changes. Wait for
WorkspaceReadyand thenReadyto become True;Readyis the condition to wait on.3ax watch task task123If it does not work: a Task that stays not-ready is usually still preparing its workspace, since the runner's
/readyzreturns 503 until clones, MCP configuration, and skills are in place.5 Useax describe task task123to read the conditions.1 -
Look inside the sandbox. This works only because the Task set
debug: true.15ax ssh task123 -- ls -la /workspace -
Suspend and resume. Suspending sets
Readyto False with reasonTaskSuspended; resuming sets it back.3ax suspend task task123 ax resume task task123 -
Clean up.
ax deleteblocks until the sandbox has been torn down on Substrate.3ax delete task task123
ax keeps these resources in Redis, so kubectl get tasks does not exist; the ax CLI is the interface.2 The State page covers why.
What happens inside the sandbox
Your process reads its own configuration from $AX_METADATA_URL/metadata/v1alpha1/ax/task without any SDK.5 The runner stays up as PID 1 whether or not the command is still running, so ax ssh keeps working after the command exits. On stop or suspend, it sends the command's process group SIGTERM, waits ten seconds, and kills what remains.5
ax apply goes from the CLI to ax-server, which records state in Redis and reconciles the Task into a Substrate Actor.ax-server serves gRPC on port 8080, uses Redis for storage, locks, and PubSub, and reconciles directly with Agent Substrate.2