How big is a workload — size classes

What small, medium, large and xlarge mean, why they are different absolute numbers for a Rails service and a static site, and where to set one.

The one thing to know first

A size class is relative to the component type, not absolute. small is each type's own baseline — what the platform thinks a workload of that shape needs for a single user and job traffic — and every step above it doubles:

class multiplier
small the type's baseline
medium ×2
large ×4
xlarge ×8

So a Rails large and a Go large are different absolute numbers, and both mean four times the platform standard for this kind of workload.

note

The alternative — one absolute table for every type — was rejected because it cannot be right for both ends of the range. A number generous enough for a Rails service with a db:prepare init container is eight times too big for a static site serving files out of nginx.

What small actually is, per type

Memory request / limit at small. Everything above doubles from here.

component type small
static-site 64Mi / 128Mi
go-service 64Mi / 256Mi
grpc-service 128Mi / 320Mi
python-service 128Mi / 768Mi
zulip-bot 128Mi / 768Mi
ruby-service 256Mi / 768Mi
ruby-scheduled-task 256Mi / 768Mi
ruby-worker 512Mi / 768Mi
vendored-service 1536Mi / 1536Mi

CPU follows the same ladder. Each type's exact numbers are on its own construct page under environmentConfigs.sizeClass, and they are written into the ComponentType as literal quantities rather than computed in CEL — so the ladder is readable in the YAML, on the construct page, and in a manifest diff.

Where you set one

On the ReleaseBinding, per environment. That is the whole point of it being there rather than on the Component: production can be bigger than staging without cutting a second release, and the two environments keep running the same immutable ComponentRelease.

apiVersion: openchoreo.dev/v1alpha1
kind: ReleaseBinding
metadata:
  name: inkwell-worker-production
spec:
  owner: {projectName: inkwell, componentName: inkwell-worker}
  environment: production
  componentTypeEnvironmentConfigs:
    sizeClass: medium

It lives in the application repository, in openchoreo/bindings-<environment>.yaml, and reaches the cluster on the next push. How big a workload is in production is a property of the application, belongs beside its code, and should be reviewed as a diff.

small chosen and small by default are different facts

The console renders them differently, and this is deliberate. An outlined chip means somebody declared that class on this binding; a flat, faint chip means nobody has said anything and the type's default applies. They are the same word and a different fact: one is a decision, and a decision can be revisited.

The Size column on any environment. A dim chip is a default; a bright one is a choice.

See the sizes per environment →

Overriding it outright

An explicit resources block on the binding wins over the class entirely, and the component then renders as custom rather than as any class:

  componentTypeEnvironmentConfigs:
    resources:
      requests: {cpu: 500m, memory: 700Mi}
      limits:   {cpu: 1, memory: 2Gi}

Prefer a class. custom cannot be compared across environments, does not appear in the ladder, and is the reason the size chip has a fourth state.

The cell quota is the real ceiling

Each project cell has a ResourceQuota capping the sum of its containers' memory limits — 6Gi on this platform. A size class is charged against it, and a promotion is refused before it starts if the projection does not fit (platform/project-flow capacity_check).

warning

Two pieces of arithmetic bite here and both are easy to get wrong by hand.

Kubernetes charges a pod max(sum(containers), max(initContainers)), not the sum of the two — so a Rails pod's 768Mi db-prepare init container contributes nothing on top of the containers that follow it.

And a rollout surges: maxSurge rounds up to 1, maxUnavailable rounds down to 0, so during a resize the cell holds the old pod at its old size and the new pod at its new one at the same time.

That pair is why inkwell-worker is medium in production and not large: production already holds about 2.7Gi across five components, and large plus the surge does not fit under 6Gi.

What a resize does

It changes the pod template, so it rolls the pods — which resizing a container does anyway. The class is stamped onto the pod template as platform.jung.town/size-class, as an annotation rather than a label, because spec.selector.matchLabels is immutable and a generated label in the selector is a way to make a Deployment unpatchable.

The stamp is what lets a cross-environment comparison render a size difference as one named line — small → medium — instead of four numeric ones with no explanation.

Open a project, then Compare. A size difference shows as the class it is.

Compare two environments →