Deployment Lifecycle
Zerops build & deploy pipeline: temporary build container runs prepareCommands + buildCommands, uploads artifact via deployFiles, then deploys to runtime containers with optional readiness checks. Default is zero-downtime rolling deployment. Build has a 60-minute timeout. The pipeline emits events trackable via zerops_events.
Build Phase
Build Container Lifecycle
The build container is temporary -- created on demand, destroyed after completion or failure.
Step-by-step execution order:
- Container creation -- base environment from
build.base(<os>/<tech>@<ver>; the prefix selects Alpine or Ubuntu, a bare value resolves to Alpine) - Source code download -- from GitHub, GitLab, or zcli push to
/var/www - Cache restoration -- cached files moved to
/build/source(no-clobber, source wins) - prepareCommands -- install additional tools/packages (skipped if cache valid)
- buildCommands -- compile, bundle, package your application
- Artifact upload -- files matching
deployFilesstored in internal Zerops storage - Cache preservation -- files matching
cache:moved to/build/cache - Container deletion -- build container destroyed regardless of outcome
Build Limits
- Resources: CPU 1-5 cores, RAM 8 GB fixed, Disk 1-100 GB (auto-scales, not charged separately)
- Timeout: 60 minutes hard limit -- no retry, must trigger new pipeline
- Cancellation: only available before build finishes -- once artifact uploaded, deploy cannot be cancelled
Command Exit Codes
- Exit 0 -- success, next command runs
- Non-zero -- build cancelled, check build log for errors
- YAML list items = separate shells; use
|block scalar for single shell (shared env/cwd)
Runtime Prepare Phase (Optional)
Runs after build, before deploy when run.prepareCommands is defined. Creates a custom runtime image with additional system packages.
Execution order:
- Create prepare container from
run.os+run.base - Copy files from
build.addToRunPrepareto/home/zerops/ - Execute
run.prepareCommandsin order - Snapshot as custom runtime image
- Image cached for future deploys
Cache invalidation triggers:
- Change to
run.os,run.base, orrun.prepareCommands - Change to
build.addToRunPreparefile contents - Manual invalidation via GUI
DO NOT include application code in the runtime prepare image. Deploy files arrive separately.
Deploy Phase
First Deploy
For each new container (count based on auto scaling settings):
- Install runtime -- base image or custom runtime image
- Download artifact -- from internal storage to
/var/www - initCommands -- optional per-container initialization (runs every start/restart)
- start command -- launch application
- Readiness check -- if configured, gates traffic routing
- Container active -- receives incoming requests
Multiple containers deploy in parallel.
Subsequent Deploys (Rolling Deployment)
Default behavior (temporaryShutdown: false):
- New containers started (same count as existing)
- New containers go through steps 1-6 above
- Both old and new versions run simultaneously during transition
- Old containers removed from load balancer (stop receiving new requests)
- Old container processes terminated
- Old containers deleted
Deploy strategy & checks
temporaryShutdown (in the deploy block) controls cutover order: false (default) starts new containers before removing old ones (zero-downtime); true stops old before new (downtime, use only when two versions cannot coexist — exclusive DB migrations, singleton locks). The readiness check gates traffic to the new container during a deploy; the health check monitors the live app continuously. For check behavior, params, the httpGet/exec timeouts, and the production pattern, see zerops_knowledge uri="zerops://guides/readiness-health-checks".
Event Timeline (zerops_events)
Typical pipeline events in chronological order:
stack.buildprocess RUNNING -- build container created, pipeline startedstack.buildprocess FINISHED -- build complete, artifact uploadedappVersionbuild event ACTIVE -- the new version is deployed and running (this is the terminal success state, NOT "launching")- Service status returns to RUNNING -- all containers active, deploy complete
Terminal states:
- Build done:
stack.buildprocess status =FINISHED - Build failed:
stack.buildprocess status =FAILED - Deploy done: service containers all active, new appVersion is
ACTIVE
DO NOT keep polling after stack.build shows FINISHED -- that means the build itself is complete. The ACTIVE status on appVersion means deployed and running.
Build Event Polling Checklist
When monitoring a build/deploy via zerops_events:
- Filter by service: always use
serviceHostnameparameter to avoid stale events from other services or previous iterations - Check
stack.buildprocess: look for statusFINISHED(success) orFAILED(error). OnceFINISHED, the build is done — stop polling build status - Check
appVersionbuild event: statusACTIVEmeans deployed and running. This confirms deploy completion - Do NOT confuse build events:
stack.buildprocessRUNNING= build in progress.appVersionACTIVE= already deployed. These are different events - Timeout guidance: builds have a 60-minute hard limit. If no
FINISHEDafter ~5 minutes for typical apps, check build logs viazerops_logs - Stale events: project-level events may include old builds from previous deploys. Always verify the event timestamp and service match
Application Versions
Zerops keeps 10 most recent versions. Older auto-deleted. Any archived version can be restored -- activates that version, archives current, restores env vars to their state when that version was last active.
Gotchas
- initCommands run on EVERY container start -- including restarts and horizontal scaling, not just deploys
- initCommands failures DO fail the deploy --
run.initCommandsrun during runtime-prepare, BEFORE the start command, and are deploy-gating: a non-zero exit aborts the deploy. The platform emitsRUN.INIT COMMANDS FINISHED WITH ERRORin the runtime log and surfaces the failed command + exit code on thestack.buildprocess (commandExec/ "init command failed"). The new appVersion goes toDEPLOY_FAILEDand is never activated; the start command never runs and the previously-active version keeps serving. Diagnose viaappVersion.status(DEPLOY_FAILED) +activationDate(null), NOT the service status — the service staysACTIVEon the old version, so service-status alone reads as "fine". Keep init commands idempotent and exit 0.
SSHFS Mount and Deploy Interaction
When using SSHFS (zerops_mount) for dev workflows, deploy replaces the container. This has important consequences:
- After deploy, run container only has
deployFilescontent. All other files (including zerops.yml if not in deployFiles) are gone — persistent data belongs in a storage service. UsedeployFiles: [.]for dev services to ensure zerops.yml and source files survive the deploy cycle. - SSHFS mount auto-reconnects only while the service is running. Usually no remount is needed, but if the mount goes stale after a deploy (stat/ls returns empty, writes hang), recover it explicitly with
zerops_mount action="mount". A stopped service has no live mount until it starts again. - zerops.yml must be in deployFiles for dev self-deploy lifecycle. Without it, subsequent deploys from the container fail because zerops.yml is missing.
Three kinds of "mount" (disambiguation):
zerops_mount-- SSHFS tool, mounts service/var/wwwlocally for development. This is a dev workflow tool.- SeaweedFS mount --
weed mount(orzsc shared-storage mount <hostname>) run as arun.startCommandsentry in zerops.yml, mounts a SeaweedFS service's filer at a directory of your choice. There is nomountfield in zerops.yml (arun.mountis silently ignored) and the import-levelmount:of the deprecated Shared Storage is rejected. These are completely unrelated features. - Local Storage volume -- platform feature, mounts a Local Storage service's persistent volume via the
run.volumefield in zerops.yml (volume: {hostname, mountPath, readOnly}). The volume survives deploys: new containers get the same volume attached at the configuredmountPath.