Clusters

Run your app across many machines

A container cluster is member VMs on a private network behind a managed load balancer. Deploy one or more services into it, scale replicas, and roll out new versions one service at a time — Suble places, balances and heals the containers for you.

from zero to load-balanced
# 1 — create the cluster (3 member VMs + a managed load balancer + a private network)
suble cluster create shop --size BXS.s1 --replicas 3

# 2 — deploy a service into it (3 container copies, load-balanced, with a domain)
suble cluster service add shop web \
  --image registry.suble.io/acme/web:1.4 --port 3000 --replicas 3 --domain app.example.com

# 3 — that's it. The LB now serves app.example.com across all three members.
suble cluster show shop

Quickstart

Three steps from nothing to a load-balanced, multi-VM deployment. The same flow lives in the dashboard (Console → Clusters) and declaratively in suble.yml.

1

Create the cluster

A cluster is shared infrastructure: N member VMs on a private network, behind a managed load balancer. Pick a member plan and how many VMs — everything else is provisioned for you.

create infrastructure
suble cluster create shop --size BXS.s1 --replicas 3

2

Deploy a service

A service is a container image + a replica count. Replicas are placed across the members and registered as load-balancer backends. Give it a domain and it's served with automatic TLS.

add a service
suble cluster service add shop web \
  --image registry.suble.io/acme/web:1.4 \
  --port 3000 --replicas 3 --domain app.example.com

3

Ship updates

Re-deploy a single service and it rolls out one container at a time behind the LB — health-checked before it takes traffic. Other services in the cluster are untouched.

roll out a new version
suble cluster service deploy shop web --image registry.suble.io/acme/web:1.5

What a cluster is made of

Four managed pieces, provisioned together and torn down together. You work with services; Suble keeps the machines, the routing and the network in sync underneath.

Member VMs

Real, visible instances — you can open a console, read their metrics, and see them in your instances list. Every member runs the same plan; add up to 9. Your containers run here, as Docker containers managed by the cluster.

Managed load balancer

A dedicated Caddy + HAProxy load balancer (optionally an HA pair with a floating VIP) sits in front. It routes each service's domain to that service's containers and terminates TLS. Exposed services get https + automatic Let's Encrypt certificates.

HTTP or raw TCP

Give a service a domain and it's published as HTTP/HTTPS with automatic TLS. Or set its protocol to TCP and pick a port — the load balancer forwards raw TCP straight through to your containers (game servers, message brokers, databases, any non-HTTP protocol). TCP is passthrough: your app terminates its own TLS.

Services & replicas

One cluster runs many services (up to 20). Each service is an image with its own replica count (up to 9), spread round-robin across the members as individual containers — so two replicas of the same service can land on the same member on different ports, and the LB balances across all of them.

Private network

Members and the load balancer share a private VLAN. The LB reaches your containers over their private IPs — traffic between the edge and your app never leaves the internal network. Auto-created with the cluster, or point it at an existing one.

Locked-down members

Every member's public interface is firewalled to ping only — no public SSH, no exposed container ports. All traffic reaches your services through the load balancer over the private network, and members are managed from the dashboard console, so nothing needs to listen on the public IP. You can re-enable SSH on any individual member from its firewall settings if you need direct access.

how a request reaches your containers
Internet
   │  https://app.example.com  (TLS terminated at the LB)
   ▼
┌─────────────── Managed Load Balancer ───────────────┐
│  Caddy (L7 http/https + auto-TLS) · HAProxy (L4 tcp) │
└───────┬───────────────┬───────────────┬─────────────┘
        │ private net    │               │
        ▼                ▼               ▼
   member-1         member-2        member-3      ← your member VMs
   web:20000        web:20000       web:20000     ← service "web", 3 replicas
   api:20001        api:20001                     ← service "api", 2 replicas

Roll out new versions safely

Deploying a service replaces its containers one at a time behind the load balancer, each health-checked before it takes traffic — the same rolling / canary / blue-green strategies as a single instance. A deploy only ever touches the one service you name, so other services keep serving.

deploy one service
# roll out one service — the rest of the cluster is untouched
suble cluster service deploy shop web --image registry.suble.io/acme/web:1.5

# or declaratively, per repo, in that repo's suble.yml:
name: web
service:
  cluster: shop
  image: registry.suble.io/acme/web:1.5
  port: 3000
  replicas: 3
  domain: app.example.com
  deploy:
    strategy: rolling        # rolling (default) | canary | blue-green | immediate
    healthcheck:
      type: http
      path: /healthz
      status: 200

Scale & self-heal

