Guides

Replicas and autoscaling

Run several copies of an app on one server with the edge balancing requests across them, and let Synq add or remove copies with the load.

A replica is a full copy of your app running next to the original on the same server. With three replicas, Synq runs three identical containers (same image, environment, mounts and networks) and the edge spreads incoming requests across them in turn. If one copy stops answering, the edge skips it and retries the request on the next one, so a crash or a restart of one replica does not turn into an error page.

Replicas help when one container is the bottleneck: a single-threaded app saturating one CPU core, a worker that should consume a queue in parallel, or an app you want to keep serving while one copy restarts. They do not add servers: every replica shares the machine's CPU, memory and disk.

What you need first

  • A single-app project deployed with Docker to one of your own servers. Compose stacks and monorepo services are not replicated (see Limits).
  • Permission to change the project's settings.

Set the number of replicas

Open Resources

Open the project, choose Resources in the project menu and open the Replicas card.

Pick a count

Use the plus and minus buttons (or type a number from 1 to 20) and press Apply. The change applies to the running deployment right away, without a rebuild:

  • Adding replicas: each new copy is started from the running container, must stay up through a short stabilization window (and answer the project's readiness check, when one is configured), and only then joins the edge's list of targets.
  • Removing replicas: the highest-numbered copies leave the edge's list first, are then stopped gracefully (the app receives its stop signal and Docker's grace period to finish in-flight requests) and removed.

1 turns replicas off. The count is saved on the project, so the next deploy and any rollback run the same number.

Check them

Running now lists every replica with its container and state. The project's logs show all replicas together, each line prefixed with [replica N], and the usage card lists each replica's CPU and memory with the total across all of them.

From the CLI:

synq project replicas proj_123          # show the count, each replica and recent scale events
synq project replicas proj_123 3        # run three copies now
synq project replicas proj_123 3 --no-apply   # save for the next deploy only

Over the API, GET /api/projects/:id/replicas reads the same view and PUT /api/projects/:id/replicas with { "replicas": 3 } changes it (see the projects reference). The same operations are available as MCP tools.

How requests are balanced

The project's domains point at an nginx upstream group with one member per replica, in round-robin order. Each member is checked passively: one failed connection or timeout takes it out of rotation for ten seconds, and the request is retried on the next member. Only when every replica fails does the visitor see the edge's "application unavailable" page.

Linked apps that reach this one over the private network use its internal hostname, which every replica answers to, so Docker's DNS spreads those calls across the replicas too.

There is no session affinity: consecutive requests from one visitor can land on different replicas. Keep sessions in a shared store (a database, Redis) or in signed cookies.

Deploys with replicas

A deploy starts the new release's replicas next to its first container, checks each one, and only then points the edge at the new set. The previous release's replicas keep serving until the switch and are then drained and removed. A replica that fails to start fails the deploy before any traffic moves, and the previous release keeps running. Pausing, resuming and restarting the project act on every replica.

Volumes

Named volumes and bind mounts in the app's persistent storage are mounted by every replica at the same time. That is fine for uploaded files or a cache, and unsafe for a SQLite file or anything only one process may write. The Replicas card lists the shared volumes and asks you to confirm before running more than one copy; the API needs "allowSharedVolumes": true. Volumes an image declares without a name are created per container, so each replica has its own.

What cannot run replicas

ReasonWhy
The project runs services (compose or monorepo)Replicas are per single-app project.
It is a static siteThe edge serves the files; there is no container to copy.
It uses the bare runtime, Synq Cloud or a clusterReplicas need Docker on a server. Clusters have their own replica count.
It publishes fixed host ports (Ports)Only one container can hold a host port. Remove the published ports; the edge still reaches every replica.
Zero-downtime deploys are offThat setting says the app must never run twice at once.

Publishing a host port on an app that runs replicas is refused the same way.

Autoscaling

Autoscaling moves the replica count between a minimum and a maximum with the load. Turn it on in the Autoscaling section of the Replicas card, or from the CLI:

synq project replicas proj_123 --autoscale on --min 2 --max 6 --cpu 70
synq project replicas proj_123 --memory 80      # also scale on memory
synq project replicas proj_123 --autoscale off

The settings are the minimum and maximum replicas (up to 20), a CPU target in percent (70 by default) and an optional memory target. Every 30 seconds Synq reads each replica's usage (the same docker stats reading the usage card shows) and averages it across the replicas:

  • CPU is measured against each replica's CPU limit, or one core when the app has no limit (a single-threaded app is saturated at 100%).
  • Memory is measured against each replica's memory limit, or the server's memory when there is none.

It then applies these rules:

RuleEffect
The average stays above the target (CPU, or memory when set) for 2 minutesAdd one replica.
Every watched metric stays below half its target for 10 minutesRemove one replica.
Less than 3 minutes since the last change (manual or automatic)Do nothing yet.
The count is outside the minimum and maximumBring it back at once.
The server lacks the free memory another replica needsDo not add it; report why.

The memory needed for another replica is the app's memory limit, or the busiest replica's current usage when it has none. When the server is short, the card shows the reason, a Not scaled event is recorded and a notification is sent once; the replica is added as soon as memory frees up while the load lasts.

New replicas pass the same checks as a manual change before the edge sends them traffic, and removed ones are drained the same way.

With autoscaling on, a number you set by hand is the starting point and is kept between the minimum and maximum. A deploy runs the current count, so an app that was scaled up stays scaled up through a release.

Scale history and alerts

The card's Recent scale events list keeps the last 20 changes with their reason, for example CPU 91% > 70% for 2m, whether they came from you or from the autoscaler. Every automatic change is also written to the audit log as App autoscaled (a refused scale-up as Autoscaling blocked), and sent through notifications under App autoscaled, which is on by default.

Limits

  • Compose stacks and monorepo services are not replicated. Scale the stack's app into its own single-app project to use replicas.
  • One server. Replicas share the server's resources and each one gets the app's full CPU and memory limits, so three replicas of a 1 GB app can use 3 GB.
  • Single apps have no terminal, so there is no replica picker for exec; service terminals are per service.
  • A replica that is stopped is left out of the edge's list until it runs again. Scaling or redeploying recreates any replica that disappeared.

On this page