Deployment
Scale a service
Decide between more replicas, larger replicas and sleeping when idle, and apply the change.
A service has two dials. Replicas decide how many copies of it run at once, and the CPU and memory limits decide how large each copy is allowed to be. Scaling out means turning the first dial up; scaling up means turning the second.
Where each action lives:
| Surface | Scaling actions |
|---|---|
| Product App | Service → Settings carries all of it: the replica count under Replicas, Sleep when idle and its idle window under Scale to zero, the limits under Resources, and the two probes under Readiness probe and Liveness probe. Wake appears on the service panel while a service is sleeping. |
| CLI | Every action on this page: the CLI renders the same operation catalog as MCP. Run it from your own terminal. |
| MCP | Every action on this page. Tool names are linked per section. |
| REST | Every action on this page, under /deployment/…. Browse the endpoints. |
Choose the dial
| Dial | What it changes | Takes effect |
|---|---|---|
| Replica count | How many copies run and share the traffic. From 0 to 50. | In seconds. No rebuild, no release, no entry in the deploy history. |
| CPU and memory limits | How large one replica may become before it is throttled or restarted. | On the next deploy — the limits are part of the release. |
| Sleeping when idle | Whether the service drops to zero replicas on its own after an idle period, and wakes on the next request. | Immediately, after a 15-minute grace period. |
Read the two numbers that describe pressure — CPU and memory — together with the three that describe what your users experience: request rate, latency, and the share of requests failing with a server error. Which dial to turn follows from the pair:
| What you see | What it means | What to do |
|---|---|---|
| Latency climbing while CPU sits near the limit | The replicas are working as hard as they can. | Add replicas, or move up a preset. |
| Replicas restarting, memory climbing to the limit first | A memory ceiling, not a traffic problem. | More replicas will not help. Raise the memory limit and redeploy. |
| Request rate up, latency flat, resources comfortable | The service is absorbing the traffic. | Nothing. |
| Error rate up while resources are idle | The bottleneck is somewhere else, often a database or an upstream service. | Scaling this service will not fix it. |
Change the replica count
- Set the count anywhere from 0 to 50. A service is created with one.
- That is the whole change — nothing is rebuilt. New replicas start from the image of the release that is already running, and replicas being removed are asked to stop and given a grace period to finish the requests they are in the middle of. A service under load can be scaled out in seconds without a deploy.
Through the Lessly MCP server: deployment_service_scale. Token scope: a key with write access to the service.
A replica is one running copy of the current release. Every replica runs the same image with the same variables, and incoming requests are spread across all of them, so a service with three replicas handles roughly three times the traffic of the same service with one — as long as the work itself can be done in parallel.
- Nothing is pinned to a particular replica. Two requests from the same visitor may be served by two different copies, so anything you want to survive across requests belongs in a database, a cache or a volume rather than in the memory of one replica — see Choose where state lives.
- Scaling to zero replicas stops the service. The release history stays, the domains stay attached, the variables and volumes stay where they are — the service simply stops running and stops answering. Setting the count back above zero starts it again from the same release.
Change CPU and memory
- Pick a preset, or set the two values directly.
- Redeploy the service. Changing the limits records the new setting but does not resize what is running.
| Preset | CPU | Memory |
|---|---|---|
| XS | 250m | 512Mi |
| S (default) | 500m | 1Gi |
| M | 1 | 2Gi |
| L | 2 | 4Gi |
| XL | 4 | 8Gi |
| 2XL | 8 | 16Gi |
| Format | Bounds | |
|---|---|---|
| CPU | A number of cores (2, 0.5), or thousandths of a core with an m suffix (500m). | 50m to 32 cores |
| Memory | A whole number with a Ki, Mi, Gi or Ti suffix, or no suffix for bytes. | 64Mi to 32Gi |
A value outside that range, or one Lessly cannot parse, is rejected when you save it rather than at deploy time.
The limit is a ceiling for one replica, not a reservation, so a service that needs very little uses very little. What the ceiling changes is what happens under pressure: a replica that tries to go past its memory limit is stopped and started again, and one that wants more CPU than its limit is slowed down rather than stopped.
There is also a ceiling on how much one environment can run in total. If an environment is close to it, a deploy or a scale-out can fail to get the capacity it asked for; scaling something else down, or moving work into another environment, frees it up.
MCP: deployment_service_update to save the limits, then deployment_service_redeploy to apply them.
Switch on sleeping when idle
Scale-to-zero reaches the same end state as stopping a service by hand, on its own: switch it on for a service and it goes to sleep at zero replicas once it has sat idle long enough.
- Turn the setting on for the service. It is off until you do — no service starts sleeping on its own, and it is a per-service setting, so a worker that sleeps leaves everything else in its environment alone.
- Set the idle window, or leave it at 15 minutes. Anything from 5 to 60 minutes is accepted.
- Expect nothing to happen for a quarter of an hour. Switching the setting on comes with a grace period of fifteen minutes, during which the service will not sleep however quiet it is. A service that stays up right after you enable the setting is behaving as expected.
A request wakes it again:
- The request is not turned away. It waits while the service starts and is answered once it is up, and from there the service serves normally until it goes idle again.
- The service comes back on the release it was running, with the same variables and the same volumes, so waking changes nothing about what runs.
- A call from another service in the same environment wakes it in exactly the same way. A sleeping internal API that nothing outside the environment ever touches still comes back the moment one of your own services calls it.
The trade-off is that first request, and it is the whole trade-off. A sleeping service has nothing running, so it has to start before it can answer — seconds rather than milliseconds, and longer for anything slow to boot. Whoever sends that request waits for the start-up. That makes sleeping a good fit for staging, preview environments and internal tools that sit idle most of the day, and a poor fit for anything a user is waiting on. Think carefully before switching it on for a production service that has to feel instant.
You can also wake a service yourself, ahead of any request, with deployment_service_wake. It is the tidy way to warm a preview environment before you hand the link to someone, rather than leaving the first visitor to pay the start-up cost.
What a push does to a service at zero replicas
Sleeping and stopped look the same from outside — no replicas, nothing answering. They do not behave the same way when a service built from a repository receives a push to the branch it tracks.
| The service is | A push to its branch |
|---|---|
| Sleeping, after its idle period | Deploys as usual and brings the service back with it, at the replica count it had before it went to sleep. You do not have to wake it first. |
| Stopped by hand, with sleeping when idle on | Starts it again. This is the opposite of what most people expect: setting the replicas to zero yourself does not hold the service down against a push while that setting is on. Switch sleeping off before you stop it if you need it to stay down. |
| Stopped by hand, with sleeping when idle off | Ignored. The service stays down, nothing is built, and nothing appears in the deploy history or the deploy timeline. If you are waiting for a build that never shows up, this is the reason — the push was not lost. Set the replicas back above zero and redeploy when you want the new commit. |
Configure health checks
Two optional HTTP checks tell Lessly what a replica is doing, and both matter for scaling.
| Check | What it decides | What happens when it fails |
|---|---|---|
| Readiness | Whether a replica should be sent traffic. | The replica is left out of the rotation instead of answering with errors, and a new release is not promoted until it passes. |
| Liveness | Whether a replica that is already running is still alive. | The replica is restarted. This is what catches a copy that has deadlocked or wedged itself without exiting. |
Each check is a path on your service, such as /health, and takes the same settings:
| Setting | What it means | Readiness default | Liveness default |
|---|---|---|---|
path | The path to request. Required, must start with /. | — | — |
port | The port to request it on. | The service’s container port | The service’s container port |
initialDelaySeconds | How long to wait after a replica starts before checking. | 0 | 10 |
periodSeconds | How often to check. | 10 | 10 |
timeoutSeconds | How long a single check may take. | 1 | 1 |
failureThreshold | Consecutive failures before the replica is considered bad. | 3 | 3 |
successThreshold | Consecutive successes before it is considered good again. | 1 | 1 |
If you configure no checks, a replica is treated as ready as soon as its container starts. That is fine for a service that is genuinely ready immediately, and a poor fit for anything with a slow startup — without a readiness check, traffic arrives before the service can answer it.
Be careful with a liveness check that is too strict. A path that is slow under load, combined with a one-second timeout, will restart replicas exactly when the service is busiest and make the problem worse. A liveness path should do the least work that still proves the process is alive.
What scaling does during a deploy
When a new release rolls out, Lessly adds new replicas before it takes old ones away, so the number of replicas actually serving traffic never dips below what you configured. A release that never becomes ready therefore does not cost you capacity — the previous one keeps serving.
The exception is a service with a volume mounted. A volume can only be attached to one replica at a time, so the old copy has to release it before the new one can take it: such a service is replaced rather than overlapped, and there is a short gap on every deploy.
Next steps
- Choose where state lives: move per-replica state into a database, so replicas stay interchangeable.
- Set variables and secrets: change
WORKER_CONCURRENCYand the rest of what a replica runs with. - Run a command inside a service: reach a replica, and see why a stopped service has no shell to open.
- Understand how deployment works: the environment whose capacity ceiling your services share.