Deployments
Long-lived application instances ZopNight runs in your Kubernetes cluster — built from a connected git repo and managed as a Helm release.
A deployment is a long-lived application instance ZopNight runs in your Kubernetes cluster. It lives inside an environment, is built from a connected git repository, and is rolled out through a Helm release that ZopNight manages on your behalf.
Which service serves which call
Since the catalog/runtime split (Phase 3a–3c, May 2026), ownership of the deployment surface is split across two services: config owns the long-lived catalog row, and deployer owns the per-deploy runtime (builds, revisions, events, Helm rollout). Both services sit behind the gateway at zopnight.com/api — the split is invisible to API clients, but the table below explains which service handles what, in case you’re tracing logs or wiring fine-grained IAM.
| Concern | Service | Surface |
|---|---|---|
| Catalog row (name, source, configs, status, current revision, canvas position) | config | /deployments, /deployments/{id}, /environments/{envID}/deployments |
| Configs (env vars, secrets, port, resources) | config | PATCH /deployments/{id}/configs |
| Revisions, rollback, analyze | deployer | /deployments/{id}/revisions, /rollback, /analyze |
| Event timeline + LoadBalancer status | deployer | /deployments/{id}/events, /loadbalancer |
| Status & current-revision writeback | deployer → config | gRPC UpdateDeploymentStatus, SetCurrentRevision |
| Teardown (helm uninstall + revision/event cleanup) | config → deployer | gRPC Teardown(orgID, deploymentID) |
Lifecycle
- Client calls
POST /deployments. Config writes the catalog row plus a revision-#1 placeholder, then enqueues a job into the deployer’s MySQLjobsqueue via deployer gRPC. - The deployer’s worker pool claims the job, clones the repo, runs stack detection, and dispatches a GitHub Actions build.
- On build completion, the deployer mints a per-deploy ephemeral kubeconfig from the cloud account credentials and applies a Helm release to the target namespace on the deployment’s deployment space.
- Every stage boundary appends an event to
deployment_events(deployer). Status changes are written back to config via gRPC so the catalog row always reflects the live state. - Subsequent pushes (auto-deploy) or config changes create new revisions; the deployer reconciles them as rolling updates with zero downtime.
DELETE /deployments/{id}on config calls the deployer’s inboundTeardowngRPC (idempotent — re-runs returnalready_clean=true) before soft-deleting the catalog row.
Imported (Helm) Services
Besides git- and image-sourced deployments, the cluster import flow can bring existing Helm releases into a project as deployments with sourceType: "helm-import". These are catalog entries for workloads whose lifecycle stays with Helm on the cluster — ZopDay did not build or roll them out, so the UI treats them as view-only: the canvas card carries an Imported · Read-only badge and is not clickable, edit / redeploy / delete are hidden wherever the service is inspected, and no revision history exists (currentRevisionId stays empty). Manage or remove the underlying release with Helm (or re-run the import) — the deployment row is a mirror, not a control surface.
Create a Deployment
Two equivalent forms: a flat path for integrations and CI scripts, and an env-nested form for the canvas (which already has the environment in context). Both land on config.
/deployments Create a deployment. Writes the catalog row and revision #1 placeholder, then enqueues a build job. (config)
curl -X POST https://zopnight.com/api/deployments \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "name": "checkout", "envId": "env_abc", "sourceType": "git", "repoUrl": "https://github.com/acme/checkout", "branch": "main", "integrationId": "int_github_001", "autoDeploy": true, "configs": { "port": 8080, "envVars": { "DB_HOST": "checkout-db.zopnight.internal", "LOG_LEVEL": "info" }, "secretRefs": ["secret://checkout-db-password"] } }'/environments/{envID}/deployments Env-nested create. Same payload, used by the canvas. (config)
List & Get
/deployments List all deployments visible to the caller across every environment. (config)
/environments/{envID}/deployments List deployments in one environment. (config)
/deployments/{id} Get a deployment, hydrated with current revision, configs, and the env's inherited infra (space, namespace, registry). (config)
{ "data": { "id": "dep_xyz", "name": "checkout", "orgId": "org_acme", "envId": "env_abc", "sourceType": "git", "repoUrl": "https://github.com/acme/checkout", "branch": "main", "integrationId": "int_github_001", "autoDeploy": true, "spaceId": "spc_abc", "namespace": "checkout-prod", "registryUrl": "123456789012.dkr.ecr.us-east-1.amazonaws.com/checkout", "currentRevisionId": "rev_005", "status": "active", "configs": "{\"port\":8080,\"envVars\":{\"LOG_LEVEL\":\"info\"}}", "canvasPosition": { "x": 320, "y": 180 }, "lastDeployAt": "2026-04-29T11:30:00Z", "createdAt": "2026-04-15T08:00:00Z", "updatedAt": "2026-04-29T11:30:00Z" }}/deployments/{id} Tear down a deployment. Config calls the deployer's Teardown gRPC (helm uninstall + revision/event cleanup, idempotent) before soft-deleting the catalog row. (config)
Update Configs
Env-var, secret, port, replica, and resource changes go through PATCH /deployments/{id}/configs on config. The deployer picks the new shape up on the next revision and applies it as a rolling update — the catalog stores configs as opaque JSON; the deployer parses the typed shape (port, replicas, liveness, resources, expose, rootPath) at apply time.
/deployments/{id}/configs Patch deployment configs. (config)
curl -X PATCH https://zopnight.com/api/deployments/dep_xyz/configs \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "configs": { "envVars": { "LOG_LEVEL": "debug" }, "port": 8080 } }'Canvas Position
/deployments/{id}/canvas-position Persist the (x, y) of a deployment card on the canvas. Body: { x, y }, both finite numbers. (config)
Analyze a Deployment
/deployments/{id}/analyze Re-run deploy-readiness checks against the current source + configs. Returns warnings (missing health probe, missing resource requests, etc.) without creating a revision. (deployer)
Revisions
Every image or config change is captured as a revision. Revisions are immutable. Push-triggered revisions carry the GitHub delivery_id as their idempotency key (UUID v5 of the delivery), so webhook replays coalesce into a no-op via INSERT IGNORE — see Webhooks for the full dedup story.
/deployments/{id}/revisions Create a new revision (typically from CI after a build completes). (deployer)
curl -X POST https://zopnight.com/api/deployments/dep_xyz/revisions \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "image": "registry.zopnight.com/acme/checkout:v1.4.3", "buildID": "build_789", "note": "Hotfix for retry loop" }'/deployments/{id}/revisions List revisions for a deployment. (deployer)
/deployments/{id}/revisions/{revID} Get a specific revision. (deployer)
Rollback
/deployments/{id}/rollback Roll back to a prior revision. The deployer clones the target revision into a new revision and applies it. (deployer)
curl -X POST https://zopnight.com/api/deployments/dep_xyz/rollback \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "toRevisionID": "rev_004" }'Events Timeline
/deployments/{id}/events Append-only chronological list of deploy stage events. Used by the canvas drawer's activity feed. (deployer)
{ "data": [ { "id": "ev_1", "stage": "building", "status": "started", "createdAt": "2026-04-29T11:00:00Z" }, { "id": "ev_2", "stage": "building", "status": "succeeded", "meta": { "imageRef": "..." }, "createdAt": "2026-04-29T11:04:30Z" }, { "id": "ev_3", "stage": "deploying", "status": "started", "createdAt": "2026-04-29T11:04:35Z" }, { "id": "ev_4", "stage": "rolling_out", "status": "succeeded", "meta": { "ready": 3, "desired": 3 }, "createdAt": "2026-04-29T11:05:10Z" }, { "id": "ev_5", "stage": "active", "status": "succeeded", "meta": { "address": "a-1234.elb..." }, "createdAt": "2026-04-29T11:06:50Z" } ]}LoadBalancer Status
For deployments that expose ports as http or tcp, ZopNight provisions a cloud LoadBalancer through the cluster’s ingress class. The endpoint below is what the UI polls during the post-deploy “Waiting for public address…” state.
/deployments/{id}/loadbalancer Get LoadBalancer status (assigned address, listeners, TLS certificate). (deployer)
{ "data": { "status": "ready", "address": "a-12345.elb.us-east-1.amazonaws.com", "ports": [ { "port": 80, "protocol": "HTTP" }, { "port": 443, "protocol": "HTTPS" } ], "tls": { "issuer": "letsencrypt-prod", "expiresAt": "2026-07-29T00:00:00Z" } }}See Builds for how images are produced, Projects & Environments for how deployments are grouped, Deployment Spaces for the cluster they target, and Service Connections for wiring deployments together on the canvas.