Infrastructure
Configure your Chalk Kubernetes cluster
Under Infrastructure > Resources, you will find the Cloud Resource Configuration page, where you can
configure the resources allocated to each of your Chalk services and most other system-related settings.
Your Chalk environment consists of multiple Services, each of which serves a different purpose and represents
a different kind of feature, compute, or background workload. For instance, the Query Server (also referred to
as the engine) is responsible both for serving online inference queries and also for serving dataplane data requests,
the Job Queue Consumer is responsible for pulling jobs like offline queries (backfills, dataset ingestions, etc.) from a
queue and running them, and the Streaming Server handles execution and coordination of Stream Resolvers.
Chalk also provides a higher-level isolation concept called a Resource Group. Each resource group provides its own
independently-configurable set of Chalk Services, and is backed by logically-independent Kubernetes deployments. This provides
isolation between different kinds of workloads so that they don’t interfere with each other. For instance, you may want to run
low-latency high-volume IO-bound inference queries against the Query Server in one resource group, and slower CPU-bound analytics
queries against the Query Server in a separate resource group. You may also want to configure several different resource groups
of different sizes so that you can target long-running offline workloads against machines of different sizes.
All Chalk environments will have a Default resource group. New resource groups can be added and configured independently
from each other in the Cloud Resource Configuration pane.
For online inference queries, each resource group exposes its own URL for the query client to use, and for offline workloads like
Offline Query, a resource group can be specified in the resource_request parameter of the ChalkClient.offline_query() method
and other related methods.
Each Chalk Service can be independently configured to consume different amounts of compute resources, depending on your needs.
The primary way to configure resource consumption for a Service is to select a Chalk Machine Type from the dropdown selector
on the Cloud Resource Configuration page when viewing the individual Service configuration pane. This guarantees that each replica
of the Service will have full access to the listed CPU and memory amounts, and will run isolated on its own cloud instance, ensuring
that separate services cannot interfere with each other. Different machine types are available for online, offline, and other kinds of
workloads, since these workloads tend to have different performance requirements.
In addition to configuring the resources allocated to each replica of a Service, you can configure the Service’s scaling properties.
Chalk supports CPU-utilization-based autoscaling as the default, as well as time-based pre-scaling and offset/backlog-based scaling for
stream resolver execution. You can configure the min and max instance counts as well as the target cpu % for each Service
independently for each Resource Group, so different workloads can scale independently.
More information about Chalk Machine Types can be found on the detailed docs page.
Chalk supports autoscaling of resources in your environment in order to optimize resource usage.
For the branch server, you can set the Auto Shutdown Period, which will determine the duration of
inactivity (no queries or deploys) after which the branch server pod will be automatically shut down.
For the Query Server and gRPC Query Server which are used to serve your production traffic, you can configure autoscaling based on a CPU utilization target or you can set time-based pre-scaling.
For stream resolvers, you can also set an autoscaling policy based on the stream queue message backlog. The underlying system used to define scaling policies is KEDA.
To enable CPU-based autoscaling, you can set the Target CPU % in the Cloud Resource Configuration.
Chalk will then monitor the CPU utilization of the service and scale the number of instances up or down
based on the target CPU percentage. In order for CPU-based autoscaling to work, you must have the
Min Instances, Max Instances, and Target CPU% all set for the service.
To enable scheduled autoscaling, you can define scaling triggers for each service configuration in the
Cloud Resource Configuration page. For each service in a resource group that you would like to autoscale,
define the Min Instances and Max Instances under Scaling. Then, under the ADVANCED section, under
Time-based Scaling, you can define one or more Scaling Triggers. For each Scaling Trigger, you can define
the desired number of replicas, the start time, the end time, and the timezone for the schedule. The start
time and end time are defined using standard cron syntax. The underlying implementation of time-based scaling
is the KEDA Cron scaler.

