steep.yaml
The file steep.yaml contains the main configuration of Steep. This page
describes all configuration keys and values you can set.
Note that keys are specified using the dot notation. You can use them as they are given here or use YAML notation instead. For example, the following configuration item
is identical to:
You may override items in your configuration file with environment variables.
This is particularly useful if you are using Steep inside a Docker container.
The environment variables use a slightly different naming scheme. All variables
are in capital letters and dots are replaced by underscores. For example,
the configuration key steep.http.host becomes STEEP_HTTP_HOST and
steep.cluster.eventBus.publicPort becomes STEEP_CLUSTER_EVENTBUS_PUBLICPORT.
You may use YAML syntax to specify environment variable values. For example,
the array steep.agent.capabilities can be specified as follows:
General configuration
steep.tmpPath- The path to a directory where temporary files should be stored during processing. Steep generates names for the outputs of execute actions in a workflow. If the
storeflag of an output parameter isfalse(which is the default), the generated filename will be relative to this temporary directory. steep.outPath- The path to a directory where output files should be stored. This path will be used instead of
steep.tmpPathto generate a filename for an output parameter if itsstoreflag istrue. steep.overrideConfigFile- The path to a file that keeps additional configuration. The values of the
overrideConfigFilewill be merged into the main configuration file, so it basically overrides the default values. Note that configuration items in this file can still be overridden with environment variables. This configuration item is useful if you don’t want to change the main configuration file (or if you cannot do so) but still want to set different configuration values. Use it if you run Steep in a Docker container and bind mount theoverrideConfigFileas a volume. steep.services- The path to the configuration files containing service metadata. Either a string pointing to a single file, a glob pattern (e.g.
**/*.yaml), or an array of files or glob patterns. steep.macros- The path to the configuration file(s) containing macros. Either a string pointing to a single file, a glob pattern (e.g.
**/*.yaml), or an array of files or glob patterns. steep.plugins- The path to the configuration file(s) containing plugin descriptors. Either a string pointing to a single file, a glob pattern (e.g.
**/*.yaml), or an array of files or glob patterns.
Cluster settings
Use these configuration items to build up a cluster of Steep instances. Under
the hood, Steep uses Vert.x and Hazelcast,
so these configuration items are very similar to the ones found in these two
frameworks. To build up a cluster, you need to configure an event bus connection
and a cluster connection. They should use different ports. host typically
refers to the machine your instance is running on and publicHost or
publicAddress specify the hostname or IP address that your Steep instance will
use in your network to advertise itself so that other instances can connect to
it.
For more information, please read the documentation of Vert.x and Hazelcast.
steep.cluster.eventBus.hostThe IP address (or hostname) to bind the clustered eventbus to
Default: Automatically detected local network interface
steep.cluster.eventBus.portThe port the clustered eventbus should listen on
Default: A random port
steep.cluster.eventBus.publicHostThe IP address (or hostname) the eventbus uses to announce itself within in the cluster
Default: Same as
steep.cluster.eventBus.hoststeep.cluster.eventBus.publicPortThe port that the eventbus uses to announce itself within in the cluster
Default: Same as
steep.cluster.eventBus.portsteep.cluster.hazelcast.clusterNameAn optional cluster name that can be used to separate clusters of Steep instances. Two instances from different clusters (with different names) cannot connect to each other.
By default, no cluster name is set, which means all instances can connect to each other. However, a Steep instance without a cluster name cannot connect to a named cluster.
Heads up: if you have a cluster name set and you’re using a cloud connection to deploy remote agents on demand, make sure these Steep instances use the same cluster name. Otherwise, you won’t be able to connect to them.
steep.cluster.hazelcast.publicAddressThe IP address (or hostname) and port Hazelcast uses to announce itself within in the cluster
steep.cluster.hazelcast.portThe port that Hazelcast should listen on
steep.cluster.hazelcast.interfacesA list of IP address patterns specifying valid interfaces Hazelcast should bind to
steep.cluster.hazelcast.membersA list of IP addresses (or hostnames) of Hazelcast cluster members
steep.cluster.hazelcast.tcpEnabledtrueif Hazelcast should use TCP to connect to other instances,falseif it should use multicastDefault:
falsesteep.cluster.hazelcast.placementGroupNameAn optional name specifying in which group this Hazelcast member should be placed. Steep uses distributed maps to share data between instances. Data in these maps is partitioned (i.e. distributed to the individual cluster members). In a large cluster, no member keeps all the data. Most nodes only keep a small fraction of the data (a partition).
To make sure data is not lost if a member goes down, Hazelcast uses backups to distribute copies of the data across the cluster. By specifying a placement group, you can control how Hazelcast distributes these backups. Hazelcast will always prefer creating backups in a group that does not own the data so that if all members of a group go down, the other group still has all the backup data.
Examples for sensible groups are racks, data centers, or availability zones.
For more information, see the following links:
- https://docs.hazelcast.com/hazelcast/5.1/architecture/data-partitioning
- https://docs.hazelcast.com/hazelcast/5.1/clusters/partition-group-configuration
- https://docs.hazelcast.com/hazelcast/5.1/data-structures/backing-up-maps
Note that if you configure a placement group name, all members in your cluster must also have a placement group name. Otherwise, you will receive an exception about mismatching configuration on startup.
steep.cluster.hazelcast.liteMembertrueif this instance should be a Hazelcast lite member. Lite members do not own any in-memory data. They are mainly used for compute-intensive tasks. With regard to Steep, an instance with a controller and a scheduler should not be a lite member, because these components heavily rely on internal state. A Steep instance that only contains an agent and therefore only executes services, however, could be a lite member. See the architecture section for more information about these components.Your cluster cannot consist of only lite members. Otherwise, it is not able to maintain internal state at all.
Note that since lite members cannot keep data, they are not suitable to keep backups either. See
steep.cluster.hazelcast.placementGroupNamefor more information. For reasons of reliability, a cluster should contain at least three full (i.e. non-lite) members.steep.cluster.lookupOrphansIntervalThe interval at which Steep’s main thread looks for orphaned entries in its internal remote agent registry (specified as a duration). Such entries may (very rarely) happen if there is a network failure during deregistration of an agent. You normally do not have to change this configuration.
Default: 5m
steep.cluster.hazelcast.restoreMembersOnStartup.enabledtrueif Steep should try to load IP addresses of possibly still running VMs from its database during startup and add them tosteep.cluster.hazelcast.members. This is useful if a Steep instance has crashed and should be reintegrated into an existing cluster when it’s back.Default:
falsesteep.cluster.hazelcast.restoreMembersOnStartup.defaultPortIf
steep.cluster.hazelcast.restoreMembersOnStartup.enabledistrue, potential Hazelcast cluster members will be restored from database. This configuration item specifies on which Hazelcast port these members are listening.steep.cluster.hazelcast.splitBrainProtection.enabledtrueif split-brain protection should be enabled. This mechanism makes sure the cluster is only able to operate if there are at leastnmembers, wherenis defined bysteep.cluster.hazelcast.splitBrainProtection.minClusterSize. If there are less thannmembers, Steep instances in the cluster will not be able to access cluster-wide data structures and stop to operate until the issue has been resolved.This mechanism protects against so-called split-brain situations where one part of the cluster loses connection to another part, and the cluster is therefore split into different partitions. If one partition becomes too small, it should stop operating to avoid doing any harm.
See the Hazelcast documentation for more information.
Default:
falsesteep.cluster.hazelcast.splitBrainProtection.minClusterSizeThe minimum number of members the cluster must have to be able operate if split-brain protection is enabled.
Recommendation: Your cluster should have an odd number of members. The minimum cluster size should be even and represent the majority of your cluster. For example, if your cluster has 7 nodes, set this value to 4. This makes sure that when a split-brain situation happens, the majority of your cluster will be able to continue operating while the smaller part will stop.
This configuration item does not have a default value. It must be set if
steep.cluster.hazelcast.splitBrainProtection.enableequalstrue.steep.cluster.hazelcast.splitBrainProtection.gracefulStartuptrueif the split-brain protection mechanism should only start to be in effect once the cluster has reached its minimum size. This allows the cluster to startup gracefully even if the member count is temporarily lower than the defined minimum.Default:
truesteep.cluster.hazelcast.splitBrainProtection.exitProcessAfterAn optional timeout (specified as a duration) defining how long a Steep instance may keep running after a split-brain situation has been detected. When the timeout is reached and the split-brain situation has not been resolved in the meantime, the Steep instance shuts itself down with exit code 16. This mechanism can be used to prevent a Steep instance from doing any harm when it is in a split-brain situation.
steep.cluster.kubernetes.enabledtrueif Hazelcast should try to automatically discover other members in a Kubernetes clusterDefault:
falsesteep.cluster.kubernetes.namespaceThe Kubernetes namespace where Steep is running. Falls back to the values of the environment variables
KUBERNETES_NAMESPACEorOPENSHIFT_BUILD_NAMESPACE, or to the pod’s namespace retrieved from/var/run/secrets/kubernetes.io/serviceaccount/namespace.steep.cluster.kubernetes.serviceNameAn optional service name to limit the cluster members to pods that are connected to a given service. If not specified, all pods in the namespace will be considered potential cluster members.
steep.cluster.kubernetes.serviceDnsInstead of specifying a namespace and a service name through
steep.cluster.kubernetes.namespaceandsteep.cluster.kubernetes.serviceName, you can also specify a DNS name in the formSERVICE-NAME.NAMESPACE.svc.cluster.local. Hazelcast will perform a DNS lookup to obtain the pod IP addresses of cluster members.
HTTP configuration
steep.http.enabledtrueif the HTTP interface should be enabledDefault:
truesteep.http.hostThe host to bind the HTTP server to
Default:
localhoststeep.http.portThe port the HTTP server should listen on
Default: 8080
steep.http.postMaxSizeThe maximum size of HTTP POST bodies in bytes
Default: 1048576 (1 MB)
steep.http.basePathThe path where the HTTP endpoints and the web-based user interface should be mounted
Default:
""(empty string, i.e. no base path)steep.http.allowRoutesA regular expression specifying a whitelist of enabled HTTP routes. Non-matching routes will be disabled. For example, the expression
/processchains.*enables all endpoints starting with/processchainsbut disables all others.Default:
.*(all routes are enabled)steep.http.cors.enabletrueif Cross-Origin Resource Sharing (CORS) should be enabledDefault:
falsesteep.http.cors.allowOriginA regular expression specifying allowed CORS origins. Use
* to allow all origins.Default:
"$."(match nothing by default)steep.http.cors.allowCredentialstrueif theAccess-Control-Allow-Credentialsresponse header should be returned.Default:
falsesteep.http.cors.allowHeadersA string or an array indicating which header field names can be used in a request.
steep.http.cors.allowMethodsA string or an array indicating which HTTP methods can be used in a request.
steep.http.cors.exposeHeadersA string or an array indicating which headers are safe to expose to the API of a CORS API specification.
steep.http.cors.maxAgeSecondsThe number of seconds the results of a preflight request can be cached in a preflight result cache.
Controller configuration
steep.controller.enabledtrueif the controller should be enabled. Set this value tofalseif your Steep instance does not have access to the shared database.Default:
truesteep.controller.lookupIntervalThe interval at which the controller looks for accepted submissions, specified as a duration.
Default: 2s
steep.controller.lookupMaxErrorsThe maximum number of consecutive errors (e.g. database connection issues) to tolerate when looking up the status of process chains of a running submission. If there are more errors, the submission will be aborted.
Default: 5
steep.controller.lookupOrphansIntervalThe interval at which the controller looks for orphaned running submissions (i.e. submissions that are in the status
RUNNINGbut that are currently not being processed by any controller instance), specified as a duration. If Steep finds such a submission it will try to resume it.Default: 5m
steep.controller.lookupOrphansInitialDelayThe time the controller should wait after startup before it looks for orphaned running submissions for the first time (specified as a duration). This property is useful if you want to implement a rolling update from one Steep instance to another.
Default: 0s
Scheduler configuration
steep.scheduler.enabledtrueif the scheduler should be enabled. Set this value tofalseif your Steep instance does not have access to the shared database.Default:
truesteep.scheduler.lookupIntervalThe interval at which the scheduler looks for registered process chains, specified as a duration.
Default: 20s
steep.scheduler.lookupOrphansIntervalThe interval at which the scheduler looks for orphaned running process chains (i.e. process chains that are in the status
RUNNINGbut that are currently not being processed by any scheduler instance), specified as a duration. Note that the scheduler also always looks for orphaned process chains when it detects that another scheduler instance has just left the cluster (regardless of the configured interval).Default: 5m
steep.scheduler.lookupOrphansInitialDelayThe time the scheduler should wait after startup before it looks for orphaned running process chains for the first time (specified as a duration). This property is useful if you want to implement a rolling update from one Steep instance to another. Note that the scheduler also looks for orphaned process chains when another scheduler instance has just left the cluster, even if the initial delay has not passed by yet.
Default: 0s
Agent configuration
steep.agent.enabledtrueif this Steep instance should be able to execute process chains (i.e. if one or more agents should be deployed)Default:
truesteep.agent.instancesThe number of agents that should be deployed within this Steep instance (i.e. how many executables the Steep instance can run in parallel)
Default: 1
steep.agent.idUnique identifier for the first agent instance deployed. Subsequent agent instances will have a consecutive number appended to their IDs.
Default: (an automatically generated unique ID)
steep.agent.capabilitiesList of capabilities that the agents provide
Default:
[](empty list)steep.agent.autoShutdownTimeoutThe time any agent instance can remain idle until Steep shuts itself down gracefully (specified as a duration). By default, this value is
0s, which means Steep never shuts itself down.Default: 0s
steep.agent.busyTimeoutThe time that should pass before an idle agent decides that it is not busy anymore (specified as a duration). Normally, the scheduler allocates an agent, sends it a process chain, and then deallocates it after the process chain execution has finished. This value is important if the scheduler crashes while the process chain is being executed and does not deallocate the agent anymore. In this case, the agent deallocates itself after the configured time has passed.
Default: 1m
steep.agent.outputLinesToCollectThe number of output lines to collect at most from each executed service (also applies to error output)
Default: 100
Runtime settings
steep.runtimes.docker.envAdditional environment variables that will be passed to containers created by the Docker runtime
Example:
["key=value", "foo=bar"]Default:
[](empty list)steep.runtimes.docker.pullDefines if the Docker runtime should pull the image before running a container
Default:
autoValid values:
always- Always pull the image before running, regardless of whether it is already present locally or not.missing- Only pull the image if it is not present locally.never- Never pull the image. If it is not present locally, the operation will fail.auto- Automatically decide whether to pull the image or not based on the following criteria (in this order):- If an image digest is given, the Docker runtime will behave as if the value were equal to
missing - If an image tag is given that is not
latest, the Docker runtime will behave as if the value were equal tomissing - If an image tag is given and it is
latest, the Docker runtime will behave as if the value wasalways - If no image tag is given, the Docker runtime will behave as if the value was
always
- If an image digest is given, the Docker runtime will behave as if the value were equal to
steep.runtimes.docker.volumesAdditional volume mounts to be passed to the Docker runtime
Example:
["/data:/data"]Default:
[](empty list)steep.runtimes.kubernetes.namespaceThe namespace in which Steep should create Kubernetes jobs and pods
Default:
defaultsteep.runtimes.kubernetes.envAn optional list of environment variables that should be injected into the containers started by the Kubernetes runtime. The list items should be Kubernetes environment variable objects. See the Kubernetes API reference for more information.
Example:
steep.runtimes.kubernetes.volumeMountsAn optional list of Kubernetes volume mount objects. The Kubernetes runtime mounts volumes specified here into the started containers. For example, the following configuration mounts a volume with the name
steep-tmp-pathto the path/tmp/steep/tmpinside the container:The volume
steep-tmp-pathmust be defined through the configuration itemsteep.runtimes.kubernetes.volumes.See the Kubernetes API reference for more information about volume mount objects.
steep.runtimes.kubernetes.volumesAn optional list of Kubernetes volumes. The Kubernetes runtime attaches these volumes to the started pods. For example, the following configuration defines a volume that refers to a host path:
You can also refer to persistent volume claims:
See the Kubernetes API reference for more information about volumes.
steep.runtimes.kubernetes.imagePullPolicyThe image pull policy for any job the Kubernetes runtime starts. See the Kubernetes documentation or the
imagePullPolicyparameter of theContainerobject in the Kubernetes API reference for more information.steep.runtimes.kubernetes.imagePullSecretsA list of image pull secrets for any job the Kubernetes runtime starts. See the Kubernetes documentation or the Kubernetes API reference for more information.
Database connection
steep.db.driverThe database driver
Valid values:
inmemory,postgresql,mongodbDefault:
inmemorysteep.db.urlThe database URL
steep.db.usernameThe database username (only used by the
postgresqldriver)steep.db.passwordThe database password (only used by the
postgresqldriver)steep.db.connectionPool.maxSizeThe maximum number of connections to keep open (i.e. to keep in the connection pool)
steep.db.connectionPool.maxIdleTimeThe maximum time an idle connection should be kept in the connection pool before it is closed
Cloud connection
steep.cloud.enabledtrueif Steep should connect to a cloud to acquire remote agents on demandDefault:
falsesteep.cloud.driverDefines which cloud driver to use
Valid values:
openstack(see the OpenStack cloud driver for more information)steep.cloud.createdByTagA metadata tag that should be attached to virtual machines to indicate that they have been created by Steep
steep.cloud.syncIntervalThe time that should pass before the cloud manager synchronizes its internal state with the cloud again, specified as a duration.
Default: 2m
steep.cloud.keepAliveIntervalThe time that should pass before the cloud manager sends keep-alive messages to a minimum of remote agents again (so that they do not shut down themselves), specified as a duration. See
minVMsproperty of the setups data model.Default: 30s
steep.cloud.setups.fileThe path to the file that describes all available setups. See setups.yaml.
steep.cloud.setups.creation.retriesA retry policy that specifies how many attempts should be made to create a VM from a certain setup (if creation fails) as well as possible (exponential) delays between those attempts.
Default:
steep.cloud.setups.lockAfterRetriesWhen the maximum number of attempts to create a VM from a certain setup has been reached (see
steep.cloud.setups.creation.retries), the setup will be locked and no other VM with this setup will be created. This parameter defines how long it will be locked, specified as a duration.Default: 20m
steep.cloud.timeouts.sshReadyThe maximum time the cloud manager should try to log in to a new VM via SSH (specified as a duration). The cloud manager will make a login attempt every 2 seconds until it is successful or until the maximum number of seconds have passed, in which case it will destroy the VM.
Default: 5m
steep.cloud.timeouts.agentReadyThe maximum time the cloud manager should wait for an agent on a new VM to become available (i.e. how long a new Steep instance may take to register with the cluster) before it destroys the VM again (specified as a duration).
Default: 5m
steep.cloud.timeouts.createVMThe maximum time that creating a VM may take before it is aborted with an error (specified as a duration).
Default: 5m
steep.cloud.timeouts.destroyVMThe maximum time that destroying a VM may take before it is aborted with an error (specified as a duration).
Default: 5m
steep.cloud.timeouts.provisioningThe maximum time each individual provisioning step (i.e. executing a provisioning script or uploading files) may take before it is aborted. Running provisioning commands will be killed after this timeout regardless of whether they are still active or not. This value is specified as a duration.
Default: 10m
steep.cloud.agentPoolAn array of agent pool parameters describing how many remote agents the cloud manager should keep in its pool how many it is allowed to create for each given set of capabilities.
Default:
[](empty list)
OpenStack cloud driver
steep.cloud.openstack.endpointOpenStack authentication endpoint
steep.cloud.openstack.usernameOpenStack username used for authentication
steep.cloud.openstack.passwordOpenStack password used for authentication
steep.cloud.openstack.domainNameOpenStack domain name used for authentication
steep.cloud.openstack.projectIdThe ID of the OpenStack project to which to connect. Either this configuration item or
steep.cloud.openstack.projectNamemust be set but not both at the same time.steep.cloud.openstack.projectNameThe name of the OpenStack project to which to connect. This configuration item will be used in combination with
steep.cloud.openstack.domainNameifsteep.cloud.openstack.projectIdis not set.steep.cloud.openstack.networkIdThe ID of the OpenStack network to attach new VMs to
steep.cloud.openstack.usePublicIptrueif new VMs should have a public IP addressDefault:
falsesteep.cloud.openstack.securityGroupsThe OpenStack security groups that should be attached to new VMs.
Default:
[](empty list)steep.cloud.openstack.keypairNameThe name of the keypair to deploy to new VMs. The keypair must already exist in OpenStack.
SSH connection to VMs
steep.cloud.ssh.usernameUsername for SSH access to VMs. Can be overridden by the
sshUsernameproperty in each setup. May even benullif all setups define their own username.steep.cloud.ssh.privateKeyLocationLocation of a private key to use for SSH
Log configuration
steep.logs.levelThe default log level for all loggers (console as well as file-based)
Valid values:
TRACE,DEBUG,INFO,WARN,ERROR,OFF.Default:
DEBUGsteep.logs.main.enabledtrueif logging to the main log file should be enabledDefault: false
steep.logs.main.logFileThe name of the main log file
Default:
logs/steep.logsteep.logs.main.dailyRollover.enabledtrueif main log files should be renamed every day. The file name will be based onsteep.logs.main.logFileand the file’s date in the formYYYY-MM-DD(e.g.steep.2020-11-19.log)Default:
truesteep.logs.main.dailyRollover.maxDaysThe maximum number of days’ worth of main log files to keep
Default: 7
steep.logs.main.dailyRollover.maxSizeThe total maximum size of all main log files in bytes. Oldest log files will deleted when this size is reached.
Default: 104857600 (100 MB)
steep.logs.processChains.enabledtrueif the output of process chains should be logged separately to disk. The output will still also appear on the console and in the main log file (if enabled), but there, it’s not separated by process chain. This feature is useful if you want to record the output of individual process chains and make it available through the process chain logs endpoint.Default:
falsesteep.logs.processChains.pathThe path where process chain logs will be stored. Individual files will will be named after the ID of the corresponding process chain (e.g.
aprsqz6d5f4aiwsdzbsq.log). If a process chain has been executed more than once (for example, due to a retry), the file name will include the run number (e.g.aprsqz6d5f4aiwsdzbsq.2.log).Default:
logs/processchainssteep.logs.processChains.groupByPrefixSet this configuration item to a value greater than
0to group process chain log files by prefix in subdirectories under the directory configured throughsteep.logs.processChains.path. For example, if this configuration item is set to3, Steep will create a separate subdirectory for all process chains whose ID starts with the same three characters. The name of this subdirectory will be these three characters. The process chainsapomaokjbk3dmqovemwaandapomaokjbk3dmqovemsqwill be put into a subdirectory calledapo, and the process chainao344a53oyoqwhdelmnawill be put intoao3. Note that in practice,3is a reasonable value, which will create a new directory about every day. A value of0disables grouping.Default: 0
Garbage collector configuration
steep.garbageCollector.enabledtrueif the garbage collector should be enabled. The garbage collector runs in the background and removes outdated objects from the database at the interval specified withsteep.garbageCollector.cronDefault: false
steep.garbageCollector.cronA UNIX-like cron expression specifying the interval at which the garbage collector should be executed. Cron expressions consist of six required fields and one optional field separated by a white space:
SECONDS MINUTES HOURS DAY-OF-MONTH MONTH DAY-OF-WEEK [YEAR].Use an asterisk
*to specify all values (e.g. every second or every minute). Use a question mark?forDAY-OF-MONTHorDAY-OF-WEEKto specify no value (only one ofDAY-OF-MONTHorDAY-OF-WEEKcan be specified at the same time). Use a slash/to specify increments (e.g.*/5for every 5 minutes).More information about the format can be found in the javadoc of the
org.quartz.CronExpressionclass.Example:
0 0 0 * * ?(daily at 12am)steep.garbageCollector.retention.submissionsThe maximum time a submission should be kept in the database after it has finished (regardless of whether it was successful or not). The time can be specified as a human-readable duration.
Default:
null(submissions will be kept indefinitely)steep.garbageCollector.retention.vmsThe maximum time a VM should be kept in the database after it has been destroyed (regardless of its status). The time can be specified as a human-readable duration.
Default:
null(VMs will be kept indefinitely)
Cache configuration
steep.cache.plugins.enabledtrueif the persistent compiled plugin cache should be enabled. Steep updates this cache on startup when it has first compiled a plugin script or when it detects that a previously compiled script has changed. On subsequent startups, Steep can utilize the cache to skip compilation of known plugins and, therefore, to reduce startup time.Default:
falsesteep.cache.plugins.pathThe path to a directory where Steep should store compiled plugin scripts if the persistent compiled plugin cache is enabled (see
steep.cache.plugins.enabled).Default:
.cache/plugins
Agent pool parameters
Steep’s cloud manager component is able to create virtual machines and deploy remote agent instances to it. The cloud manager keeps every remote agent created in a pool. Use agent pool parameters to define a minimum and maximum number of instances per provided capability set.