Skip to content

Reinstall a VPS with a new operating system

5 steps · Checked against the API code 3 November 2026

A guide does one task from start to finish. Every endpoint, with all its fields and cases, is in the API reference.

A reinstall erases the disk of a VPS and installs a clean operating system from a template. The VPS keeps its IP addresses and the root password stored for it. The API answers at once with a job. You follow the job until the new system has started. Then you log in.

  • Your API key in $HOSTUP_API_KEY, as set in the Quickstart. It needs these scopes (Keys and scopes):
    • write:vm to read the details, the disks and the templates.
    • rebuild:vm to start the reinstall. The reinstall needs write:vm as well.
  • The ID of the VPS (vps_…). List VPS instances if you don’t have it.
  • A backup or another copy of everything on the server you want to keep. A snapshot is not such a copy: the reinstall deletes the snapshots of the VPS together with the old system. Back up and restore a VPS
  • jq for the job script in step 4.
export VPS_ID="vps_…" # your VPS: List VPS instances

Get VPS details and action gates includes actions.canRebuild, with allowed and the reason when it isn’t. A reinstall needs an active VPS. It is not allowed while another reinstall of the same VPS is running.

GET /api/v2/vps/{id}/details
curl "https://cloud.hostup.se/api/v2/vps/$VPS_ID/details" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"id": "vps_01hxa3b4c5d6e7f8g9h0j1k2m3",
"serviceStatus": "active",
"operatingSystem": {
"displayName": "Ubuntu 22.04",
"family": "ubuntu",
"version": "22.04",
"variant": null
},
"bandwidth": {
"hasOverage": false,
"…": "…"
},
"actions": {
"canRebuild": { "allowed": true, "reason": null },
"…": "…"
},
"…": "…"
}

When allowed is false, reason says why, for example “This action requires an active service.” or “A rebuild is already in progress on this VPS.”

canRebuild does not show two other things that block a reinstall:

  • bandwidth.hasOverage is true. The VPS has used up its bandwidth. The reinstall is refused until the usage resets or the limit is raised.
  • The VPS has an extra disk. Check the disks next.

List VPS disks lists the disks of the VPS.

GET /api/v2/vps/{id}/disks
curl "https://cloud.hostup.se/api/v2/vps/$VPS_ID/disks" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
[
{
"id": "vpsdisk_01hxa3b4c5d6e7f8g9h0j1k2m3",
"name": "scsi0",
"isCdrom": false,
"bootOrder": 1,
"actions": {
"canDetach": { "allowed": false, "reason": "Cannot detach root disk." }
},
"…": "…"
},
{
"id": "vpsdisk_01hxa3b4c5d6e7f8g9h0j1k2m4",
"name": "scsi1",
"isCdrom": false,
"bootOrder": null,
"actions": {
"canDetach": { "allowed": true, "reason": null }
},
"…": "…"
}
]

The disk whose bootOrder is 1 is the system disk. Every other disk whose isCdrom is false blocks the reinstall. In this example that is scsi1. A CD-ROM drive, such as a mounted ISO, does not block it. With an extra disk the job fails before anything is erased.

Keep the id of the extra disk. The delete call takes this id (vpsdisk_…), not the disk’s name.

export DISK_ID="vpsdisk_…" # id of the extra disk above

Delete a VPS disk removes it. This call also needs the scope write:storage.

DELETE /api/v2/vps/{id}/disks/{diskName}
curl -X DELETE "https://cloud.hostup.se/api/v2/vps/$VPS_ID/disks/$DISK_ID" \
-H "Authorization: Bearer $HOSTUP_API_KEY"

The answer is 204 with no body. When actions.canDetach of the disk is false, the call answers 403. On a VPS with a fixed plan, some disks can only be removed by support. reason then says so.

On a Cloud VPS, keepVolume=true keeps the disk as a volume instead of deleting it. Do not use it before a reinstall. A disk kept as a volume still blocks the reinstall, though the disk list no longer shows it. The same holds for a volume you detached from this VPS. Delete that volume before you reinstall.

List the operating systems for a VPS lists the systems this VPS can install. They are grouped by family, newest version first.

GET /api/v2/vps/{id}/templates
curl "https://cloud.hostup.se/api/v2/vps/$VPS_ID/templates" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"templates": [
{
"id": "os_01hxa3b4c5d6e7f8g9h0j1k2m4",
"name": "Debian 12",
"family": "debian",
"version": "12",
"licensing": null,
"licensingNotice": null
},
{
"id": "os_01hxa3b4c5d6e7f8g9h0j1k2m3",
"name": "Ubuntu 24.04",
"family": "ubuntu",
"version": "24.04",
"licensing": null,
"licensingNotice": null
}
]
}