Scheduled capacity and CPU-based autoscaling work best together: the schedule handles known demand, while CPU autoscaling provides additional capacity for unplanned or longer-lived demand. They do not replace capacity planning. If traffic can arrive as a large synchronized fan-out, consider spreading it over a short interval with batching or jitter where your application permits. This reduces the amount of capacity that must be available at the exact start of the burst.
CPU-based autoscaling is well-suited to sustained or unexpected changes in demand, but it is reactive. When traffic rises abruptly, a new pod must still be created, scheduled, and pass readiness checks before it can serve requests. If the cluster needs to provision a node as well, this can take longer. Requests that arrive at the start of a burst may therefore see increased latency before autoscaling adds capacity.
Allow several minutes of lead time for a scheduled increase when additional nodes might be needed. The actual time depends on signal evaluation, pod scheduling, node provisioning, image startup, and readiness checks. Exact times vary by environment, so use the p95 time from a scaling signal to a ready replica to choose the schedule’s lead time.
When your production traffic has a recurring peak (for example, a daily batch, campaign, or scheduled workflow), use scheduled autoscaling to make capacity available before the peak begins:
Max Instances above the scheduled baseline so CPU-based autoscaling
can still respond to demand beyond the expected peak. The maximum should be tested against the resource
requests, nodepool capacity, and any downstream dependencies.The same resource-group settings exposed in Infrastructure > Resource Configuration can be
read and edited from the CLI. This is useful for codifying environment configuration in
source control, scripting per-environment changes (image bumps, replica counts, autoscaler
overrides), and applying targeted edits without leaving the terminal.
The full configuration lives on the environment record under spec_config_json. A typical
shape looks like:
{
"id": "tm-abc123",
"name": "production",
"spec_config_json": {
"services": {
"engine-grpc": {
"min_instances": 2,
"max_instances": 10,
"target_cpu_utilization_percentage": 50,
"chalk_machine_type": "small"
},
"branch": { "chalk_machine_type": "large" }
},
"resource_groups": [
{
"resource_group_name": "high-throughput",
"services": {
"engine-grpc": {
"min_instances": 5,
"chalk_machine_type": "large-highmem",
"engine_image_uri_override": "gcr.io/chalk/engine:sha-abc123"
}
}
}
]
}
}The fields directly under spec_config_json (alongside resource_groups) make up the
default resource group. Each entry in resource_groups is a named override group
keyed by resource_group_name.
There are three CLI commands you’ll use:
| Command | What it does |
|---|---|
chalk environment config --json | Print the active environment, including spec_config_json, as JSON. |
chalk environment update | Replace whole top-level fields of an environment from a YAML file. |
chalk environment patch | Apply a targeted JSON-merge or RFC 6902 patch to a single resource group. |
environment is aliased to env, so chalk env config, chalk env update, and chalk env patch
all work.
Use chalk environment config --json to dump the active environment. With --json the
command emits the full environment proto as JSON; without it you get a YAML rendering.
$ chalk environment config --json > env.json
# Inspect just the resource groups
$ chalk environment config --json | jq '.spec_config_json.resource_groups'
# Find a specific group
$ chalk environment config --json \
| jq '.spec_config_json.resource_groups[] | select(.resource_group_name=="high-throughput")'
# Grab the environment id (needed by `chalk environment update`)
$ chalk environment config --json | jq -r '.id'The active environment is determined by your CLI auth context — switch with chalk environment <name>
before running the command, or use --env-id to scope individual operations.
chalk environment updatechalk environment update overwrites whole top-level fields on the environment from a YAML
config file. Use this when you want to replace the resource-group configuration wholesale
(for example, deploying a checked-in environment.yaml from a repo).
$ chalk environment update \
--env-id env_abc123 \
--config-file environment.yamlThe YAML file may contain any subset of the following keys; only keys that are present get sent in the update mask, so omitted keys are left untouched on the server:
| YAML key | Effect |
|---|---|
specs_config | Replaces the whole spec_config_json blob (resource groups, services, autoscalers, …). |
additional_env_vars | Replaces the environment-level env-var map (engine configuration variables) |
private_pip_repositories | Sets the private PyPI URL string. |
online_store_secret | Sets the secret name for the online store. |
feature_store_secret | Sets the secret name for the feature/offline store. |
A minimal environment.yaml that edits only resource groups looks like:
specs_config:
services:
engine-grpc:
min_instances: 2
max_instances: 10
target_cpu_utilization_percentage: 50
chalk_machine_type: small
resource_groups:
- resource_group_name: high-throughput
services:
engine-grpc:
min_instances: 5
max_instances: 20
engine_image_uri_override: gcr.io/chalk/engine:sha-abc123specs_config is a wholesale replacement: anything you omit under it is removed. The
typical workflow is therefore “fetch → edit → apply”:
$ chalk environment config --json | jq '.spec_config_json' > spec.json
# edit spec.json (or convert to YAML and edit)
$ yq -P spec.json > environment.yaml # wrap under `specs_config:` first
$ chalk environment update --env-id $(chalk environment config --json | jq -r .id) \
--config-file environment.yamlFor per-field edits without round-tripping the whole config, prefer chalk environment patch.
chalk environment patchchalk environment patch applies a kubectl-style patch to a single resource group
within the active environment’s spec_config_json. The rest of the spec config is
preserved as-is, which makes it safe to script narrow changes (image bumps, replica
overrides, autoscaler tweaks) without touching unrelated services.
The command operates on the environment selected in your CLI auth context — there is no
--env-id flag. Switch with chalk environment <name> first if needed.
There are two input modes.
--service + --setThe fastest way to bump a single field on a single service inside a resource group:
$ chalk environment patch \
--resource-group high-throughput \
--service engine-grpc \
--set engine_image_uri_override=gcr.io/chalk/engine:sha-abc123Each --set key=value entry nests under services.<service> of the resource group.
You can pass --set multiple times. Values parse as JSON when they look like JSON
(true, 42, "quoted", [1,2,3]); otherwise they’re treated as strings. Dotted keys
build nested objects, so:
$ chalk environment patch \
--resource-group high-throughput \
--service engine-grpc \
--set min_instances=5 \
--set max_instances=20 \
--set resources.requests.cpu=500m \
--set resources.requests.memory=1Giis equivalent to a merge patch of:
{
"services": {
"engine-grpc": {
"min_instances": 5,
"max_instances": 20,
"resources": { "requests": { "cpu": "500m", "memory": "1Gi" } }
}
}
}--patch / --patch-fileFor anything the sugar can’t express (multiple services in one shot, deletes, array
operations) supply a raw patch body. The --patch-type flag selects the format:
merge (default) — RFC 7396 JSON merge patch. Setting a field to null deletes it.json — RFC 6902 JSON patch (an array of {op, path, value} operations).# Inline merge patch
$ chalk environment patch --resource-group high-throughput \
--patch-type merge \
--patch '{"services":{"engine-grpc":{"engine_image_uri_override":"gcr.io/chalk/engine:sha-abc123"}}}'
# Delete a field by setting it to null in a merge patch
$ chalk environment patch --resource-group high-throughput \
--patch '{"services":{"engine-grpc":{"engine_image_uri_override":null}}}'
# RFC 6902 patch from a file
$ chalk environment patch --resource-group high-throughput \
--patch-type json --patch-file replace-image.json
# Read the patch from stdin
$ jq -n '{services:{"engine-grpc":{min_instances:5}}}' \
| chalk environment patch --resource-group high-throughput --patch-file -Sugar (--service/--set) and raw (--patch/--patch-file) modes are mutually exclusive.
default resource groupThe fields directly under spec_config_json (the ones that live alongside resource_groups)
are treated as the default resource group. Pass --resource-group default to patch
them:
$ chalk environment patch --resource-group default \
--service branch \
--set request.cpu=2 \
--set request.memory=6GiPatches targeting default are not allowed to touch the resource_groups key — patch
each named group by name instead.
Every patch prints a JSON diff of the resource group before vs. after, then prompts for
confirmation before applying. Use --dry-run to see the diff and exit without applying,
and --yes (or -y) to skip the prompt in scripts:
# Preview only
$ chalk environment patch --resource-group high-throughput \
--service engine-grpc --set min_instances=5 --dry-run
# Apply non-interactively (CI)
$ chalk environment patch --resource-group high-throughput \
--service engine-grpc --set min_instances=5 --yesThe patch fails fast if the named resource group doesn’t exist, if the patch body is
invalid JSON, if the merge would change resource_group_name, or if a default patch
would mutate resource_groups.
Under Settings > Shared Resources, environment admins can view and manage shared resources across
different services in your environment, including the Metrics Database configuration, Gateway
configuration, and Background Persistence configurations. You can adjust these configurations here
if, for example, you need to scale up your Metrics Database to handle larger volumes of data, or
if you drastically increase the volume of data being written to the online or offline stores.
For enabling autoscaling of Background Persistence workers to handle variations in data being persisted online and offline, you can set the Horizontal Pod Autoscaling (HPA) Settings in the Background Persistence Configuration tab. In order to enable autoscaling, it is required to set
Under Integrations > Config Variables, you can view and edit global configuration variables for
data plane services like the query server or offline query consumer for your environment. Please reach
out to the Chalk team if you have any questions about which configuration variables to set for your use case.
Directly setting configuration variables is an advanced operation designed for enabling experimental or advanced
features, and using the existing Resources pane in Infrastructure > Resources is preferable in most cases.
Configuration variables are directly injected into the query server or offline query jobs (the Chalk execution engine runtime) for the purpose of configuration.
Values that you would like to make available as environment variables in your resolver runtime should be set under Integrations > Secrets instead.
The legacy resources configuration system allows for extremely flexible configuration of your Chalk workloads, but can
also lead to issues provisioning resources that can be hard to debug. It remains available but should be treated as an
advanced form of configuration and should only be used when necessary for special cases not covered by Chalk Machine Types.
The primary controls that are available for configuring individual Chalk Services in this system are effectively passthroughs for
the underlying Kubernetes API: requests and limits. Each of these allows specifying amounts of an available resource: cpu,
memory, and ephemeral storage. Since the underlying system is the Kubernetes Resource API,
specifying a request reserves the specified amount of that resource; any node that the service schedules on must have at least that
much of that resource available; the available amount is calculated by adding up all the other resource requests (reservations) on that machine
along with Kubernetes overhead and subtracting that from the available amount of the resource on the machine.
For example, if there is an 8-cpu machine (node) in the cluster, and there are 3 pods running on the node requesting 2 cpu each, the node
will have (8 - 2*3 - overhead)=~1.5cpu available. A new pod with a 1-cpu request may be able to schedule on the node, but a new pod with a 2-cpu
request will not, because of the overhead. An interesting consequence of the overhead that is always present is that a request for 2cpu or 4Gi of memory
can never schedule on a node of that size; typically it will end up scheduling on a node twice the size (4cpu or 8Gi of memory).
This is just a reservation system for ensuring that too many pods don’t schedule on the same node; nothing about the requests actually requires the
pod running your workload to use only the requested amount, and this applies to all pods running on a node. Setting limits will instruct Kubernetes
to reach in and terminate a pod that is using “too much” memory, or throttle a pod using “too much” CPU. With Chalk Machine Types, requests and limits
are set automatically, so you’ll never have to worry about fine-tuning resource requests to make sure your workload can schedule.
The legacy resources configuration system contains a few different techniques for isolating your workloads, depending on the cloud provider you’re using:
Instance Type and/or a NodepoolGCP Nodepool or a GCP Machine Family.Isolate from other Chalk Services checkboxAWS and Azure clusters use Karpenter, a flexible node provisioning system that creates nodes on-demand. Karpenter provides
the Nodepool as an available tool for isolation and configuration; Nodepools can be configured with an arbitrary and flexible set of constraints that
determine what kinds of nodes they can produce. This is very flexible but can lead to internally-unsatisfiable configuration that prevents workloads from scheduling.
In clusters using Karpenter, you can explicitly select a Nodepool via the Nodepool selector and you can independently select an Instance Type with the Instance Type
selector. Using the Instance Type selector ensures that you will schedule on exactly that type of instance.
GCP Nodepools are different; each nodepool can only produce one Instance Type, so you can’t independently select Instance Type and Nodepool for your
Services. If your cluster is set up with NAP then you can also
select a GCP Machine Family, although this doesn’t give you as much control.
In all clusters, you can use the Isolate from other Chalk Services checkbox to set an anti-affinity that prevents your selected Service from running on the same
node alongside other services; this is recommended for production configurations in order to avoid cross-service interference.
As you can see, this system is very flexible, but it can also be hard to debug: it’s possible to set various kinds of unsatisfiable configuration.
For instance, you can configure a Karpenter Nodepool that only makes m6a.2xlarge instances, then explicitly request that nodepool, explicitly request an instance
type of r8i.4xlarge, and independently configure a cpu request of 32. These configurations are all mutually incompatible: the Nodepool can’t make instances of the
required type, and the 32-cpu request can’t fit on either of the specified instance types (it can’t fit on the larger machine because of overhead).
When you use a Chalk Machine Type, all of these settings are chosen automatically, including requests, limits, the Instance Type, the Nodepool, and the isolation setting.
For this reason, selecting a Chalk Machine Type is recommended unless custom configuration is required. More information is available on the detailed docs page.
Configuring and using custom nodepools in AWS, GCP, and Azure environments is a legacy feature that will eventually be deprecated. Selecting a Chalk Machine Type
instead of using custom resource configuration ensures that your workloads will always run isolated from each other on an appropriately sized cloud instance.
Under Infrastructure > Nodepools, you can view and configure the nodepools
used to run your Chalk environment. A NodePool defines constraints on the Kubernetes nodes that can be
scheduled in your Kubernetes cluster. Notably, you can select the machine families that you would like
to use in your deployment, as different machine families are optimized for different workloads. You can
also set a CPU limit on your NodePool. This page will allow you to provision the resources and machine
families that are best suited for your workload and usage requirements.
When configuring a nodepool, you’ll notice two checkboxes, “Isolate this nodepool” and “Restrict to Chalk workloads only”.
“Isolate this nodepool” being set means that workloads cannot
be scheduled in the nodepool unless they are specifically configured to do so under
Infrastructure > Resource Configuration, which can be useful to minimize interference or reserve certain instance
types for specific workloads. This causes the nodepool to have the Isolated label.
“Restrict to Chalk workloads only” being set means that unrelated non-Chalk workloads in the cluster
will not be allowed to schedule on this nodepool, which can be useful for environments deployed into pre-existing
clusters that also run unrelated workloads. This ensures that those non-Chalk workloads do not inadvertently consume
Chalk credits. This causes the nodepool to have the Restricted label.
You may also notice that the nodepools you create through the Chalk dashboard automatically have the Chalk label.
This indicates that the nodepool makes Chalk-managed nodes, whose uptime contributes to credit usage. All nodepools
created through the Chalk dashboard automatically have this label, although the dashboard will also display nodepools
without it. Chalk workloads are only allowed to schedule on nodes from nodepools with the Chalk label.
More details about these labels can be found in the Billing Documentation.
Once you have configured your Nodepools, under Infrastructure > Resource Configuration, you can select the Nodepool
to use for each service in each resource group in your environment, as well as the Pod Disruption
Budget.