AppSpec Reference¶
The appspec.yaml file is a multi-document YAML file that defines the Kubernetes resources for your Marketplace app. This page covers every supported field and Cohesity-specific extension.
Supported Kinds¶
| Kind | API Version | Purpose |
|---|---|---|
Service | v1 | Expose container ports |
ReplicaSet | apps/v1 | Long-running stateless workloads |
StatefulSet | apps/v1 | Stateful workloads with persistent storage |
Job | batch/v1 | One-off or cleanup tasks |
All other Kubernetes kinds will fail validation.
Service¶
apiVersion: v1
kind: Service
metadata:
name: my-app-svc # Unique name within the app
labels:
app: my-app # Must match ReplicaSet selector
spec:
type: NodePort # Only NodePort is supported
selector:
app: my-app # Selects pods with this label
ports:
- port: 8080 # Container port to expose
protocol: TCP
name: http # Port name (referenced by cohesityEnv)
cohesityTag: ui # (optional) Expose in Marketplace UI + token gate
cohesityEnv: MY_PORT # (optional) Inject assigned NodePort as env var
Service Fields¶
| Field | Required | Description |
|---|---|---|
spec.type | Yes | Must be NodePort |
spec.ports[].port | Yes | Container port number |
spec.ports[].protocol | No | TCP (default) or UDP |
spec.ports[].name | No | Port name; required if using cohesityEnv |
spec.ports[].cohesityTag | No | See cohesityTag |
spec.ports[].cohesityEnv | No | See cohesityEnv |
cohesityTag¶
Marks a port for special handling by the Marketplace platform.
| Value | Effect |
|---|---|
ui | Exposes this port in the Marketplace app launcher. Adds the Cohesity token gate (unless unrestricted_app_ui_access: true in app.json). |
cleanup | For Jobs — marks the Job to run during app uninstall. |
cohesityEnv¶
Injects the dynamically assigned NodePort into every pod as an environment variable.
ports:
- port: 8002
cohesityTag: ui
cohesityEnv: MCP_NODE_PORT # Pods get: MCP_NODE_PORT=<actual_nodeport>
This is how containers learn their own externally-accessible URL at runtime, without hardcoding a port number.
ReplicaSet¶
Used for stateless long-running services (web apps, API servers, MCP servers).
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: my-app # Unique name
labels:
app: my-app
spec:
replicas:
fixed: 1 # or: share: 0.1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app # Must match Service selector
spec:
containers:
- name: my-app
image: my-app:latest # Image name from Docker build
resources:
requests:
cpu: 250m
memory: 128Mi
env:
- name: GAIA_API_KEY
value: "your-api-key"
ports:
- containerPort: 8080
ReplicaSet Fields¶
| Field | Required | Description |
|---|---|---|
spec.replicas.fixed | Either/or | Fixed replica count |
spec.replicas.share | Either/or | Fractional replicas per node |
spec.template.spec.containers[].image | Yes | Docker image name:tag |
spec.template.spec.containers[].resources.requests.cpu | Recommended | CPU request (e.g. 250m) |
spec.template.spec.containers[].resources.requests.memory | Recommended | Memory request (e.g. 128Mi) |
Replicas: Fixed vs Share¶
# Fixed: always exactly N replicas
replicas:
fixed: 1
# Share: N replicas per cluster node (scales with cluster size)
replicas:
share: 0.1 # 0.1 replicas per node = 1 replica on a 10-node cluster
Use fixed: 1 for most Gaia applications. Use share for apps that need to co-locate with cluster nodes.
StatefulSet¶
For applications that need persistent storage (databases, stateful caches).
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-stateful-app
labels:
app: my-stateful-app
spec:
replicas:
fixed: 1
selector:
matchLabels:
app: my-stateful-app
template:
metadata:
labels:
app: my-stateful-app
spec:
containers:
- name: my-stateful-app
image: my-stateful-app:latest
resources:
requests:
cpu: 250m
memory: 256Mi
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
Note
Most Gaia applications are stateless (Gaia itself holds the data). Prefer ReplicaSet unless you have a specific reason to use StatefulSet.
Job¶
For one-off tasks. Use cohesityTag: cleanup to run the Job during app uninstall.
apiVersion: batch/v1
kind: Job
metadata:
name: my-app-cleanup
labels:
app: my-app
cohesityTag: cleanup # Runs on app uninstall
spec:
template:
spec:
restartPolicy: Never
containers:
- name: cleanup
image: my-app:latest
command: ["/bin/sh", "-c", "python manage.py cleanup"]
resources:
requests:
cpu: 100m
memory: 64Mi
Auto-Injected Environment Variables¶
These variables are automatically available in every pod. You do not need to declare them in appspec.yaml.
| Variable | Type | Description |
|---|---|---|
HOST_IP | String | IP address of the host node |
APPS_API_ENDPOINT_IP | String | IP of the Cohesity Apps API |
APPS_API_ENDPOINT_PORT | String | Port of the Cohesity Apps API |
APP_AUTHENTICATION_TOKEN | String | Short-lived Marketplace identity token |
POD_UID | String | Unique identifier for this pod |
Reading Auto-Injected Variables in Python¶
import os
host_ip = os.getenv("HOST_IP", "localhost")
node_port = os.getenv("MCP_NODE_PORT", "8002") # Only available if cohesityEnv declared
is_marketplace = bool(os.getenv("APP_AUTHENTICATION_TOKEN"))
if is_marketplace:
print(f"Running on cluster: http://{host_ip}:{node_port}/mcp")
app.json Reference¶
The app.json file provides metadata displayed in the Marketplace app listing.
{
"name": "Gaia Chat",
"description": "Chat with your Cohesity knowledge bases using Gaia RAG",
"version": "1.0.0",
"category": "AI / Analytics",
"min_software_version": "7.2",
"unrestricted_app_ui_access": false
}
| Field | Required | Description |
|---|---|---|
name | Yes | Display name in the app catalog |
description | Yes | Short description |
version | Yes | Semantic version string |
min_software_version | Yes | Minimum Cohesity cluster version (use "7.2" for Gaia apps) |
unrestricted_app_ui_access | No | true to bypass the Marketplace token gate (default false) |
When to use unrestricted_app_ui_access: true¶
Set this to true only when: - Clients connect programmatically (e.g., MCP clients) and cannot complete a browser-based SSO flow. - You have an alternative auth mechanism (e.g., API key in the request).
Set to false (the default) for web applications where users access the app through a browser — the Marketplace token gate handles authentication automatically.
Complete Example: Gaia Chat App¶
# appspec.yaml
apiVersion: v1
kind: Service
metadata:
name: gaia-chat-service
labels:
app: gaia-chat
spec:
type: NodePort
selector:
app: gaia-chat
ports:
- port: 8080
protocol: TCP
name: http
cohesityTag: ui
---
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: gaia-chat
labels:
app: gaia-chat
spec:
replicas:
fixed: 1
selector:
matchLabels:
app: gaia-chat
template:
metadata:
labels:
app: gaia-chat
spec:
containers:
- name: gaia-chat
image: gaia-chat:latest
resources:
requests:
cpu: 250m
memory: 256Mi
env:
- name: GAIA_API_KEY
value: "YOUR_GAIA_API_KEY"
- name: GAIA_BASE_URL
value: "https://helios.cohesity.com/v2/mcm/gaia"
- name: GAIA_VERIFY_SSL
value: "true"
- name: GAIA_TIMEOUT_SECONDS
value: "60"
// app.json
{
"name": "Gaia Chat",
"description": "Chat with Cohesity knowledge bases",
"version": "1.0.0",
"min_software_version": "7.2",
"unrestricted_app_ui_access": false
}
Next Steps¶
- Auth in the Marketplace — Token gate and API key patterns
- Packaging a Gaia App — Building and submitting your app bundle
- Single-Container Pattern — Serving React + FastAPI from one image