K8s manifests: view and edit
View any Kubernetes resource on a registered cluster as YAML, diff and dry-run your changes, and apply them live with optimistic concurrency, RBAC checks, and an audit trail.
ZopDay has a live manifest view and editor for every Kubernetes resource discovered on a registered cluster. You can read the running YAML, diff it against your edited copy, dry-run it, and apply the change. Kubernetes RBAC and ZopDay’s own permission system both gate every change, and every change is audited.
Before you start
- A registered cluster. See Cluster registration.
- The
resource:updatepolicy in ZopDay to edit. Without it, the Edit button isn’t shown. Editing a Secret requiresorganisation:update(admin). - Kubernetes RBAC on the cluster that lets ZopDay’s identity update the kind in that namespace. See RBAC on the cluster.
How it works
Open the manifest
Open a resource’s detail drawer and click Edit. The manifest drawer opens with the live YAML, read-only. Click Edit in the drawer to change it in the editor, then Apply: ZopDay dry-runs the change on the cluster and shows a side-by-side diff of the live manifest and your copy. Nothing is written until you click Confirm & Apply.
The Edit button on the detail drawer appears only when your role can update the resource, so without that permission there is no YAML view from this drawer.
The editor loads the first time you open it, so the first open takes a moment; later opens are faster.
Secret values
Secret values are admin-only. Admins get a Secret’s data and stringData. For anyone with read-only access, ZopDay strips the values: they can confirm the Secret exists and see its keys, but not what is in it. A request with no identity at all is treated as untrusted rather than as a system call. Its values are stripped and it gets no namespace access.
Stripping also removes kubectl.kubernetes.io/last-applied-configuration and any other annotation whose value carries a copy of the secret data. Those annotations echo the whole manifest, so removing only data would still expose the credentials.
Server-managed fields are visible but greyed out in the editor: status, metadata.managedFields, metadata.resourceVersion, and similar. You can edit them, but the server strips them and ignores the change on apply.
Why optimistic concurrency
ZopDay applies your edited manifest to the live cluster and checks resourceVersion first. If a colleague, or a controller, changed the same resource after you opened it, you get a 409 instead of overwriting their change. Refresh to load the live version and re-apply your edit.
If a GitOps controller (such as Argo CD or Flux) manages the resource, it may revert a hand edit on its next sync. Make lasting changes in the source it syncs from.
RBAC gating
A change must pass two permission layers:
- ZopDay RBAC: the user’s role must include the
resource:updatepolicy. The UI shows the Edit button only when it does. - Kubernetes RBAC: on apply, the Kubernetes API server authorises the request. If the identity ZopDay uses on the cluster can’t update this kind in this namespace, the server returns 403.
Both must succeed. ZopDay checks its own policy first. The cluster checks its RBAC on apply, which costs a network round-trip.
Edit a manifest
Open the YAML
Open the resource’s detail drawer and click Edit.
Edit
Click Edit in the manifest drawer and change the manifest.
Apply and review the diff
Click Apply. ZopDay dry-runs the change on the cluster (the button reads Validating…), then shows the diff between the live manifest and your copy.
Confirm
Click Confirm & Apply. If the live resource changed since you opened it, you get a 409; refresh and re-apply.
Dry-run from the UI
Every Apply in the UI runs a dry run first. It calls the edit endpoint with dryRun: true, which the backend sends to the cluster as a server-side dry run (dryRun=All): the cluster validates the change and returns the object it would have written, without applying anything. If the cluster rejects the change, the error gives the reason (RBAC, validation, or conflict) and nothing is applied.
The diff you confirm is shown only after the dry run passes.
Use the API
Read a manifest
GET /resources/{resID}/live/manifestReturns the Kubernetes object as JSON; the UI converts it to YAML for display. Secret values are stripped for non-admins, as above.
Edit a manifest
PUT /resources/{clusterResID}/children/{kind}/{namespace}/{name}/manifestThe request body is your full edited manifest as JSON. The endpoint does five things:
- Validates identity:
name,namespace, andkindin the body must match the path. Mismatch returns 400. - Strips server-managed fields: removes
status,metadata.managedFields, and similar fields before apply. - Blocks dangerous kinds: Node, CustomResourceDefinition, ClusterRole, ClusterRoleBinding, Role, RoleBinding, ServiceAccount, Pod, ReplicaSet, EndpointSlice. You can’t edit these here. Use the cloud console or
kubectlinstead. Secrets and Namespaces are editable here but cannot be deleted. See Dangerous-kind blocklist. - Supports server-side dry-run: pass
?dryRun=Allto validate without applying. The response is the mutated object as the cluster would have written it. - Enforces optimistic concurrency: detects conflicts with
resourceVersion. If the live resource changed since you read it, the endpoint returns 409 Conflict with the currentresourceVersion. Refresh and re-apply.
Create a resource
POST /resources/{clusterResID}/childrenCreates a new Kubernetes resource. Returns 409 if a resource already exists at that (kind, namespace, name). Use it for one-off resources outside the Helm / ZopDay deploy flow, such as a CR for a controller that ZopDay’s catalogue doesn’t know about yet.
Delete a resource
DELETE /resources/{clusterResID}/children/{kind}/{namespace}/{name}Delete is idempotent. It succeeds whether the resource was deleted or was already gone. It returns an error only if the delete itself fails, for example on an RBAC denial or a finalizer hang.
CLI access
For automation, call the same endpoints with curl or an SDK:
# $ZOPDAY_API is your ZopDay API base URL; see the API referencecurl -X PUT "$ZOPDAY_API/resources/$RES_ID/children/Deployment/payments-prod/api-server/manifest" \ -H "Authorization: Bearer $ZOPDAY_PAT" \ -H "Content-Type: application/json" \ -d @manifest.jsonThe PAT acts as you, within the organisations and write access you gave it, so RBAC works as it does in the UI.
Dangerous-kind blocklist
These Kubernetes kinds cannot be created or edited through ZopDay’s manifest editor:
| Kind | Why |
|---|---|
| Node | Editing a node spec can break the cluster autoscaler in non-obvious ways. Use the cloud console. |
| CustomResourceDefinition | Editing a CRD can break every custom resource in the cluster. Use kubectl deliberately. |
| ClusterRole / ClusterRoleBinding | RBAC changes are sensitive. Use a GitOps workflow or kubectl with peer review. |
| Role / RoleBinding | Same. |
| ServiceAccount | Identity, and therefore RBAC, by another route. |
| Pod | Edit the thing that owns it (the Deployment or StatefulSet), not the pod it created. |
| ReplicaSet | Same: owned by a Deployment, which is what you should be editing. |
| EndpointSlice | Maintained by the cluster from your Services. A hand edit is overwritten. |
The UI does not disable the Edit button for these kinds. The server rejects the change on Apply with a 403 saying editing that kind is not allowed.
Some kinds cannot be deleted through this surface: Node, CustomResourceDefinition, Secret, Namespace, PersistentVolume, StorageClass, ClusterRole, ClusterRoleBinding and IngressClass. Secrets and Namespaces stay editable, because a bad edit is recoverable. Deleting a Namespace deletes everything in it, and deleting a Secret breaks whatever mounts it.
For every other kind, the two permission layers govern delete. Deleting a sensitive kind succeeds only if both ZopDay RBAC and Kubernetes RBAC allow it. Take the same care with it, or use kubectl and a GitOps workflow.
Reference
Audit trail
Every successful create, update, or delete via the manifest endpoints:
- Writes an audit log entry (actor, action, resource, full request and response bodies, timestamp)
- Appears in Activity → Audit Logs
- Is forwardable to your SIEM with the rest of the audit feed
Because the entry holds the full request and response bodies, compliance reviewers can see who changed what. Bodies over the audit size cap are truncated with a marker, as in Audit logs, so a very large manifest may not be captured in full. Keep this in mind when you edit a Secret: treat its audit entries as sensitive, and restrict audit-log:view accordingly.
Limitations
The manifest editor can’t yet do the following:
- Edit across clusters in one operation: every edit targets one resource on one cluster. For multi-cluster changes, use a GitOps tool.
- Edit by selector: you can’t say “edit every Deployment with label
app=payments”. One resource at a time. - Diff against an arbitrary historical version: the diff compares the live manifest with your edits, not with earlier revisions. For deploy revisions, use the deployment history.
Troubleshooting
Apply returns 409 Conflict
Someone, or a controller, changed the resource after you opened it. Refresh to load the live version and re-apply your edit.
Apply returns 403
Kubernetes RBAC denied the change: the identity ZopDay uses on the cluster can’t update this kind in this namespace. Widen its ClusterRole binding, or make the change with kubectl.
My edit was reverted
A GitOps controller (such as Argo CD or Flux) manages the resource and restored it on its next sync. Make the change in the source it syncs from.
Apply says the kind is not allowed
The kind is on the dangerous-kind blocklist, and the server refused the edit with a 403. If there is no Edit button at all, your role lacks resource:update (or, for a Secret, organisation:update).
A delete times out
The resource has a stuck finalizer. Remove the finalizer by hand or with kubectl, then delete again.