Dedicated machines
Give an always-on backend a Hetzner VM of its own. Sp00ky Cloud creates it, keeps it healthy, rolls it on every deploy, and proxies traffic to it, so exposure and custom domains work as for any backend.
Dedicated machines are new. The shape of machines: is settled; what
spky deploy prints for them may still change.
A backend normally runs as a container on the shared host of your deployment, next to the
database, the scheduler and the SSPs. A dedicated machine gives one always-on backend a
Hetzner VM of its own instead: its own CPU and memory, nothing else on the box, and a fixed
address. Sp00ky Cloud owns the machine from start to finish: it creates it, probes it, replaces it
with a fresh one on every deploy and whenever it stops answering, and proxies traffic to it, so
deploy.expose, spky domain add --app, outbox jobs and the operator dashboard all work exactly
as for any other backend.
This is not a machine pool. A pool runs a backend for its jobs and takes the machines away when the queue is empty. A dedicated machine runs a backend as a service, always, on one VM.
Declaring one
machines.<name> is the VM; runOn: { machine: <name> } on a backend puts it there. Each machine
runs exactly one backend, and runOn takes either pool or machine, never both. spky lint
checks all of it, and spky deploy refuses a control plane that does not offer dedicated machines
before building anything.
What the backend needs
deploy.port: the port the service listens on. Sp00ky Cloud forwards it (anddeploy.grpc_port, if set) to the machine through the core host.deploy.ports(optional): raw ports the machine opens to the internet on its own public address, for traffic the HTTP forwarder cannot carry: WebRTC media, RTMP, anything UDP. Each entry is published by docker on the VM and allowed in the machine’s firewall from anywhere ("7882/udp","7881","3000:8080", same syntax as inspky dev). They are not proxied by the core host, so clients need the machine’s public IP, which changes with every generation: a service on them must discover its own address (a WebRTC SFU does, via STUN) and hand it out over the forwarded port. Changing the list rolls a fresh VM.deploy.healthcheck: an HTTP path answering 2xx when the service is ready. A new machine takes traffic only after this answers, and the running one is probed on it every 30 seconds.hosting: cloud(the default): Sp00ky Cloud runs it there. Ahosting: externalbackend has its own host already.- An image with a command: like a pool machine, the VM imports the exported image, so
deploy.cmdis needed when the Dockerfile has noCMD.
The backend’s environment reaches the machine the same way as on the core host, with the usual
SPKY_* variables, except that SPKY_DB_URL and SPKY_DB_WS point at the published database
host (https://<slug>-db.<domain>) rather than the internal one, and SPKY_SCHEDULER_URL and
SPKY_SSP_ADDR are not set: those addresses have no public twin. deploy.resources is ignored;
the machine type is the size.
What a deploy does
The VM is started as soon as the deploy is accepted, so it boots while your migrations run. In the apps phase Sp00ky Cloud waits for it to answer its healthcheck, then routes to it. Every deploy that changes the backend (image, env, command, port, machine type or locations) boots a fresh VM beside the running one and only switches over once the newcomer answers; the old one is retired after that. A machine that never answers is destroyed, the deploy fails, and the previous machine keeps serving.
The backend row is the backend’s presence on the core host: a small forwarder that carries the
backend’s name and points at the machine. That is what keeps job routing, health probes and
custom domains unchanged.
A deploy of a dedicated backend takes a few minutes longer than a container deploy: a VM has to
boot and import the image. Expect the deploy to sit on booting for one to three
minutes.
What spky status shows
g3 is the generation: the third VM this machine name has had. The IP is the machine’s public
address. serving means traffic goes there; booting and healthy mark a newcomer that has not
taken over yet; unhealthy means the serving machine is failing probes and a replacement is on its
way.
Failure and replacement
Sp00ky Cloud probes the serving machine every 30 seconds. Three failures in a row, or the VM being gone at the provider, start a replacement: a fresh machine with the same spec, promoted the moment it answers. The one that failed is retired afterwards. This happens at most three times per hour per machine; past that the backend is reported down and you get the uptime alert email. The serving machine is never touched before its replacement answered, so a wrong probe costs a machine-hour, not an outage.
When Hetzner has no capacity for the machine type in any of the listed locations, the replacement
cannot be made and the backend is reported down; Sp00ky Cloud keeps trying every 30 seconds until
capacity is back, and those attempts do not count against the three per hour. Listing more than one
location is the cheapest insurance against this.
spky restart <app> on a dedicated backend rolls a fresh VM the same way. There is no way to
restart the process on the box, and none is needed: docker’s restart policy on the machine brings a
crashed process back on its own.
Every switch to a new machine recreates the small forwarder container on the core host, which costs
about three seconds of 404 at the platform hostname (measured on staging), the same as any other
backend’s container restart. The machine itself is never the reason for a gap: the old one serves
until the new one has answered.
Local development
spky dev ignores runOn.machine. The backend is started like any other app, from its dev:
command, and the local SSP dispatches its jobs to its baseUrl.
Limitations
- One backend per machine, and one machine per backend.
- Hetzner only.
providermust behetzner; the machine type must be an x86 type the account can order. - Git-linked deploys place the backend and carry
deploy.portslikespky deploydoes;spky restart <app>(the forced roll) remains a CLI action. - No logs from the machine yet.
spky logs <app>shows the forwarder’s log, not the backend’s. deploy.portsare not forwarded, onlyportandgrpc_portreach the machine through the core host; the others are open on the VM’s own public address.deploy.resourcesis ignored.- A backend that calls the scheduler (
SPKY_SCHEDULER_URL) does not work from the machine, as on pool machines. - Machines count against your plan’s machine allowance, together with pool machines, and bill per machine-hour.
Reference
Machine fields
| Prop | Type | Default | Description |
|---|---|---|---|
| provider | hetzner | hetzner | Where the VM comes from. Only hetzner. |
| type | string | cx33 | Hetzner Cloud server type. x86 types only. |
| locations | string[] | [] | Hetzner locations in order of preference. Empty lets Hetzner choose. |
On a backend
| Prop | Type | Default | Description |
|---|---|---|---|
| runOn.machine | string | - | Name of the dedicated machine this backend runs on. Needs deploy.port and deploy.healthcheck. |
| deploy.ports | string[] | [] | Raw TCP/UDP ports published on the machine's public address and opened in its firewall to everyone. Not proxied by the core host. |