nerdctl run container for each Worker, and the broker inside the container starts executor sandboxes through the host’s containerd. Production has two mutually isolated clusters (block and bundle, each with its own coordinator + worker fleet), plus a parallel sync-invoker fleet that doesn’t go through a coordinator. This page is written for developers, to help you build a mental model of where the code runs and how it gets started; the full operational details are in docs/deploy.md and docs/deploy/ in the blockx repository.
The isolation between the two clusters comes only from the etcd registry prefix: the block coordinator watches only /blockx/workers/, the bundle coordinator watches only the bundle prefixes, and neither the request fields nor the gRPC protocol carry any “cluster” identifier. The bundle cluster being live doesn’t mean traffic has been switched over; the upstream routing scheme is designed separately. The sync-invoker doesn’t register with etcd and doesn’t go through a coordinator; one Invoke is one synchronous function call (see Sync Invoker for details).
Processes and binaries
TheDockerfile builds the five binaries in the table below, all in the same image, with the start command choosing the entry point. The four coordinator/worker entry points are “thin profiles”: cmd/*/main.go only declares an app.Profile; shared assembly, config loading, and shutdown ordering live in internal/coordinator/app and internal/worker/app.
The ports in the table are the defaults in
internal/coordinator/app/config.go, internal/worker/app/config.go, and cmd/syncinvoker/config.go. The EC2 systemd templates override them: both the worker and the sync-invoker use gRPC 0.0.0.0:9900 and metrics 0.0.0.0:9901 (see deploy/env/*.env.example). The metrics HTTP endpoint starts only when BLOCKX_PROMETHEUS_LISTEN_ADDR is set; the path is determined by BLOCKX_PROMETHEUS_PATH and defaults to /metrics.app.Profile: Deployment, Service, WorkerRegistryPrefix, Builders, Plugins, and TuneDefaults. On EC2, the entry point is chosen through WORKER_BINARY in worker.env (blockx-worker or blockx-bundle-worker); deploy/scripts/blockx-worker-start accepts only these two values.
Dependent services
BundleWrite and TableUpserts upload their data with a direct
PUT to the presigned URL returned by BlockDB; they don’t use the worker’s AWS credential chain and don’t need write permission on the target bucket. See docs/deploy.md §2 and §3.3 in the blockx repository for the IAM and address lists.
How configuration is released
Application configuration and cloud resources live in two separate repositories:- The blockx repository maintains only runtime artifacts: systemd units, host scripts, env examples, the health gate, and runbooks, all under
deploy/. Chaintable/blockx-ec2-manifestmaintainsworker.env(and latersyncinvoker.env); after merging tomain, it is uploaded as an immutable S3 object keyed by Git SHA, SSM stores only the three-lines3-v1pointer (BLOCKX_WORKER_ENV_FORMAT/BLOCKX_WORKER_ENV_S3_URI/BLOCKX_WORKER_ENV_SHA256), and a State Manager association is then triggered.DeBankDeFi/SRE(Terraform) maintains the ASG, launch template, User Data, IAM, SG, NLB, Prometheus scrape, and deploy bundle URI. The blockx side doesn’t change AWS resources directly.
/opt/blockx-deploy and calls blockx-worker-install; after that, State Manager periodically runs blockx-worker-reconcile, which skips when the env is unchanged and otherwise delegates to install to reinstall, restart, and run the health gate. Validation failures fail closed and don’t overwrite the local /etc/blockx/worker.env.
The deploy/ directories:
Image and CI
TheDockerfile is a two-stage build:
go-builder(golang:1.26-bookworm): compilesblockx-coordinator,blockx-bundle-coordinator, andblockx-syncinvoker(CGO_ENABLED=0) plusblockx-workerandblockx-bundle-worker(CGO_ENABLED=1, DuckDB needs cgo), targetinglinux/amd64.runtime(python:3.12-slim):pip install /app/pythoninstallsblockx_executor/blockx_sdk/blockx_auditand the private dependencies (blockdb-py,blockx-py, and so on), along withpy-spy; the five Go binaries are then copied to/usr/local/bin. There is no separate venv: the Python packages go straight into the image’s system site-packages, withPYTHONPATH=/app/python.
--secret id=github_token) and never enters an image layer.
.github/workflows/:
Releasing an image means tagging and pushing (
git tag v1.2.3 && git push origin v1.2.3). The EC2 side references only pinned tags or digests, never latest.
Graceful shutdown
All processes handleSIGTERM / SIGINT. On a signal, the coordinator calls cancel() on the root context and GracefulStop() on the gRPC server. The worker’s shutdown in internal/worker/app/app.go has two phases: first SetDraining and Deregister from etcd, then wait for in-flight tasks to finish (DRAIN_TIMEOUT_MS, code default 30s, EC2 template 90s); then cancel(), stop the executor pool, close the IO scope, close the WatchTasks subscriptions, and finally GracefulStop (falling back to Stop if it doesn’t finish within 5s). ASG scale-in has no lifecycle hook; it relies on three layers of protection: the worker’s built-in drain, systemd ExecStop, and the etcd lease TTL. See Worker for details.
Observability entry points
See Observability for metric definitions and how to read the dashboards.
Related docs
In the blockx repository:docs/deploy.md— deployment overview from the SRE perspective, full environment variable table, image addresses.docs/deploy/ec2-automation-plan.md— responsibility boundaries between blockx / blockx-ec2-manifest / SRE and the release path.docs/deploy/worker-systemd-nerdctl.md— worker EC2 runbook (install, env, acceptance, rollback).docs/deploy/syncinvoker-systemd-nerdctl.md— sync-invoker EC2 runbook.docs/deploy/sync-invoker-sandbox-host.md— verification record for the sync-invoker host containerd sandbox setup.docs/deploy/sync-invoker-loadtest-2026-06-29.md— sync-invoker capacity load test record.docs/sync-invoker-grafana.md— metric definitions for the sync-invoker Grafana dashboard.docs/specs/2026-07-21-block-bundle-clusters.md— design of the block / bundle dual-cluster split.docs/specs/2026-07-28-registry-prefix-migration.md— registry prefix v2 andCOORDINATOR_REGISTRY_PREFIXES.docs/specs/2026-07-30-ec2-worker-profiling.md— performance sampling for EC2 workers without host dependencies.
Local development environment
The minimal steps to build and run locally.
Observability
Code locations for logs, metrics, tracing, and usage.
Worker
Worker assembly, drain, and shutdown ordering.
Bundle clusters
bundle coordinator / worker and bundle-specific capabilities.