Skip to content

Provisioning

Creating an instance turns a plan + location + image into a running VM. The pipeline is asynchronous, idempotent and rolls back on failure.

Create-instance wizard

Instance overview

Request pipeline

POST /api/client/cloud   (or admin application API)
  │  validate form + semantic rules + entitlements
  ▼
── DB TRANSACTION (no provider calls inside) ──────────────
  instance row (pending → scheduling)
  scheduler decision + capacity reservation
  VMID reservation from the connection range
  IP allocation (IPv4 required, IPv6 soft-fail)
  root volume row (pending)
  task (INSTANCE_CREATE) with a step thread
── COMMIT ─────────────────────────────────────────────────
  ▼
ProvisionInstanceJob (queue pterocloud-tasks)
  one tick per handle(): begin → clone → configure → cloud_init
                          → network → boot → verify → finalize

Stages

Step What happens
clone Clone the provider template; instance reserved → cloning.
configure Apply resources (sockets/cores/memory/tags), grow-only disk resize.
cloud_init Identity + network seed (user/password/ssh keys/IP config/DNS).
network Attach the primary NIC on the mapped bridge (+ VLAN), read back the MAC.
boot Set boot order/agent/onboot, start the VM.
verify Confirm the provider reports running; a guest-agent timeout is a warning, never a failure.
finalize Consume the VMID, convert capacity, mark the volume ready, store the reveal-once credential, complete the task.

Each stage is idempotent, so a crashed worker re-enters the current step without executing anything twice. Every stage is bounded by a timeout; over budget triggers rollback.

Credentials

  • A unique password is generated with the CSPRNG for every operation; it is never derived from the instance id, uuid, hostname, plan or customer.
  • The login username follows the precedence instance explicit → image default_username → plan default → global default. root is used only when the image explicitly declares it and root login is allowed.
  • The credential is stored encrypted with an expiry and a revealed_at marker. A reveal is a locked transaction that marks the row revealed and erases the secret before returning the plaintext, so a concurrent second call is denied.
  • A failed provision deletes the credential; a rebuild mints a new one.

Credential types: PASSWORD, SSH_KEY, PASSWORD_AND_SSH_KEY, IMAGE_DEFINED. An image that supports only SSH requires a valid public key (IMAGE_SSH_KEY_REQUIRED); an image supporting neither is blocked (IMAGE_LOGIN_METHOD_UNSUPPORTED), always before any provider resource is created.

Idempotency

  • Request — the Idempotency-Key header caches the created instance uuid; a replay answers 200 with the same instance (meta.idempotent_replay: true).
  • Task — complete/fail are idempotent; the job exits when the task is terminal.
  • Stage — clone is proven by persisted state or adopted when the occupying guest carries our hostname; resize is grow-only; config PUTs converge.
  • Provider op — a recorded UPID is the resume token; the poll path never re-issues the operation.

Failure handling

Any stage exception funnels into the rollback service: guests provably ours are stopped and destroyed (deletion confirmed against provider truth); VMID/IP/ capacity reservations release in order; the volume row stays as an errored audit trail; the instance lands in failed with a customer-safe message and the task fails with the same code. When the guest destroy is rejected or unconfirmable, the instance parks in rollback_failed with every reservation kept for admin follow-up.

Customer visibility

Clients see the status ladder, the task (progress, current step, step thread) and the safe failure block. Provider coordinates (VMID, node, UPIDs, template ids) never appear in client payloads.

Rate limits

Instance creation is rate-limited per user (5/minute, 20/hour by default).