Production engineering guide for cell based architecture covering patterns, implementation strategies, and operational best practices.
Cell Based Architecture is a deployment pattern where a large application is decomposed into independent, self‑contained units called cells. Each cell packages its own runtime, dependencies, and often its own data partition, and can be brought online or taken offline without bringing down the entire system. You adopt this pattern when a single service has grown beyond the safe blast radius of a monolithic deployment, when you need independent release cycles for different functional domains, or when you're operating at global scale and need to localize failures. It matters most when your traffic or data volume means a single failure domain would impact too many users, and you want the rest of the system to keep running.
To start, split the monolith into directories, each becoming a cell. Each cell needs its own Dockerfile, its own dependency list, and a way to identify itself at runtime. A common pattern is to read a CELL_ID and CELL_REGION environment variable at startup, then use those to select the correct schema, endpoint prefix, or feature flag set.
Example: a minimal Python entrypoint that respects cell boundaries.
import os, fastapi, uvicorn
cell_id = os.getenv("CELL_ID", "default")
region = os.getenv("CELL_REGION", "default")
app = fastapi.FastAPI(title=f"{cell_id}-{region}")
@app.get("/healthz")
def health():
return {"cell": cell_id, "status": "ok"}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=int(os.getenv("PORT", "8080")))
Deploy the cell with real docker and kubectl commands:
# Build the cell image
docker build -t myorg/checkout-cell -f Dockerfile.checkout .
# Run with cell identifiers passed as environment variables
docker run --restart=unless-stopped \
-e CELL_ID=checkout \
-e CELL_REGION=us-east-1 \
-e PORT=8080 \
-p 8080:8080 \
myorg/checkout-cell
In Kubernetes, label the deployment and inject the same identifiers:
kubectl create deployment checkout-cell --image=myorg/checkout-cell
kubectl label deployment checkout-cell cell=checkout --overwrite
kubectl set env deployment/checkout-cell CELL_ID=checkout CELL_REGION=us-east-1
Once the boundary is defined, the next step is getting cells onto infrastructure and tagging them so they can be addressed, monitored, and reasoned about independently. Labels are the primary mechanism; they flow through to pods, services, and metrics.
# Create a deployment and immediately label it
kubectl apply -f checkout-cell.yaml
# View which pods belong to which cell
kubectl get pods -l cell=checkout
# List all cells in the cluster (assuming every deployment carries the label)
kubectl get all -l 'cell in (checkout, payments, inventory)'
Real‑world tip: many teams also use a lightweight service registry or DNS entry per cell, but the label itself is sufficient for kubectl, Prometheus relabeling, and internal tooling to route or alert on a per‑cell basis.
Traffic steering is where the blast‑radius effect becomes visible to end users. The simplest approach is DNS‑based routing: each cell gets its own hostname or subdomain, and a global DNS entry round‑robins across active cells. More commonly, a layer 4 or layer 7 load balancer does the steering, using the cell label to select backend endpoints.
Example HAProxy configuration snippet (real config syntax, no product‑specific hooks):
backend checkout_cells
balance roundrobin
option httpchk GET /healthz
http-check expect status 200
server checkout-cell-1 checkout-cell-1.example.com:8080 check
server checkout-cell-2 checkout-cell-2.example.com:8080 check
In Kubernetes, service meshes or internal load balancers can apply the same principle using the cell label to route to endpoints tagged with that label. The key invariant is: the routing layer never sends traffic to a cell that fails its health check.
Concrete test:
# Hit a specific cell by its resolved endpoint
curl -fsS http://checkout-cell-1.example.com/healthz && echo "cell up" || echo "cell down"
Health checks are the boundary between a cell that’s merely slow and a cell that’s considered failed. A liveness probe determines if the cell process should be restarted; a readiness probe determines if it should receive traffic.
Kubernetes makes this concrete:
# Set a liveness probe that hits the cell’s health endpoint
kubectl set probe deployment/checkout-cell \
--liveness --get-url=http://localhost:8080/healthz \
--initial-delay-seconds 10 --period-seconds 15
# Set a readiness probe with the same endpoint
kubectl set probe deployment/checkout-cell \
--readiness --get-url=http://localhost:8080/healthz \
--initial-delay-seconds 5 --period-seconds 10
If a probe fails beyond the configured threshold, Kubernetes removes the pod from service (readiness) or restarts it (liveness). In Docker‑compose‑style deployments, equivalent behavior is achieved with restart policies and external process managers that monitor the health endpoint.
Sharp edge: a cell may pass an HTTP liveness check while its database connection pool is exhausted. The HTTP stack responds 200, but every subsequent request blocks waiting on a connection. The load balancer still routes traffic, and the user sees timeouts or errors despite the cell being “up”. Always include database connectivity or pool usage in your readiness probe if the cell serves stateful requests.
When a cell becomes unresponsive, the rest of the system should continue operating. The mechanics depend on your routing layer, but the pattern is always the same: detect failure, stop sending new traffic, drain existing connections, and optionally restart or redeploy the cell.
If you’re using a load balancer with health checks, failed cells are automatically removed from the rotation. Existing connections are allowed to drain per the balancer’s drain timeout. In Kubernetes, a pod that loses readiness is immediately un‑selected by services and ingress controllers.
What you only learn after it breaks in production:
CELL_ID or CELL_REGION may get out of sync across deployments if updated manually on some nodes and via CI on others. Treat cell identifiers as configuration that lives in the same source‑controlled pipeline as the container image.cell, region) and down‑sample aggressively for historical view.Cell Based Architecture moves the failure domain from “the whole service” to “individual cells”. The pattern is straightforward in principle—decompose, label, health‑check, route—but the sharp edges surface most clearly under load, across global deployments, and when data ownership isn’t explicitly carved. The concrete commands above (docker run -e CELL_ID=..., kubectl label, kubectl set env, kubectl set probe, curl health checks) are the daily operators for keeping cells independent and resilient. Treat cell identifiers as first‑class configuration, keep health checks honest, and route traffic at the infrastructure level rather than embedding routing logic in every cell’s code.
This page was rewritten on 17 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.
We use cookies for analytics (Google Analytics) and advertising (Google AdSense) to improve your experience and support free content. Privacy Policy