Scale two independent dials: the number of member VMs (more capacity to place containers) and how many replicas each service runs (more copies behind the LB). Both are live operations — no downtime for the services already running.

scale members and replicas
# more member VMs (more room to place containers) — up to 9
suble cluster scale shop --replicas 5

# more copies of one service — up to 9, balanced by the LB
suble cluster service scale shop web --replicas 6

Self-healing members

If a member VM stops being healthy, the cluster provisions a replacement and re-places the containers that were on it — automatically, without you intervening.

HA at the edge

Create the cluster with an HA load-balancer pair and a floating VIP so the ingress itself survives a node failure and fails over in seconds.

Many services, one cluster

A cluster can run up to 20 services. The per-repo model keeps them independent: each repository has its own suble.yml with a service: block naming the shared cluster, and its own CI/CD. Deploying one repo reconciles only that service — teams ship on their own cadence without stepping on each other.

repo A → cluster “shop”
# repo A — apps/web/suble.yml
name: web
service:
  cluster: shop                       # the shared cluster
  build: { dockerfile: Dockerfile }   # built + pushed to your registry
  port: 3000
  replicas: 3
  domain: app.example.com             # https + auto-TLS via the LB
repo B → same cluster
# repo B — apps/api/suble.yml  (its own pipeline, same cluster)
name: api
service:
  cluster: shop
  image: registry.suble.io/acme/api:2.1
  port: 8080
  replicas: 2
  domain: api.example.com

A service with no domain stays internal — reachable by the other services in the cluster but not exposed by the load balancer. Perfect for workers and internal APIs.

Pricing

A cluster costs the sum of its parts — all metered hourly and capped monthly, like everything else on Suble. No separate “cluster” fee.

Member VMs

Each member is billed exactly like a standalone instance — its plan's hourly rate, capped at the monthly price. Scale the member count up or down and the bill follows by the hour.

The load balancer

The managed LB is billed at its size's rate — doubled for an HA pair. Reuse an existing load balancer for the cluster and it isn't billed twice.

The private network

The cluster's private network is 50 DKK / month, metered hourly. Reuse an existing network and there's nothing extra to pay. Deleting the cluster releases everything it owns.

The dashboard shows the live monthly estimate as you size the cluster, and the cluster page breaks the running cost down by members, load balancer and network.

Reference

The CLI commands and the suble.yml fields that drive a cluster. See the full suble.yml reference for every field, and Container Registry for pushing the images you deploy.

CLI commands

Commands

suble cluster create <name>--size --replicas --haCreate the infrastructure: member VMs (--size plan, --replicas count) + a managed LB (--ha for an HA pair) + a private network.
suble cluster ls / show <name>readList your clusters, or show one cluster's members, services, per-container placements, health and monthly cost.
suble cluster scale <name>--replicas NChange the number of member VMs (1–9). Members are added or drained + removed; running services are re-balanced.
suble cluster service add <cluster> <name>--image/--build --port --replicas --domainAdd a service. --domain exposes it via the LB (https); omit to serve on the LB IP over http, or leave it internal.
suble cluster service scale <cluster> <name>--replicas NChange how many container copies of that service run across the members (1–9).
suble cluster service deploy <cluster> <name>--image / --strategyRoll a new image out for one service, one container at a time behind the LB. Never touches other services.
suble cluster service rm <cluster> <name>deleteRemove a service and its containers + LB routes. suble cluster rm <name> tears down the whole cluster.

cluster: — the infrastructure

Fields

cluster.sizerequiredstringMember VM plan code (e.g. BXS.s1). Every member runs this plan.
cluster.replicasnumberNumber of member VMs (default 2, max 9). The service's own replicas decide how many container copies run across them.
cluster.habooleanProvision an HA load-balancer pair with a floating VIP instead of a single LB VM. Default false.
cluster.networkstringPrivate network (name or uid) the members + LB share. Auto-created if omitted; point it at an existing one to reuse it.

service: — a workload in the cluster

Fields

service.clusterrequiredstringTarget cluster (name or uid). Bootstrapped from the optional clusterSize/clusterInstances/clusterHa fields if it doesn't exist yet.
service.build | service.imagerequiredblock | stringBuild a local Dockerfile (pushed to your registry.suble.io namespace, pulled automatically) or run a prebuilt image.
service.portrequirednumberContainer port the service listens on.
service.replicasnumberContainer copies spread across the members (default 1, max 9).
service.domainstringPublic hostname → served by the LB with https + auto-TLS. Omit to serve on the LB IP over http, or leave the service internal.
service.env / cpus / memorymap / number / sizeEnvironment variables (${VAR} expands from the environment) and per-container CPU/memory limits.