Restore a VPS from a backup
4 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 restore writes a backup back onto the VPS it came from. What is on the disk now is replaced. To get old files back without touching the server, restore the backup onto a new Cloud VPS instead (Restore to a new Cloud VPS instead).
Before you start
Section titled “Before you start”- An API key with the scope
backup:vm. It covers every read on this page. A restore to a new Cloud VPS needswrite:billingas well. The requests use the key as$HOSTUP_API_KEY(Quickstart). Keys and scopes - The ID of the VPS,
vps_…. List VPS instances if you don’t have it. - A backup of that VPS. Back up a VPS takes one.
export VPS_ID="vps_…" # your VPS: List VPS instances1. Pick the backup and check it can be restored
Section titled “1. Pick the backup and check it can be restored”GET /api/v2/vps/{id}/backups lists the backups of the VPS and says whether a restore is allowed now.
curl "https://cloud.hostup.se/api/v2/vps/$VPS_ID/backups" \ -H "Authorization: Bearer $HOSTUP_API_KEY"{ "backups": [ { "id": "bkp_01hxa3b4c5d6e7f8g9h0j1k2m3", "createdAt": "2026-11-02T10:24:00.000Z", "backupType": "manual", "note": "Before the PHP 8.4 upgrade", "actions": { "canRestore": { "allowed": true, "reason": null } }, "…": "…" } ], "actions": { "canRestore": { "allowed": true, "reason": null }, "canRestoreToNewCloudVm": { "allowed": true, "reason": null }, "…": "…" }, "…": "…"}Two gates must both say allowed: true:
actions.canRestorefor the VPS. It isfalsewhile the VPS isn’t active or another restore is running.reasonsays which.backups[].actions.canRestorefor the backup itself. A backup that holds only the server’s settings and no disk hasallowed: falseand the reason “This backup contains configuration only, with no server disk data. Choose another backup.”
When listing.status is unavailable, the list is empty because the backups could not be read right then. They are still there. Try again in a moment.
Keep the id of the backup you restore. The next requests use it.
export BACKUP_ID="bkp_…" # backups[].id of the backup to restore2. Restore it
Section titled “2. Restore it”POST /api/v2/vps/{id}/backups/{backupId}/actions/restore takes no body. The path picks the backup.
curl -X POST \ "https://cloud.hostup.se/api/v2/vps/$VPS_ID/backups/$BACKUP_ID/actions/restore" \ -H "Authorization: Bearer $HOSTUP_API_KEY" \ -H "Content-Type: application/json"{ "operation": { "status": "queued", "jobId": "job_01hxc4d5e6f7g8h9j0k1m2n3p4", "pollUrl": "/api/jobs/job_01hxc4d5e6f7g8h9j0k1m2n3p4" }}export JOB_ID="job_…" # operation.jobId above3. Wait for the restore to finish
Section titled “3. Wait for the restore to finish”Poll the job until data.status is completed or failed. While it runs, it is waiting or running.
curl "https://cloud.hostup.se/api/jobs/$JOB_ID" \ -H "Authorization: Bearer $HOSTUP_API_KEY"{ "success": true, "data": { "jobId": "job_01hxc4d5e6f7g8h9j0k1m2n3p4", "type": "restore", "status": "completed", "progress": 100, "…": "…" }, "…": "…"}To poll until it ends, run the script from Jobs:
./wait-for-job.sh "/api/jobs/$JOB_ID"If it ends failed, data.progressText says why when it is known. Check the server in step 4 either way.
A completed job can carry a warning in data.result.warning (Jobs). Log it. Read only message and warning from data.result. Its other fields are technical and can change.
4. Check that the server is running
Section titled “4. Check that the server is running”GET /api/v2/vps/{id}/status shows what the server is doing now.
curl "https://cloud.hostup.se/api/v2/vps/$VPS_ID/status" \ -H "Authorization: Bearer $HOSTUP_API_KEY"{ "id": "vps_01hxa3b4c5d6e7f8g9h0j1k2m3", "powerState": "running", "uptimeSeconds": 35, "…": "…"}A small uptimeSeconds confirms the fresh start. Your own services may need a little longer to come up than the server itself.
If the job failed
Section titled “If the job failed”On a Cloud VPS that is on hold for an overdue invoice, the job fails before it stops the VPS. Pay the invoice first. Then restore again. Any other failure can leave the VPS stopped.
If the VPS is stopped
Section titled “If the VPS is stopped”The restore did not start the server again. The job stops a running VPS before it writes the disk. It does not start it again after a failure. A completed restore whose start did not succeed also leaves it stopped. Its data.result.warning then says so. Start the VPS with Start a VPS and wait for that job the same way. If the server does not hold the data you expect, run the restore again.
Restore to a new Cloud VPS instead
Section titled “Restore to a new Cloud VPS instead”Instead of overwriting the VPS, the API orders a new Cloud VPS on your account and writes the backup onto it. The new server gets its own IP addresses. The VPS you restore from stays as it is. Use this to get old files back or to test a backup.
1. Check that it is offered
Section titled “1. Check that it is offered”List the backups as in step 1. actions.canRestoreToNewCloudVm.allowed must be true. It is false while a restore is already running. It is also false when the key lacks backup:vm or write:billing and when the account can’t use Cloud VPS yet. reason says which. Typical account blockers are a missing card and an account that isn’t verified.
backups[].actions.canRestore must be true for the backup you pick, as in step 1. Keep its id.
export BACKUP_ID="bkp_…" # backups[].id of the backup to restore2. Restore to the new server
Section titled “2. Restore to the new server”POST /api/v2/vps/{id}/backups/{backupId}/actions/restore-to-payg takes an optional body. Every field has a default:
name: the hostname of the new server, 3 to 63 characters. Use letters, digits and hyphens only. Start and end with a letter or digit. A name with a dot passes this request. The job then fails before it orders anything. Withoutnamethe API builds one from the VPS name and the end of the backup ID, such asrestore-web-01-j1k2m3.cpuCores(1 to 32) andmemoryGb(1 to 256): the same as the VPS when left out. A higher number is lowered to the maximum.storageGb: at least 50 and at least what the backup needs, up to 2,000. Without it the API picks the size the backup needs.
Any other field answers 400 invalid_request.
This call orders a new Cloud VPS. It is billed by usage like any Cloud VPS. Get Cloud VPS account limits has the hourly rates in billing. It also shows how much of your Cloud VPS limits is left.
curl -X POST \ "https://cloud.hostup.se/api/v2/vps/$VPS_ID/backups/$BACKUP_ID/actions/restore-to-payg" \ -H "Authorization: Bearer $HOSTUP_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "web-01-restored"}'{ "operation": { "status": "queued", "jobId": "job_01hxd5e6f7g8h9j0k1m2n3p4q5", "pollUrl": "/api/jobs/job_01hxd5e6f7g8h9j0k1m2n3p4q5" }}export JOB_ID="job_…" # operation.jobId aboveWhile that job runs, sending the same request again returns the same job. It does not order a second server. A job that failed after it had ordered the server is retried on that server.
3. Find the new server
Section titled “3. Find the new server”The job places the order, waits until the new server is built and then writes the backup onto it. Wait for it as in step 3:
./wait-for-job.sh "/api/jobs/$JOB_ID"When it is completed, data.result names the new server. The script prints data.result. The job endpoint shows it as well:
curl "https://cloud.hostup.se/api/jobs/$JOB_ID" \ -H "Authorization: Bearer $HOSTUP_API_KEY"{ "success": true, "data": { "jobId": "job_01hxd5e6f7g8h9j0k1m2n3p4q5", "type": "restore_to_new_payg", "status": "completed", "result": { "targetVpsId": "vps_01hxe6f7g8h9j0k1m2n3p4q5r6", "orderId": "ord_01hxa3b4c5d6e7f8g9h0j1k2m3", "invoiceId": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3", "…": "…" }, "…": "…" }, "…": "…"}export TARGET_VPS_ID="vps_…" # data.result.targetVpsId aboveUse $TARGET_VPS_ID like any VPS ID. The status call in step 4 works with it. orderId and invoiceId point to the order behind the new server.
If you no longer need the new server
Section titled “If you no longer need the new server”The new server is billed until you end it. Its disk is billed even while it is stopped. Copy what you need off it first. Then end it with Cancel a VPS on $TARGET_VPS_ID, with "cancelType": "immediate". That call needs write:billing as well.
What happens next
Section titled “What happens next”- A restore onto the same VPS keeps its IP addresses. It also keeps its current CPU and memory.
- A restore onto the same VPS creates no invoice. Hostup sends the account holder no e-mail about it.
- A restore to a new Cloud VPS places an order on your account. The new server runs until you end it.
Errors you may hit
Section titled “Errors you may hit”Restore onto the same VPS
Section titled “Restore onto the same VPS”- 403
forbiddenwithrequiredScopes: the key lacks the scope named there (When a key is refused). - 403
forbiddenwithoutrequiredScopes:detaildoes not say what is wrong. Two things cause it:- The VPS has used up its bandwidth allowance. Restores stay blocked until the usage resets or the limit is raised.
- The backup was deleted or belongs to another VPS. List the backups again and pick an
idfrom there.
- 404
backup_not_found: no backup has that ID. Schedules delete old backups. Read the list right before you restore. - 409
upstream_failed: the backup holds only the server’s settings and no disk. The answer does not say why.backups[].actions.canRestorein step 1 shows it. Pick another backup.
Restore to a new Cloud VPS
Section titled “Restore to a new Cloud VPS”- 403 with a code such as
no_card_on_fileornot_verified: the account can’t use Cloud VPS yet.detailsays what to do. - 403
payg_payment_overdue: a Cloud VPS invoice is overdue.canRestoreToNewCloudVmdoes not show this. Pay the invoice first (Pay an invoice). - 404
backup_not_found: no backup has that ID. This call also answers it for a backup that was deleted or belongs to another VPS. - 409
target_storage_too_small:storageGbis below what the backup needs.requiredStorageGbin the answer has the size to send. LeavestorageGbout to get it. - 409
target_storage_too_large:storageGbis above 2,000. A backup that needs more than that gets this answer too.maxStorageGbin the answer has the limit. - 409
backup_has_no_disk_images: the backup holds no disk data. Pick another backup. - 409
payg_limit_exceeded: the new server would go past your Cloud VPS limits. The answer showsrequested,currentandmax. Ask for more with Request a higher Cloud VPS limit.