---
id: platform/scaling
title: Scaling
description: Set instance counts or autoscaling policies, understand plan and disk constraints, and verify capacity changes.
keywords: [bex, scaling, autoscaling, instances, replicas]
last_updated: 2026-09-23
---

Scaling changes how many application instances run. A plan controls the
resources available to each instance; adding instances increases concurrency,
while changing the plan changes the capacity of each process.

Use instance scaling for web services, private services, and workers. Cron
schedules and static publishing have different execution models. More replicas
help only when the application can divide work or requests between them.

## Before scaling

Keep shared sessions and durable data outside disposable process memory or
container files. Confirm your database and queues can handle additional
connections. Rehearse aggregate Postgres clients with the
[Postgres pooling](./postgres-pooling.md) sample before raising replica counts.
Choose a plan that supports the desired count: Bex's current
`free` service tier is capped at one instance.

A service with a [persistent disk](./persistent-disks.md) is constrained to a single instance and uses
`Recreate` deployments. Do not apply a multi-instance or autoscaling example
to that service. Machine provisioning and application scaling are also separate:
requested instances can remain pending if the cluster has insufficient capacity.

## Set a fixed count

For an existing service on a suitable plan, set `BEX_API_URL`, an authorized
`BEX_TOKEN`, and `SERVICE_ID`, then:

```bash
curl --fail-with-body -X POST "$BEX_API_URL/v1/services/$SERVICE_ID/scale" \
  -H "Authorization: Bearer $BEX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"numInstances":3}'
```

The manual scale endpoint accepts counts from 1 to 100, subject to plan and
workload constraints. Use [suspend](./app-lifecycle.md) to stop a service instead
of sending zero. This is a scale action, not a new source build; wait for the
requested instances to become ready after submission.

For configuration managed in Git, use `numInstances`:

```yaml
services:
  - name: api
    type: web
    runtime: docker
    repo: https://github.com/your-org/api
    plan: starter
    numInstances: 3
```

Replace the source and choose a plan available on your instance. Follow the
[Blueprint guide](./app-resource.md) to validate and apply the change.

## Enable autoscaling

Autoscaling adjusts the instance count within a minimum and maximum range
using CPU or memory utilization. For an existing service on an eligible plan:

```bash
curl --fail-with-body -X PUT "$BEX_API_URL/v1/services/$SERVICE_ID/autoscaling" \
  -H "Authorization: Bearer $BEX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"minInstances":2,"maxInstances":6,"targetCPUPercent":70}'
```

The API uses **`minInstances` and `maxInstances`**. Kubernetes App resources
use `minReplicas` and `maxReplicas`; do not send those CR field names to REST.
At least one CPU or memory target is required, with a value from 1 to 100.
The maximum is also subject to the service plan's cap.

A Blueprint can express the same policy:

```yaml
services:
  - name: api
    type: web
    runtime: docker
    repo: https://github.com/your-org/api
    plan: starter
    scaling:
      minInstances: 2
      maxInstances: 6
      targetCPUPercent: 70
```

Choose either fixed `numInstances` or `scaling` for a clear desired policy.
Review Blueprint validation before applying policy changes to an existing service.

The operator evaluates usage periodically (currently every 30 seconds) and
stabilizes scale-down decisions over five minutes. Targets are percentages of
the instance's resource limits. If both targets are configured, the larger
capacity recommendation wins. Missing metrics cannot establish a useful scaling
recommendation; inspect metrics availability if the count is not changing.

The controller floors active autoscaling at one instance, even though the API
can accept a minimum of zero. Do not use `minInstances: 0` as a guarantee of
scale-to-zero; suspension and [idle hibernation](./idle-services.md) are
separate lifecycle features.

## Inspect or disable the policy

```bash
curl --fail-with-body "$BEX_API_URL/v1/services/$SERVICE_ID/autoscaling" \
  -H "Authorization: Bearer $BEX_TOKEN"

curl --fail-with-body -X DELETE "$BEX_API_URL/v1/services/$SERVICE_ID/autoscaling" \
  -H "Authorization: Bearer $BEX_TOKEN"
```

Disable autoscaling before returning to a fixed manual count. Then set and
verify the count you want; do not assume the last autoscaled count is your
intended manual configuration.

## Verify the result

Check both requested and ready instances, application latency/error rates,
CPU/memory, and dependency load. If instances remain pending, inspect scheduling
and resource capacity. If they start but fail health checks, fix the application
readiness issue before adding more. If scaling raises database load without
improving throughput, the bottleneck may be outside the application processes.

See [Metrics](./metrics.md), [Health checks](./health-checks.md), and
[How deploys work](./how-deploys-work.md). More instances also mean more
metered `instance_seconds`: confirm the cost impact in
[Month-to-date usage](./usage.md) before the invoice.
