> For the complete documentation index, see [llms.txt](https://docs.zerve.ai/self-managed/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.zerve.ai/self-managed/configuration/execution-environment-manifest.md).

# Execution Environment Manifest

The Execution Environment Manifest configures how Zerve:

* stores Notebook state, artifacts and intermediate block data ([Object Storage](/self-managed/configuration/object-storage.md))
* builds the container images behind Environments ([Builds](/self-managed/configuration/builds.md))
* runs Notebook code on your cluster ([Code Execution](/self-managed/configuration/code-execution.md))

This page covers how the manifest is delivered and the conventions that apply to all three sections.

## Deploying the manifest

Zerve is installed from a single Helm chart. The manifest is set in that chart's values file, under `executor.execEnvManifest`:

```yaml
# my-values.yaml
executor:
  execEnvManifest:
    storage:
      type: s3
      # ...
    build:
      # ...
    execution:
      # ...
```

Snippets on the Object Storage, Builds and Code Execution pages start at the manifest root. Top-level structure is of the manifest is specified below:

```yaml
storage: { ... }        # required — see Object Storage
build: { ... }          # required — see Builds
execution: { ... }      # required — see Code Execution
prebuiltImages: { ... } # optional — Environments seeded on install, see Builds
```

Keys are camelCase in YAML, and unknown keys are ignored.

The manifest is validated as a whole and cannot be split across several `-f` files. Keep it in one file.

### Mounting the manifest

Zerve reads the manifest from disk, which requires two further settings. The chart renders a non-empty `execEnvManifest` into a ConfigMap; the volume below mounts that ConfigMap, and `EXEC_ENV_MANIFEST_PATH` points at the file inside it:

```yaml
executor:
  volumes:
    execEnvManifest:
      name: exec-env-manifest-volume
      mountPath: /etc/executor-service
      readOnly: true
      source:
        configMap:
          # Resolved at render time to <release>-exec-env-manifest.
          componentRef: exec-env-manifest

  workloads:
    common:
      env:
        # The mount path above plus the file name the ConfigMap renders.
        EXEC_ENV_MANIFEST_PATH: "/etc/executor-service/exec_env_manifest.yaml"
```

Both are commented out in the chart's default values. Without them the manifest is never mounted and the application has no execution configuration.

## Reference values

Many fields accept either a literal value or a **reference** that is resolved at startup. Use references to keep secrets out of the manifest, and therefore out of the ConfigMap.

**Environment variable reference:**

```yaml
bucketName:
  ref: env
  name: MY_ENV_VAR       # reads from $MY_ENV_VAR at startup
  default: fallback-val  # optional, used when the variable is not set
```

**File reference** (useful for Kubernetes-mounted secrets):

```yaml
token:
  ref: file
  path: /var/run/secrets/kubernetes.io/serviceaccount/token
```

Fields that support references are marked `(ref)` on the configuration pages. Referenced values must be available to the workloads that read the manifest: set environment variables with `executor.workloads.common.env`, credentials with `executor.workloads.common.secrets`, and files with `executor.volumes`.

```yaml
executor:
  workloads:
    common:
      secrets:
        # No `key`, so each defaults to the variable name: this Secret must
        # hold keys literally named S3_ACCESS_KEY and S3_SECRET_KEY.
        S3_ACCESS_KEY:
          secretName: zerve-storage-creds
        S3_SECRET_KEY:
          secretName: zerve-storage-creds
```

## Schema file

The manifest has a JSON Schema, generated from the same model the executor service validates against. Download it if you want machine-checked editing rather than a round trip through a failing deploy:

{% file src="/files/fb6WyNrg4Ectu7apDhSI" %}
JSON Schema for `exec_env_manifest.yaml`
{% endfile %}

Point your editor at it and you get completion, inline descriptions and validation as you type. In VS Code, with the YAML extension installed, a comment on the first line is enough:

```yaml
# yaml-language-server: $schema=./execEnvSchema.json
storage:
  type: vast
  # ...
```

The schema is generated from the service and so describes everything it will accept, including fields that only apply to Zerve's hosted deployments and builders that are no longer supported for self-managed installs. These pages are the guide to what's relevant; the schema is there to catch typos and wrong shapes.

## Full examples

Four manifests that each stand on their own. The volume and `EXEC_ENV_MANIFEST_PATH` settings from [Mounting the manifest](#mounting-the-manifest) are omitted throughout but are still required.

{% tabs %}
{% tab title="Minimal" %}
The smallest manifest that runs, for a trial install where everything lives in the cluster: an S3-compatible store (MinIO, here), an in-cluster registry over plain HTTP, Python only. Every image and every optional field is left at its default, which means base images come from Docker Hub and packages from PyPI — fine for a trial, not for production.

```yaml
executor:
  execEnvManifest:
    storage:
      type: s3
      bucketName: zerve
      endpointUrl: http://minio.zerve-system.svc.cluster.local:9000
      auth:
        type: aws_static_creds
        accessKeyId:     { ref: env, name: S3_ACCESS_KEY }
        secretAccessKey: { ref: env, name: S3_SECRET_KEY }

    build:
      builder:
        type: k8s_buildkit
        k8s:
          cluster: &cluster
            endpoint: https://kubernetes.default.svc
            caPath: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
            token: { ref: file, path: /var/run/secrets/kubernetes.io/serviceaccount/token }
          namespace: zerve-runtime
      defaultImageRegistry:
        url: registry.zerve-system.svc.cluster.local:5000
        insecure: true
      imageConfigs:
        python:
          imageRepo: zerve/python-executor
          defaultIndex:
            name: pypi
            url: https://pypi.org/simple

    execution:
      defaultComputeOption: k8s
      computeOptions: [k8s]
      k8s:
        cluster: *cluster
        namespace: zerve-runtime
```

{% endtab %}

{% tab title="Kubernetes + S3" %}
A cloud install with outbound access: S3 for storage, images pushed to ECR, and builds and runs on the same cluster. No static credentials — the executor's IRSA role covers storage and the registry, and `downscopingRoleArn` restricts each run to its own notebook's data. Job scratch space comes from `gp3` volumes rather than node disk.

```yaml
executor:
  execEnvManifest:
    storage:
      type: s3
      bucketName: zerve-artifacts-eu-west-1
      regionName: eu-west-1
      downscopingRoleArn: arn:aws:iam::123456789012:role/zerve-execution-storage
      # no auth block — Zerve uses its IRSA role

    build:
      builder:
        type: k8s_buildkit
        k8s:
          cluster: &cluster
            endpoint: https://kubernetes.default.svc
            caPath: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
            token: { ref: file, path: /var/run/secrets/kubernetes.io/serviceaccount/token }
          namespace: zerve-runtime
          serviceAccountName: default
      defaultImageRegistry:
        url: 123456789012.dkr.ecr.eu-west-1.amazonaws.com
      imageConfigs:
        python:
          imageRepo: zerve/python-executor
          baseImage: public.ecr.aws/docker/library/python:3.11-slim-bookworm
          defaultIndex:
            name: pypi
            url: https://pypi.org/simple
        r:
          imageRepo: zerve/r-executor
          baseImage: docker.io/posit/r-base:4.5-bookworm
          defaultIndex:
            name: p3m
            url: https://p3m.dev/cran/__linux__/bookworm/latest

    prebuiltImages:
      python:
        images:
          - Minimal
          - "ML Starter"
        default: Minimal
      r:
        images:
          - Minimal
        default: Minimal

    execution:
      defaultComputeOption: k8s
      computeOptions: [k8s]
      languages:
        - python
        - r
      k8s:
        cluster: *cluster
        namespace: zerve-runtime
        ephemeralVolumeConfig:
          type: generic
          storageClassName: gp3
```

{% endtab %}

{% tab title="Air-gapped, Kubernetes" %}
An air-gapped install with builds and runs on the same cluster. Every external image and package source is proxied through an internal Nexus: BuildKit, Fluent Bit and Alpine for the builder, the Python and R base images, PyPI, CRAN and the Debian archive. Zerve runs on the cluster it submits to, so the in-cluster API server address is used, and the cluster block and Nexus credentials are defined once as anchors.

```yaml
executor:
  execEnvManifest:
executor:
  execEnvManifest:
    storage:
      type: vast
      bucketName: zerve
      endpointUrl: https://vast.example.com
      caPath: /etc/ssl/certs/internal-ca.crt
      # downscopingRoleArn: arn:vast::<tenant>:role/zerve-execution-storage
      auth:
        type: aws_static_creds
        accessKeyId:     { ref: env, name: VAST_ACCESS_KEY }
        secretAccessKey: { ref: env, name: VAST_SECRET_KEY }

    build:
      builder:
        type: k8s_buildkit
        buildkitImage: nexus.example.com/docker/moby/buildkit:v0.30.0-rootless
        fluentbitImage: nexus.example.com/ghcr.io/fluent/fluent-bit:5.0
        contextDownloaderImage: nexus.example.com/docker/library/alpine:3.24.1
        k8s:
          cluster: &cluster
            endpoint: https://kubernetes.default.svc
            caPath: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
            token: { ref: file, path: /var/run/secrets/kubernetes.io/serviceaccount/token }
          namespace: zerve-runtime
          serviceAccountName: default
          imagePullSecrets:
            - name: regcred
      defaultImageRegistry:
        url: nexus.example.com
        caPath: /etc/ssl/certs/internal-ca.crt
        auth: &nexusAuth
          type: basic
          username: { ref: env, name: NEXUS_USERNAME }
          password: { ref: env, name: NEXUS_PASSWORD }
      imageConfigs:
        python:
          imageRepo: zerve-docker/python-executor
          baseImage: nexus.example.com/docker/library/python:3.11-slim-bookworm
          uvVersion: "0.10.12"
          defaultIndex:
            name: zerve-python
            url: https://nexus.example.com/repository/python-group/simple
            auth: *nexusAuth
          aptSources: &aptSources
            - types: [deb]
              uris: ['https://nexus.example.com/repository/debian']
              suites: [bookworm, bookworm-updates]
              components: [main]
              signedBy: /usr/share/keyrings/debian-archive-keyring.gpg
            - types: [deb]
              uris: ['https://nexus.example.com/repository/debian-security']
              suites: [bookworm-security]
              components: [main]
              signedBy: /usr/share/keyrings/debian-archive-keyring.gpg
          extraCaCertPaths: &extraCaCerts
            - /etc/ssl/certs/internal-ca.crt
          aptAuth: &aptAuth
            - machine: nexus.example.com
              login:    { ref: env, name: NEXUS_USERNAME }
              password: { ref: env, name: NEXUS_PASSWORD }
          requiredAptPackages:  # build dependencies of the `zerve` runner package
            - build-essential
            - g++
            - make
            - cmake
            - unzip
            - libcurl4-openssl-dev
            - git
            - gcc
            - unixodbc-dev
            - libpq-dev
            - python3-dev
        r:
          imageRepo: zerve-docker/r-executor
          baseImage: nexus.example.com/docker/posit/r-base:4.5-bookworm
          defaultIndex:
            name: r-group
            url: https://nexus.example.com/repository/r-group
            auth: *nexusAuth
          aptSources: *aptSources
          aptAuth: *aptAuth
          extraCaCertPaths: *extraCaCerts
          requiredAptPackages: []

    prebuiltImages:
      python:
        images:
          - Minimal
        default: Minimal
      r:
        images:
          - Minimal
        default: Minimal

    execution:
      defaultComputeOption: k8s
      computeOptions:
        - k8s
      capabilities:
        - jobs
      languages:
        - python
        - r
      k8s:
        cluster: *cluster
        namespace: zerve-runtime
        serviceAccountName: default
        fluentbitImage: nexus.example.com/ghcr.io/fluent/fluent-bit:5.0
        imagePullSecrets:
          - name: regcred
```

{% endtab %}

{% tab title="Air-gapped, Armada" %}
The same air-gapped mirroring, with Armada in front of the cluster. Builds go to one Armada queue and runs to another, sharing a single `server` anchor. Both `armada` and `k8s` are listed as compute options, so runs default to Armada but can still be submitted directly. Armada accepts only a PodSpec, so the image pull secrets must already exist in `zerve-runtime` and taints need explicit `tolerations`.

```yaml
executor:
  execEnvManifest:
    storage:
      type: vast
      bucketName: zerve
      endpointUrl: https://vast.example.com
      caPath: /etc/ssl/certs/internal-ca.crt
      downscopingRoleArn: arn:vast::<tenant>:role/zerve-execution-storage
      auth:
        type: aws_static_creds
        accessKeyId:     { ref: env, name: VAST_ACCESS_KEY }
        secretAccessKey: { ref: env, name: VAST_SECRET_KEY }

    build:
      builder:
        type: armada_buildkit
        buildkitImage: nexus.example.com/docker/moby/buildkit:v0.30.0-rootless
        fluentbitImage: nexus.example.com/ghcr.io/fluent/fluent-bit:5.0
        contextDownloaderImage: nexus.example.com/docker/library/alpine:3.24.1
        armada:
          server: &armadaServer
            endpoint: { ref: env, name: ARMADA_ENDPOINT }
            caPath: /etc/ssl/certs/internal-ca.crt
            binocularsEndpoint: { ref: env, name: ARMADA_BINOCULARS_ENDPOINT }
            auth:
              type: oidc
              tokenUrl:     { ref: env, name: ARMADA_TOKEN_URL }
              clientId:     { ref: env, name: ARMADA_CLIENT_ID }
              clientSecret: { ref: env, name: ARMADA_CLIENT_SECRET }
              scopes: [armada]
          queue: zerve-builds
          namespace: zerve-runtime
          imagePullSecrets:
            - name: regcred
          tolerations: &tolerations
            - key: dedicated
              operator: Equal
              value: zerve
      defaultImageRegistry:
        url: nexus.example.com
        caPath: /etc/ssl/certs/internal-ca.crt
        auth: &nexusAuth
          type: basic
          username: { ref: env, name: NEXUS_USERNAME }
          password: { ref: env, name: NEXUS_PASSWORD }
      imageConfigs:
        python:
          imageRepo: zerve-docker/python-executor
          baseImage: nexus.example.com/docker/library/python:3.11-slim-bookworm
          defaultIndex:
            name: zerve-python
            url: https://nexus.example.com/repository/python-group/simple
            auth: *nexusAuth
          aptSources: &aptSources
            - types: [deb]
              uris: ['https://nexus.example.com/repository/debian']
              suites: [bookworm, bookworm-updates]
              components: [main]
              signedBy: /usr/share/keyrings/debian-archive-keyring.gpg
          extraCaCertPaths: &extraCaCerts
            - /etc/ssl/certs/internal-ca.crt
          aptAuth: &aptAuth
            - machine: nexus.example.com
              login:    { ref: env, name: NEXUS_USERNAME }
              password: { ref: env, name: NEXUS_PASSWORD }

    execution:
      defaultComputeOption: armada
      computeOptions:
        - armada
        - k8s
      armada:
        server: *armadaServer
        queue: zerve-jobs
        namespace: zerve-runtime
        fluentbitImage: nexus.example.com/ghcr.io/fluent/fluent-bit:5.0
        imagePullSecrets:
          - name: regcred
        tolerations: *tolerations
      k8s:
        cluster:
          endpoint: https://kubernetes.default.svc
          caPath: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
          token: { ref: file, path: /var/run/secrets/kubernetes.io/serviceaccount/token }
        namespace: zerve-runtime
        fluentbitImage: nexus.example.com/ghcr.io/fluent/fluent-bit:5.0
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.zerve.ai/self-managed/configuration/execution-environment-manifest.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