Take the id from the list of the VPS you reinstall. Another VPS can offer a different list. Step 3 sends it as templateId.

export TEMPLATE_ID="os_…" # id of the system you want above

A template with licensing set to byol needs your own licence. licensingNotice says so in words.

Reinstall a VPS takes the template ID. The SSH keys already on the VPS go onto the new system. To install without them, add "clearSshKeys": true to the body. You then log in with the root password.

POST /api/v2/vps/{id}/actions/rebuild
curl -X POST "https://cloud.hostup.se/api/v2/vps/$VPS_ID/actions/rebuild" \
-H "Authorization: Bearer $HOSTUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"templateId": "'$TEMPLATE_ID'"}'
Response 202
{
"operation": {
"status": "queued",
"jobId": "job_01hxa3b4c5d6e7f8g9h0j1k2m3",
"pollUrl": "/api/jobs/job_01hxa3b4c5d6e7f8g9h0j1k2m3"
}
}

Keep operation.jobId. The next step uses it.

export JOB_ID="job_…" # operation.jobId above

The VPS is offline while the job runs. Expect several minutes. While it runs, data.status is waiting or running. It ends as completed or failed. The job endpoint is older than v2. Its answer has the job inside data. Jobs explains every field.

GET /api/jobs/{jobId}
curl "https://cloud.hostup.se/api/jobs/$JOB_ID" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"success": true,
"timestamp": "2026-11-03T10:21:12.000Z",
"requestId": "11111111-1111-1111-1111-111111111111",
"data": {
"jobId": "job_01hxa3b4c5d6e7f8g9h0j1k2m3",
"status": "completed",
"progress": 100,
"error": null,
"finishedAt": "2026-11-03T10:21:05.000Z",
"…": "…"
}
}

Poll it until it ends with wait-for-job.sh:

./wait-for-job.sh "/api/jobs/$JOB_ID"

The script gives up after 10 minutes. If the job still runs by then, start the script again.

The job finishes after the VPS has started and the operating system has had time to boot.

data.error only says “Job failed.” An extra disk is one cause. The job then stops before anything is erased. Remove the disk as in step 1. Start the reinstall again once the five minutes have passed.

Any other failure can come after the old system has been erased. Read the details as in step 5. If operatingSystem does not show the new system or the VPS does not start, contact support with the job ID.

Read the details again. operatingSystem shows the new system.

GET /api/v2/vps/{id}/details
curl "https://cloud.hostup.se/api/v2/vps/$VPS_ID/details" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"id": "vps_01hxa3b4c5d6e7f8g9h0j1k2m3",
"ipv4": "192.0.2.10",
"powerState": "running",
"operatingSystem": {
"displayName": "Debian 12",
"family": "debian",
"version": "12",
"variant": null
},
"sshKeys": [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIexample [email protected]"
],
"access": {
"username": "root",
"rootPassword": "example-root-password"
},
"…": "…"
}

If powerState is stopped, the new system did not start on its own. Start the VPS and read the details again.

Log in as access.username with the password in access.rootPassword. sshKeys lists the SSH keys installed on the server. Connect to the IP address:

A password you changed inside the old system, for example with passwd, is not carried over. For another password, reset the root password once the new system runs. A Windows template gets no SSH keys. Its user is Administrator, with the password in access.rootPassword.

If SSH warns that the host key has changed

Section titled “If SSH warns that the host key has changed”

The server has a new host key. Remove the old entry with ssh-keygen -R 192.0.2.10. Then connect again.

Read the SSH password-login state of the VPS. Set SSH password login turns it on when it is off.

  • Money. A reinstall costs nothing and creates no invoice. The plan of the VPS stays the same.
  • The server. It keeps its IP addresses. Its snapshots are gone with the old system.
  • The next reinstall. It can start five minutes after this request at the earliest. That also holds when the job fails.

Next: Open a port in the VPS firewall

  • 429 cooldown_active: a reinstall of this VPS was requested less than five minutes ago. The Retry-After header and recovery.retryAfterSeconds give the seconds left. Wait that long and send the request again.
  • 400 invalid_request: templateId is missing or names a template this VPS does not offer right now. Get the list again in step 2 and use an id from it. Only a missing templateId has its own detail. For every other cause, detail is “The request is invalid.” On some VPS a refused template also starts the five-minute wait. The next request then gets 429, though nothing was reinstalled.
  • 403 forbidden without requiredScopes: the VPS is not active or has used up its bandwidth. The detail does not say which. Check serviceStatus and bandwidth.hasOverage from step 1.
  • 401 or 403 with requiredScopes: the key was refused. The reinstall needs both write:vm and rebuild:vm. When a key is refused gives each cause and what to do.
  • 409 upstream_failed: another operation is running on the VPS, for example a backup. Wait until it has finished and try again.