Skip to content

Command-line client

ghost is the GigaHost command-line client. It prints results for reading, in tables and with colors on a terminal, and as JSON when you pass --json, so the same commands serve interactive use and automation.

Publish what a host earns before opening a newly enrolled host to rentals:

Terminal window
ghost provider pricing HOST_ID
ghost provider set-pricing HOST_ID --file pricing.json --expected-revision 0 --reason "Initial pricing"

Use the returned revision for later updates. You can also change them on the host’s Pricing tab on the website (/provider/hosts/HOST_ID/pricing); neither needs a new enrollment or a deployment. The file names what you earn (fixed_instance_earnings_microusd_per_month; the compute earnings, either compute_earnings per vCPU, {"microusd_per_month": ..., "vcpus": 1}, or a whole-host total, base_host_earnings_microusd_per_month, kept for the host’s vCPUs then; network_earnings_microusd_per_tib and sla_target_basis_points); see Pricing and SLA. New rentals use published prices immediately. Existing instances receive a notice and switch after 90 full days; already-created backups keep their recorded storage and restore rates. An SLA change applies only to new rentals. Each host can publish once per 24 hours.

Review your charges, receipts, and usage:

Terminal window
ghost account history --limit 50
ghost account deposits
ghost account sla-credits
ghost instance usage INSTANCE_ID --resolution daily
ghost catalog reviews PROVIDER_ID
ghost account review PROVIDER_ID --rating 5 --message "My experience"

History commands accept --cursor from the preceding response. For reading, account history prints one line per entry: its time, kind, the part of your balance it moves (available, reserved, or credit at a provider), the amount, and the instance it is for; --json adds each entry’s transaction_id. A VM’s usage at --resolution current is empty while the VM is not running, and after a power change, reinstall, or restore until its first new sample. At the daily resolution the first page starts with today (UTC) so far, its intervals summed and what the billed ones were charged, with "in_progress": true; --resolution monthly sums each UTC month’s days (the current month with today so far), at most 12 months a page. Reviews require paid usage with the provider in the last year, and are public under your account alias. To change a review, pass its current revision as --expected-revision; a wrong one is refused with review_revision_conflict. The first change may follow publication at once, and later ones once a day; one that comes too soon is refused with 429 limit_reached, whose message names when the next is allowed.

Restoring a hosted backup from the website, over its instance or into a new instance, is approved locally, since only your account key decrypts it; the website makes local and hosted backups, restores local ones and creates instances from them itself:

Terminal window
ghost account approve REQUEST_ID
ghost account approve REQUEST_ID --yes

Inspect the request before approving it; the command prints what it costs. Keep the CLI running through restore-key authorization. If interrupted, repeat the same command to resume its admitted job without creating a second charge. An unapproved request expires after 15 minutes. Your account key and recovery private key stay in the CLI. When the backend refuses an approved request, for example because your balance does not cover it, the website’s approval page says why and that nothing was charged; once the cause is gone, approving again works until the request expires.

The released ghost is one statically linked x86-64 Linux binary: it needs no system libraries, so it runs on any x86-64 Linux distribution, old or new. It is published on a signed release channel; one command installs it for you, without root, as ~/.local/bin/ghost:

Terminal window
curl -fsSL https://api.gigahost.cloud/install/ghost.sh | sh
ghost --version

The script needs curl, jq, and openssl. It refuses the release unless the manifest’s Ed25519 signature matches the release public key embedded in the script and the archive’s SHA-256 matches the signed manifest. It checks the signature with OpenSSL 3, or with python3 on systems whose OpenSSL is older, such as Ubuntu 20.04, Debian 11, and RHEL 8; with neither, it stops and says so. Run the same command to update. It tells you if ~/.local/bin is not on your PATH. Run it as root to install for every user under /usr/local, or set GIGAHOST_INSTALL_PREFIX to another prefix. ghost --version prints the installed release.

ghost names its release with every request. Gigahost serves every release for at least 90 days after it was built (a release’s name starts with its build time, such as 20261010T120000Z-…); update at least that often with the install command above. A release Gigahost no longer serves gets client_release_unsupported for every command that needs your account, with the release to install and the command that installs it.

Use ghost --help or add --help after any command to see the options supported by the installed version.

The client defaults to https://api.gigahost.cloud. Generate your account key with:

Terminal window
ghost key generate
ghost key inspect

key generate creates the account key’s directory, and any missing one above it, for your user alone (mode 0700). A directory that exists keeps its mode, so make an existing ~/.config/ghost private with chmod 700 yourself.

For an IP-addressed test backend, override it with GHOST_API_URL or --api-url. Plain HTTP is accepted only when the URL contains a literal IP address; hostname APIs require HTTPS, and API redirects are not followed.

By default, the account key is stored at $XDG_CONFIG_HOME/ghost/account.seed, or ~/.config/ghost/account.seed when XDG_CONFIG_HOME is not set. Generation refuses to overwrite an existing account key. To use a different location, set GHOST_SEED_FILE or pass --seed-file.

Back up this file offline before funding the account. Anyone who obtains it can control the account; losing every copy can also make encrypted hosted backups permanently unrecoverable.

Read the current activation terms:

Terminal window
ghost account activation-terms

Send one or more payments to the returned address with the exact 16-character deposit_memo; their confirmed sum must reach the returned minimum. Once the payments have enough confirmations, activate without supplying transaction IDs and verify the account:

Terminal window
ghost account activate
ghost account show
ghost account balance

activate for an account key whose account exists already returns that account. A deployment that takes no MMX deposits has no self-service activation: there activation-terms and activate answer activation_unavailable (404), and its operator registers accounts from the output of ghost key inspect.

Private CLI requests are signed locally with a key derived from the account key. There is no bearer or website session token to configure. Keep using HTTPS for any non-loopback API endpoint.

For the full activation, website-login, and key-migration model, see Accounts and security.

Ask the CLI for a short-lived, single-use login URL:

Terminal window
ghost account login

Open the login_url from the response before it expires. This creates a browser session without sending your account key or private key to the website.

To end every website session of the account and cancel unused login URLs, for example after losing a device, run:

Terminal window
ghost account revoke-sessions

Log out everywhere on the website’s Account page (/account) does the same, and that page also ends single sessions, as ghost account sessions and ghost account revoke-session do in the CLI. Your CLI keys keep working.

To log in from a browser without the CLI at hand, set a website password. ghost generates it and shows it once; --print writes only the password, for a password manager, and --expires-in DAYS makes it stop working after that many days. Setting it again replaces it and ends the website sessions the old one began; remove-password turns password login off:

Terminal window
ghost account set-password
ghost account set-password --expires-in 90 --print
ghost account remove-password

The website’s Log in page then takes the account alias and the password. See Website password.

List currently available VM configurations and operating-system images:

Terminal window
ghost catalog locations
ghost catalog offerings
ghost catalog offering <OFFERING_ID>
ghost catalog images

ghost catalog alone is ghost catalog offerings, and takes the same filters, such as ghost catalog --min-vcpu 4.

For reading, catalog offerings prints one line per offering: its ID, location, host mode, size, monthly price from the base price to the price at full CPU use (for a home offering with its default gateway’s monthly base, as the website lists it), traffic price per TiB (for a home offering through that same default gateway, such as $5.00/TiB), the bandwidth limit with that address (BANDWIDTH, such as 200 Mbit/s or no limit), and how many hosts have room for it (HOSTS). catalog offering shows one listed offering in full: its hardware and storage, network readings, each gateway tier’s traffic price, monthly base and bandwidth limit, backup limits, creation fee and balance hold. The quote ghost instance create prints names the limit of the chosen network, and ghost instance get shows an instance’s as bandwidth. --json gives every field of every offering. The catalog is public, so these commands need no account key.

Use a location slug from ghost catalog locations in provider configuration and catalog filters; display_name is its human-readable UI label. Locations also include ISO two-letter country_code and geographic continent_code values. US is a country in continent NA; EU denotes geographic Europe.

Filter offerings when you already know what you need:

Terminal window
ghost catalog offerings \
--mode cloud_routed \
--country SG \
--continent AS \
--provider gh-abcdefgh \
--min-vcpu 4 \
--min-ram 8GiB \
--min-disk 100GiB

--provider takes a provider’s alias, such as gh-abcdefgh, or its UUID; the list’s PROVIDER column shows each offering’s alias. --min-ram and --min-disk take a size in binary units, such as 512MiB, 8GiB or 1TiB. M, G and T stand for MiB, GiB and TiB, so 8G is 8GiB, and a bare number is GiB. Decimal units such as 8GB are refused.

The same geography filters can narrow the location list:

Terminal window
ghost catalog locations --country US
ghost catalog offerings --continent EU

Valid modes are home_gateway and cloud_routed, spelled as the catalog shows them; home-gateway and cloud-routed still work. The server applies every filter before it cuts the list, and refuses an unknown --location, and a --country or --continent that no location is in, instead of matching nothing.

--ip dedicated|shared chooses the address the list prices, orders by and offers, as the website’s IP filter does: dedicated, the default, is an address of the instance’s own, and shared a range of a shared address’s ports. An offering without that address is not listed, and the price column and the cursor below are that address’s.

Offerings are listed cheapest first by their listed price (the base price plus a home offering’s default gateway base price, or with --ip shared the shared IP’s), at most 500 at a time; --limit lists fewer. When a list comes back full, the CLI says on standard error how to get the next one, with the same filters; --after takes the last offering’s listed price in micro-USD and its ID, joined by a dot:

Terminal window
ghost catalog offerings --min-vcpu 4 --limit 50
ghost catalog offerings --min-vcpu 4 --limit 50 --after 3100000.of2_F95QS_EuMsVz4IG5EulOew

Every host offers one ladder of sizes: 1 vCPU on a quarter of a core and on half a core, 2 vCPUs on one core and on two, then 4, 8, 16 vCPUs and so on up to the largest VM the host takes, each of these with twice the RAM and disk of the one before. Between two of those of N and 2N vCPUs with a core each, a mid-step has 2N vCPUs on N cores with the RAM and disk of the smaller one, for more CPU without more memory: 4 vCPUs on 2 cores, 8 vCPUs on 4 cores, and so on, where the larger size exists. A host whose largest VM has 1 vCPU offers 1 vCPU on a quarter, a half, and a whole core instead. A size’s RAM follows the cores it reserves, and a size with less than the platform’s least RAM for a VM, currently 768 MiB, is left out: a host with less than 3 GiB of RAM per core offers no quarter-core size, and one with less than 1.5 GiB no size of 1 vCPU. A VM boots with its size’s RAM less 64 MiB, which the VM itself uses on its host (QEMU and its devices): an offering of 4 GiB gives its guest 4 GiB less 64 MiB. The catalog lists only the sizes a host has room for now, and a cloud host with one free public address left only the largest that fits and the mid-step on as many cores. An offering is told by its vCPUs, RAM, disk, and prices; ghost prints its vCPUs alone, such as 4 vCPU, for a mid-step and a size on part of a core too. Its base price follows the cores it reserves, a mid-step’s 1.5 times the size whose cores it shares; the price at full CPU use follows the number of vCPUs alone, so a mid-step’s is that of the next size. With --json an offering also carries cpu_tier, how the platform accounts for what a vCPU reserves.

Copy the offering ID, of2_ and 22 more letters, digits, -, or _, and the image’s slug, such as debian-13, or its UUID from the response. An offering ID names a configuration group and its exact terms: hardware, prices, and SLA. It is not one physical host; the backend chooses an available matching host during creation. Once the provider changes those terms, or no host with them takes new rentals, the ID no longer works: creation is refused with offering_unavailable, and the catalog lists the current offerings with their new IDs. ghost catalog offering (GET /v1/catalog/offerings/{offering_id}) reads one offering while it is listed.

Each offering lists what creating a VM from it costs besides its monthly prices: instance_creation_charge_microusd is the one-time creation fee, and balance_reservation_microusd is the part of your balance held while the VM exists. A home VM also holds its gateway tier’s monthly base charge.

The list shows each offering’s vCPUs and CPU model, its RAM and the hosts’ memory technology, such as DDR4, its disk with its RAID level, such as 25 GiB raid 1, its monthly prices, and its cheapest traffic rate. Prices are shown to the cent, and an amount under two cents to the hundredth of a cent without trailing zeros, such as $0.0016 or $0.007; --json has every amount in micro-dollars. ghost catalog offering lists every network option of a home offering: a shared gateway at each location it may be placed at, with the location’s distance from the host and its prices, and a gateway of the VM’s own.

Each offering includes its provider UUID. Inspect that provider before renting: its name, rating and review count, the hosts, offerings and locations it lists now, and its completed-day 365-day uptime and abandonment sample:

Terminal window
ghost catalog provider <PROVIDER_ALIAS_OR_UUID>

The reliability record includes observed time and distinct rented-instance counts. An empty or small sample is not equivalent to an established reliability record.

Each offering also lists its hosts under host_ids and their best network readings under network, which ghost catalog offering and --json show, each with the location of the test server it was taken against. Inspect each test location’s latest upload, download and latency result for one host with:

Terminal window
ghost catalog network-test <HOST_UUID>

A reading flagged at_limit reached the test server’s own uplink limit and is reported as that limit with a plus sign, such as 1 Gbit/s+. Null readings are unknown, not zero.

Every newly enrolled host starts paused. This gives you time to verify its inventory, pricing, storage, and networking before customers can rent it. List the host UUID and explicitly resume new rentals when it is ready:

Terminal window
ghost provider hosts
ghost provider host resume <HOST_UUID>

ghost provider hosts lists your hosts as the website’s hosts table does: each host’s state (accepting, paused, unverified, offline, and so on), when its agent was last seen, its type, location, rentals, CPU, RAM and disk, and its SLA and prices. Retired hosts are left out unless you pass --retired. ghost provider host <HOST_UUID> shows one host in full, and --json gives every field of every host.

A cloud-mode host can be resumed only once its public path is verified; until then resume fails with public_path_unverified. See Manage provider public IPs.

See everything the platform knows about a host, and what it and its rentals are using:

Terminal window
ghost provider host <HOST_UUID>
ghost provider host metrics <HOST_UUID> --resolution current
ghost provider host metrics <HOST_UUID> --resolution daily
ghost provider host instance-usage <HOST_UUID> <INSTANCE_UUID> --resolution 15m

The detail shows the last heartbeat and the health the agent derived from its own measurements, the agent version and uptime, hardware, the inventory revision, the revisions of the host’s capacity with their reasons (capacity_revisions, the newest first), and per NUMA node and storage pool how much of the saleable capacity rentals hold. Metrics carry CPU busy and steal time, load, memory and hugepages, disk I/O and SMART health per pool and device, traffic per uplink, and temperatures, at the current, 15-minute, daily, and monthly resolutions. Each row also sums the rentals’ usage for the same period, so the platform’s overhead is visible; a current row counts only running rentals sampled since their last power change, reinstall, or restore. A single rental’s usage can be read at the resolutions the renter sees to find a noisy neighbour. On the website, the host’s Overview tab (/provider/hosts/HOST_ID) shows the current readings, its Usage tab the history each UTC month (the default), each UTC day, or every 15 minutes, and Rentals each rental’s usage (/provider/hosts/HOST_ID/rentals/INSTANCE_ID/usage), also each month by default. ghost provider host <HOST_UUID> also shows the image cache (image_cache) of a host that downloads the catalog’s images ahead of demand, a home host by default: each image with its state and why, and what the cache takes of its limit and filesystem.

Pause admission before planned maintenance. This affects only new rentals; existing VMs and already queued jobs continue normally:

Terminal window
ghost provider host pause <HOST_UUID>

To permanently retire a host that still has rentals, give customers at least seven full days of notice:

Terminal window
ghost provider host retire <HOST_UUID> --notice-days 7 --yes

The backend calculates and returns the exact deadline using its own clock. The notice immediately pauses new rentals and cannot be shortened or cancelled. Every affected customer sees the deadline on their instance. At the deadline, instance usage, gateway base charges, and the refundable balance hold stop, and the platform queues durable VM deletion. Addresses, gateways, and physical capacity remain reserved until cleanup is confirmed. Local-backup billing also ends and those backups leave the platform-managed lifecycle; hosted backups remain available. A persistent deletion failure is shown as requiring operator attention instead of being retried forever.

To retire one rental without draining the host, use the same minimum notice:

Terminal window
ghost provider retire-instance <INSTANCE_UUID> \
--notice-days 7 \
--reason 'retiring this workload class' \
--yes

If the physical host is permanently lost and cannot serve the notice, declare an emergency instead:

Terminal window
ghost provider host emergency <HOST_UUID> \
--reason 'unrecoverable hardware loss' \
--yes

Emergency declaration immediately stops affected billing, revokes the host, records its active VMs as abandoned in provider reliability history, and sends durable renter notifications. It is irreversible and is not a substitute for planned retirement.

To change enrollment settings, other than a higher CPU limit or more NUMA nodes, which raise the host’s capacity in place, first ensure the host has no rentals, then permanently retire its current identity:

Terminal window
ghost provider host delete <HOST_UUID> --yes

The API rejects retirement while any rental, unreleased instance resource, gateway, local backup a renter still keeps, or active job remains. The historical host and tenant records are not erased. Stop the agent and run the local reset on that machine before enrolling it again with a newly generated enrollment token:

Terminal window
sudo systemctl stop gigahost-hypervisor
sudo gigahost-hypervisor reset-enrollment --yes

The reset archives the old identity, job journal, and telemetry spool using a .retired-<HOST_UUID> suffix. It leaves local backup files in place for the provider to retain or remove according to their policy. Their platform records and storage billing close when the host retires; hosted backups remain available.

If you operate a cloud-mode host, register the public IPv4 addresses that its guests may use. First find the enrolled host and create a pool:

Terminal window
ghost provider hosts
ghost provider ip-pool create \
--label cloud-eu-1 \
--scope host \
--host <HOST_UUID>

Use --scope account and repeat --host only when the upstream provider lets the same addresses move between those hosts. Add one or more addresses with the route settings supplied by the server provider:

Terminal window
ghost provider ip add \
--pool <POOL_UUID> \
--address 203.0.113.10 \
--address 203.0.113.11 \
--prefix-length 32 \
--gateway 203.0.113.1
ghost provider ip-pools

Pass --mac-address <MAC> when the server provider assigns a virtual MAC to the address. Add addresses separately if their gateway, prefix, or MAC values differ. An address can be live in only one of your pools at a time, but another provider may register the same address: Gigahost cannot tell who routes an address upstream.

Gigahost therefore checks each address once, before any rental may get it: an online host of the pool boots a small temporary VM that answers on the address, and Gigahost logs in on it over SSH from outside with a one-time key. New rentals are not checked themselves. The pool listing shows each address’s allocation status and its verification: pending, checking, verified, or failed with the reason. A failed address is never assigned and is not checked again on its own; correct the upstream routing, then ask for another check:

Terminal window
ghost provider ip check --pool <POOL_UUID> --address 203.0.113.10

A host’s public path is verified once one of its pools’ addresses has answered through it, and only then can the host be resumed. ghost provider hosts shows public_path_verified_at, public_path_checking, and, after a check through the host had no answer, public_path_error. Check the path again after correcting the routing:

Terminal window
ghost provider host check-path <HOST_UUID>

See Cloud mode for how a check works and what it needs. Remove an address that no rental holds with:

Terminal window
ghost provider ip remove \
--pool <POOL_UUID> \
--address 203.0.113.10 \
--yes

The backend rejects removal while an address is reserved or assigned to a rental, or while a check is using it. An address that no rental ever held is deleted; one that a rental held stays in the pool, disabled, for that rental’s history. ghost provider ip add re-enables a disabled address in its pool, or adds it to another of your pools. An address added again after its removal, or with a different gateway, prefix, or MAC, is checked again before a rental may get it.

An address that a rental was reached at rests for an hour after the rental ends before another rental gets it, and ghost provider ip-pools shows until when (available_at). Removing it and adding it again, to this pool or another, or handing its pool over, keeps its rest. A resting address does not count as free, so a host with few free addresses may refuse its smaller sizes for up to an hour after deletions: it keeps its last free address for the largest size that fits.

Once every host of a pool is retired and no rental holds one of its addresses, you can delete the pool so that its addresses can be added to another pool:

Terminal window
ghost provider ip-pool delete <POOL_UUID> --yes

As with removing one address, addresses that no rental used are deleted and the others stay disabled with the deleted pool for their history. That history does not hold the addresses back: every one of them, those that served a rental included, can be added to another pool right after.

A host’s mode, location, and capacity settings are fixed, so changing one means retiring the host and enrolling the machine again. Hand the retired host’s pool to the new identity instead of deleting it and typing its addresses again:

Terminal window
ghost provider ip-pool hand-over <POOL_UUID> --host <NEW_HOST_UUID>

The host gets a pool with the same label and every address of the old one, with their gateways, prefixes, and MAC addresses, and the old pool is deleted as above. Each address is checked again through the new host before a rental may get it. Only the pool of one host is handed over, and only to a cloud-routed host of yours in the same location.

The backend records actionable host warnings without waiting for a renter to open a support request. List the inbox, inspect an item, and acknowledge that you are working on it:

Terminal window
ghost provider incidents
ghost provider incidents --state open --severity error
ghost provider incident get <INCIDENT_UUID>
ghost provider incident acknowledge <INCIDENT_UUID>

Incidents can report host outages or degraded health, failed jobs, root-storage pressure, telemetry gaps, and a bandwidth reading well below the host’s previous reading against the same test server. A failed job is an incident only when it failed on the host’s side, one for each kind of job, step, and error: not when it failed at a step the platform runs itself, such as setting up a home VM’s gateway, or because of the renter, such as a restore the renter did not authorize in time, or a guest that starts no operating system again after its renter’s reboot, reset, upgrade or restore, whose system is the renter’s. A guest that does not boot a catalog image when it is created or reinstalled is still an incident of yours. A failed address check opens no incident; the pool listing shows it. They show the first and latest occurrence, count, affected resource IDs, and a recommended action. Their context is limited to operational data and does not contain renter SSH keys, startup commands, or guest initialization secrets.

Resolve a condition after correcting it:

Terminal window
ghost provider incident resolve <INCIDENT_UUID>

If the backend observes the same condition again, the stable incident is reopened instead of creating an unrelated warning. Acknowledging or resolving an incident does not itself change the host or VM.

ghost provider balance shows what you earned and was not paid out yet, what is available to pay out now, what is still held, and what is on its way. Earnings become available after the 1-, 5-, or 20-day hold associated with the VM’s accepted SLA tier; an outage on one of your hosts whose SLA credits are not settled yet blocks payouts until it is.

Every payout goes to your payout address. Set it once, and change it later, with this signed command; the website cannot set it:

Terminal window
ghost provider set-payout-address <MMX_ADDRESS>

Then check the balance and request payouts, here or on the website’s Provider › Payouts (/provider/payouts):

Terminal window
ghost provider balance
ghost provider payout
ghost provider payouts

Omit the amount to pay out everything available, or add --amount-microusd for an exact smaller payout; a payout takes from your earnings exactly what it pays. The request returns immediately, and the backend processes the payout without further action. The payout list shows its state: queued, preparing, submitting, submitted, confirmed, under_review, action_required, or cancelled. The backend stores one exact signed MMX transaction, broadcasts only those bytes, sends them again while the node does not know them, and requires six confirmations by default before settling the payout. A payment the wallet may have sent without reporting it is found on chain rather than sent again. A signed transaction that fails on chain or expires unincluded ends as action_required, and its amount can be requested again. A confirmed transaction that does not pay the payout as prepared leaves it under_review, still reserved, until an operator has checked it.

Provide at least one SSH public key. You can repeat --ssh-key-file or --ssh-key to install more than one. A key is one OpenSSH public key line of type ssh-ed25519, ssh-rsa, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521, sk-ssh-ed25519@openssh.com, or sk-ecdsa-sha2-nistp256@openssh.com; DSA keys and certificates are refused. Pass public keys only, such as the .pub file: ghost refuses a private key before it sends anything. The keys are installed for root, so log in as root: the image’s usual default user, such as ubuntu or debian, is not set up, and SSH password login is off. Add other users with a --cloud-init-file.

Terminal window
ghost instance create \
--offering <OFFERING_ID> \
--image debian-13 \
--hostname database-1.example \
--startup-command 'systemctl enable --now my-service' \
--ssh-key-file "$HOME/.ssh/id_ed25519.pub" \
--wait

--image takes an image’s slug from ghost catalog images, such as debian-13, or its UUID. --hostname is optional and must be a lowercase RFC 1123 hostname. --name is optional too: the name the website and ghost show the VM by, by the same rules; without it the VM is named after its hostname. Use --startup-command-file instead of --startup-command for a multiline first-boot shell program. Raw --cloud-init-file remains available for advanced initialization, but cannot be combined with either startup-command option.

Home-mode offerings also require a public connectivity tier:

Terminal window
ghost instance create \
--offering <OFFERING_ID> \
--image debian-13 \
--gateway-tier high_bandwidth \
--ssh-key-file "$HOME/.ssh/id_ed25519.pub" \
--wait

The available tiers are shared_bandwidth, high_bandwidth and shared_ip; the spellings with a hyphen work too. Their recurring and traffic prices are included in the offering information: the monthly base charge and the traffic price of the gateway the VM would be placed on, which can differ by gateway location.

A shared bandwidth VM is reached through a shared gateway at one of a few locations. The offering lists them as gateway_locations, closest to the host first, each with its distance, its gateway’s monthly base charge and traffic price, and whether traffic between the host and it crosses a bottleneck, a congested or long-haul path; one is preselected, the closest with room that crosses no bottleneck, and taken unless you choose another with --gateway-location:

Terminal window
ghost catalog offering <OFFERING_ID>
ghost instance create \
--offering <OFFERING_ID> \
--image debian-13 \
--gateway-tier shared_bandwidth \
--gateway-location us-charlotte \
--ssh-key-file "$HOME/.ssh/id_ed25519.pub" \
--wait

A location across a bottleneck is yours to choose knowingly. When no listed location without a bottleneck has room, the creation is refused and names the closest location with room, which you may choose. instance get shows the VM’s gateway_location, and you can move the VM to another listed location later; it gets a new public address there, is unreachable for the seconds its move takes, and pays that location’s gateway prices from the move on, as the offering lists them:

Terminal window
ghost instance gateway-location <INSTANCE_UUID> us-charlotte --yes --wait

A shared_ip VM has no address of its own: it shares a shared gateway’s address with other VMs and gets a range of its ports, as many as the offering’s shared_ip_ports (20), TCP and UDP. Each forwards to a port of the VM: by default the first to port 22, for SSH, and every other to the same number on the VM. The VM’s outgoing connections leave from a range of source ports of its own, so other VMs on the address cannot use them up. It pays the shared IP’s price per the price list, which the offering shows, and is placed at a gateway location as a shared bandwidth VM is, --gateway-location included. ghost instance create --wait ends with the SSH command, such as ssh -p 1020 root@203.0.113.7. instance get shows port_forwards, each public port with the VM’s port it goes to, and outgoing_ports; instance ports shows them with the SSH command, and instance ports ... set sends public ports elsewhere, the others staying:

Terminal window
ghost instance ports <INSTANCE_UUID>
ghost instance ports <INSTANCE_UUID> set 1021=80 1022=443

The gateway follows a change within a minute and drops the connections of a port whose target changed. When the VM moves to another gateway, by your choice or because its gateway is taken out of service, it gets that gateway’s address and other ports, each going where the one in its place went, and you are told the new address and ports when it moves: update your DNS records and the ports you connect to.

A cloud offering whose hosts share their own address lists a shared_ip price among its network_prices beside the price without a tier, an address of the VM’s own (an offering whose hosts have no address left lists the shared IP alone). --gateway-tier shared_ip on such an offering gives the VM a slice of its host’s address instead of an address of its own, its ports forwarded and changed as above with instance ports, included in the instance’s price (the shared_ip entry’s monthly price is 0, shown as “included”), with the provider’s traffic price; no --gateway-location, since its address is its host’s. It keeps its address and ports while it exists. Without --gateway-tier a cloud VM gets an address of its own, at the offering’s address_microusd_per_month.

The gateway prices a VM pays are those of the gateway it was placed on. When GigaHost changes them, the VM takes the new prices after 90 days, as with a provider’s prices, and you are notified. GigaHost never moves a VM with an address of its own to another address: when its gateway is taken out of service, you are asked to move the VM to another location at a time of your choosing, and it keeps running where it is until you do.

Creating a VM posts a one-time creation fee, refunded if the creation fails, unless you caused the failure, and holds a separate refundable part of your balance for the VM’s lifetime. Before it creates anything, ghost instance create prints the offering’s terms and costs on stderr: the creation fee, with --backup the backup’s restore charge, which is not refunded, the balance hold (for a home VM plus its gateway’s base charge), the monthly prices, and the traffic price. It refuses a --hostname that is not a lowercase RFC 1123 hostname at once, and, once it has read the offering, a --gateway-tier other than shared_ip given for a cloud offering or one missing for a home one, whose tiers and prices the refusal lists, and a backup whose disk is larger than the offering’s root disk. An offering that is no longer listed, or a tier the offering does not list now, gets only a warning there; the backend then decides whether it can still be rented. --compact prints none of this, and leaves the checks after the hostname’s to the backend.

With --wait, once the new VM’s kernel has started, ghost waits up to five minutes more for cloud-init in the guest to report how the first boot went, and warns when it reported errors or finished degraded, listing its messages, or says that it has not reported yet; --compact does not wait. The command then ends with a line on stderr that says where the new VM is: instance ID (HOSTNAME) is running at ADDRESS. Repeating a creation with the same --idempotency-key returns the job of the first request, and ghost says so, with when that job was made: nothing new is charged. backup create and backup restore say the same of a repeat.

A home VM has a public IPv4 address of its own on a hosted gateway. With shared_bandwidth the VM shares a gateway in one of a few central regions with other home VMs, and every protocol and port of its address reaches the VM. With high_bandwidth the VM has a gateway of its own in the region nearest to the provider’s server and a lower traffic rate; its address is the gateway’s, and every protocol and port of it reaches the VM but port 27492, which the gateway keeps. Either way the VM’s traffic crosses the provider’s home connection, so it is never faster than that. Compare the offering’s network readings before paying for high_bandwidth. An offering lists only the tiers there is room for now, and names high_bandwidth in full_gateway_tiers while the platform can set up no gateway of its own for another VM; when the gateways have no room for the VM, the creation is refused with capacity_unavailable, and nothing is charged. Its message says what is short: no public address free at the location, with when one is free again and whether high_bandwidth could be placed, or no gateway that can be set up for high_bandwidth or has room for another shared_bandwidth VM.

A home VM’s network interface has an MTU of 1420, that of the WireGuard tunnel to its gateway, and the MSS of TCP connections through the tunnel is clamped to 1380, so ordinary traffic needs nothing of you. A tunnel of your own inside the VM, such as a WireGuard or other VPN server, carries its packets inside that one and needs a smaller MTU: 1340, the tunnel’s 1420 less WireGuard’s 80 bytes, works for WireGuard. With the default of 1420 its connections stall.

Use --cloud-init-file cloud-init.yaml to supply additional cloud-init user data. ghost checks the file before anything is created: a #cloud-config must be a YAML mapping, a #cloud-config-archive a list, and a #cloud-config-jsonp a JSON Patch array; a script starts with #!, and a MIME multipart document needs a MIME-Version header. Without an SSH key, ghost warns when a #cloud-config sets no SSH key or password of its own. Do not put secrets in files that you do not control.

Terminal window
ghost instance list
ghost instance get <INSTANCE_UUID>
ghost instance power <INSTANCE_UUID> stopped --wait
ghost instance power <INSTANCE_UUID> running --wait
ghost instance power <INSTANCE_UUID> reboot --wait
ghost instance power <INSTANCE_UUID> reset --wait
ghost instance console-log <INSTANCE_UUID>
ghost instance rename <INSTANCE_UUID> shop

Every command that takes a VM’s ID also takes its name, as instance list shows it, in any case: ghost instance delete shop --yes first prints the ID the name stands for. A name two VMs share needs the ID.

For reading, instance list prints one line per VM: its ID, name, state, the job running on it with its progress when there is one, public IPv4 address, location, image, and size. instance get, and --json for either, show all of a VM: its size (vCPUs, RAM, and root disk), firmware, location, provider, and host mode, and its image: the image its root disk now holds, which after a restore is the backup’s. While a job runs on the VM or one of its backups, active_job names it with its kind, current step, and progress: the job that holds the VM, such as a restore, rather than a backup deletion that runs beside it. last_restore names the newest backup restored over the VM, and restored_from_backup_id the backup the VM was created from. name is what the website and ghost call the VM, and hostname the hostname Gigahost set in the guest at its creation or last reinstall.

guest_initialization is what cloud-init in the guest reported of its latest boot: done, degraded or error, with its messages and when it came. A deleted VM also shows when it was deleted (deleted_at) and when its billing ended (billing_ended_at).

instance rename gives a VM another name, by the rules of a hostname: a lowercase RFC 1123 hostname of at most 253 bytes. It changes only what the website and ghost show, at once and without a job, in any state but deleted; the guest and its hostname stay as they are. A reinstall’s --hostname changes the hostname and leaves the name.

A stopped VM keeps its capacity and address and is billed like a running one, except that it uses no CPU time: its base price and metered traffic are still charged. Delete a VM to stop paying for it.

A VM cannot send mail straight to other mail servers: its outgoing TCP connections to port 25 and to port 465 (SMTPS) are dropped, on every host, so that a VM that sends spam cannot put the addresses of its host or of GigaHost’s gateways on blocklists. Send mail through a mail service instead, with a login on port 587 (submission) or through its HTTPS API, which every VM may use; receiving mail works on every port. instance get shows outgoing_mail_allowed, false unless support lifted the block for your account: open a ticket in the networking category saying what the mail is for, and once lifted, every VM of the account sends freely within a minute, running ones too.

stopped asks the guest to shut down, again every five seconds while it runs, and forces it off if it has not after two minutes. reboot does the same and starts the guest again, keeping its address, so a guest that ignores the request is rebooted all the same, up to about two minutes later; with --wait it ends once the guest’s operating system has started, which can be a little before SSH answers. reset forces the guest off at once, without asking it, as a reset button does, and starts it again like reboot: use it for a guest that hangs, since whatever the guest had not written to its disk is lost. A guest that starts no operating system within five minutes fails the reboot or reset with guest_boot_timeout, and keeps running. A guest that is paused, or that never got as far as starting its operating system, cannot act on the request and is forced off at once. A stopped VM cannot be rebooted or reset: the request is refused with instance_stopped.

ghost instance console-log prints the end of what the guest wrote to its serial console: the boot messages of its kernel and system, a kernel panic, and cloud-init’s result. It shows at most the last 64 KiB the host sent, which it does within about ten seconds of the guest writing (--bytes N for less), without the escape sequences that restyle a terminal, and then on stderr when the host sent it and whether older output precedes it; --json has the output as the guest wrote it. Once the platform has started the VM again, for a reset, a reboot, a stop and start, an upgrade, a reinstall or a restore, the output is what the guest wrote since, and the end of what it wrote before, at most 64 KiB too, comes first, above the line --- the platform started the instance again here --- (previous_boot in --json): a verbose boot cannot push out the kernel panic you reset the VM for. A VM created before consoles were recorded has its console recorded from its next start.

Asking for the state a VM is in already, running for a running VM or stopped for a stopped one, sends nothing to the host: the answer is a job that has already succeeded, whose result says changed: false, and --wait returns at once. A VM counts as running only when its guest has reported a reading since its last power change, reinstall, upgrade, or restore; otherwise running goes to the host, which starts a guest that powered itself off. A reboot always goes to the host.

For a home VM, read what its hosted gateway counted over the last hour and the last day in each direction:

Terminal window
ghost instance gateway-traffic <INSTANCE_UUID>

These figures are for information; billing never uses them. See Charges and resource limits for what the gateway bills.

If a home VM’s WireGuard key on its host may be lost or compromised, replace it:

Terminal window
ghost instance rotate-gateway-keys <INSTANCE_UUID> --wait

On a running VM, the tunnel to the gateway is down from the moment the host installs the new key until the gateway applies it, a few seconds, so connections through it may stall or drop. The job finishes once the gateway reports a handshake with the new key. On a stopped VM, the host keeps the new key for the VM’s next start, and the job finishes without waiting for a handshake.

Reinstalling destroys the current root filesystem while retaining the same VM capacity and network assignment:

Terminal window
ghost instance reinstall <INSTANCE_UUID> \
--image debian-13 \
--ssh-key-file "$HOME/.ssh/id_ed25519.pub" \
--yes \
--wait

You may also set a new hostname, startup command, or cloud-init payload; without --hostname the VM keeps its hostname. The reinstalled VM starts running even if it was stopped before, and ghost says so for a stopped VM, and for one whose stop is still under way (is stopping; the reinstall starts it again). Reinstalling is free, so to start over on the same size, reinstall the VM rather than delete it and create another, which costs the creation fee again. A failed bounded attempt leaves the VM in failed state with its allocation fenced; repeat reinstall explicitly to delete any partial replacement and try again. An image that starts no operating system within five minutes fails the reinstall with guest_boot_timeout, which is not retried, and leaves the VM powered off: reinstall another image or delete the VM.

A VM can be upgraded to a larger size of its host in place: more vCPUs, memory or disk, and no less of anything. From a size of N vCPUs with a core each, the mid-step of 2N vCPUs on the same N cores keeps the memory and disk and adds vCPUs, and the next size doubles the cores, memory and disk. List the sizes it can take, then choose one by its offering:

Terminal window
ghost instance upgrades <INSTANCE_UUID>
ghost instance upgrade <INSTANCE_UUID> --offering <OFFERING_ID> --yes --wait

Each listed size shows its CPU (4 vCPU, a mid-step’s too), its prices, what the upgrade adds to the balance the VM holds (the new size’s hold less the current size’s, at the days of its base price the VM was created with) and whether the host has room for it at the moment beside what the VM holds. The list is empty while the host takes no new rentals, or while its traffic price is above what the platform accepts now. The VM is shut down, given the size’s CPUs, memory and disk, and started again if it was running; it keeps its disk, its address and its hostname. From the moment the host reports the new shape the VM pays the size’s prices as the catalog offers them now, and a price change the provider had given notice of before no longer applies to it. Should the upgrade fail on the host, the VM is left stopped with its old size and terms, and what the upgrade took is given back; start it again or retry. A root filesystem grows into a larger disk on the next boot where cloud-init runs, which every catalog image does; on another system grow it yourself.

An upgrade to an offering of another host is refused with offering_not_on_host, since an upgrade does not move a VM, and one to the VM’s own size or a smaller one with not_an_upgrade, which names in what the offering is smaller. When the host has no room for the size, the refusal is capacity_unavailable, naming what the host lacks beside what the VM holds, and so it is while the host takes no new rentals where the VM runs. Each points to ghost instance upgrades.

Deletion is irreversible and requires an explicit confirmation flag:

Terminal window
ghost instance delete <INSTANCE_UUID> --yes --wait

A VM whose creation failed can be deleted once its host has cleaned it up, and that deletion completes at once. Deleting a deleted VM again returns the same deletion job. Deleting a VM keeps its backups, which stay billed for storage until you delete them, so ghost instance delete names those it keeps in a note on stderr.

A VM created from a hosted backup waits up to 15 minutes for you to authorize its restore, holding its capacity and balance meanwhile. Deleting it then ends its creation: the creation counts as a failure you caused (restore_authorization_abandoned), so its creation fee and restore charge are not refunded, and the deletion gives back the rest once the host has removed the VM.

A cloud VM’s public IPv4 address rests for an hour after the VM is deleted, so that what is still meant for the VM, such as the traffic your DNS records send there, reaches no other customer meanwhile; after that another VM may get it. Update your DNS records within the hour, and remove the address from your SSH known_hosts before you reach a new VM at an address you used before. The address of a VM whose creation failed was never the VM’s and is free at once. A shared_bandwidth home VM’s address rests for an hour too, and a high_bandwidth home VM’s address goes with its gateway.

Create a backup on the same host with local, or use hosted for independent off-host storage:

Terminal window
ghost backup create <INSTANCE_UUID> --destination local --wait
ghost backup create <INSTANCE_UUID> --destination hosted --wait
ghost backup list
ghost backup list <INSTANCE_UUID>
ghost backup list --all
ghost backup get <BACKUP_UUID>
ghost backup cancel <BACKUP_UUID>
ghost backup delete <BACKUP_UUID> --yes --wait

backup list leaves deleted backups out unless you pass --all (include_deleted=true in the API). For reading it prints one line per backup: its ID, its instance’s name, status, destination (hosted or local), image, size, and creation time, and its purpose when it is not a regular backup. backup get, a deleted backup included, and --json show all of it: its disk size, the smallest root it restores to, the firmware and image its instance had when it was taken, the hostname the instance had then (hostname) and its name, hostname and state now, its prices, the fingerprint of the key a hosted backup is encrypted for, and for a local backup the host and location that store it. In JSON a hosted backup’s destination reads gigahost_s3.

A hosted backup is kept in the platform’s storage nearest to its VM’s host, currently in Frankfurt, Los Angeles, or Singapore, and stays there; see Charges and resource limits. It uploads at up to its host’s backup upload limit, which all hosted backups of the host share: 32 Mbit/s, about 4 MB/s, unless the provider set another.

Restoring replaces the VM’s current root disk, so the command also requires explicit confirmation:

Terminal window
ghost backup restore <BACKUP_UUID> --yes --wait

A backup knows its instance, so backup cancel, backup restore and backup delete take the backup’s UUID alone; for the latter two the older form with the instance’s UUID first still works. A restore also gives the instance the backup’s image in ghost instance get. Only an available backup can be restored or give a new VM: ghost refuses any other before it signs anything, with backup_not_available, and says to retry once a backup still being made, or restored, is done. A local backup whose host is missing the backup pool that holds it lists as unavailable, with the reason in an unavailable column (in JSON the backup’s unavailable object): it is refused with backup_pool_unavailable and costs nothing until the host has the pool back. One whose artifact was gone when the pool came back is lost: it can never be restored, costs nothing more, and is deleted with ghost backup delete to leave the list. While a restore writes the VM’s disk, the VM reads provisioning, as during a reinstall. A restore writes only the data the backup holds, so it takes about as long as that data, not as the disk is large.

backup create and backup restore print what they cost before they sign the charge: a backup’s price per GiB and the most it can cost for the VM’s disk, with the storage and restore prices it will keep, or the restore charge. A backup deletion runs beside the VM’s other jobs, and several deletions run at once.

A backup keeps the firmware, BIOS or UEFI, that its instance booted with, and it restores in place only while the instance still boots with the same firmware. After a reinstall with an image that boots with the other firmware, restore the backup into a new instance instead, as shown below.

The source hypervisor compresses and encrypts each local or hosted backup while streaming it from the VM, and stores all-zero regions of the disk without data. For hosted restore, the client unwraps and rewraps only the small backup data key locally. The account key and private recovery key are never sent to the backend or hypervisor. backup restore and instance create --backup of a hosted backup wait at most --authorization-timeout-seconds (300) for the destination host’s key offer and authorize no offer that comes later; the job they started goes on, and their --json error names it.

Creation and restore also sign a separate paid-operation authorization with the billing key derived from the active account key. The prices are the platform’s, the same on every host, and come from the public backup-pricing catalog: a hosted backup costs $0.005 per GB to create and to restore and $0.03 per GB per month for storage, a local one $0.001 per GB and $0.01 per GB per month. Each backup records its restore and storage prices when it is created. Charging rounds artifact bytes upward to MiB; displayed GB/TB units mean GiB/TiB. The traffic of a backup is not charged.

Nothing is held for a backup. It is charged once, when it is published, for the compressed and encrypted artifact’s size and for no more than the size of the volume; a backup that fails or is cancelled before is charged nothing. ghost backup create is refused with insufficient_balance unless your balance covers a backup as large as the volume and $1 beside. Restore knows the immutable artifact size at admission and posts its non-refundable charge with the job. Storage is billed in 15-minute intervals on a fixed 30-day month, a local backup’s until its asynchronous delete completes and a hosted one’s within the same boundaries.

Backups and restores are bounded streams rather than full temporary disk copies. Running VMs use a point-in-time pull backup; stopped VMs use a read-only disk export. Restore replaces the current root volume only after the size checks pass. A backup may restore to an equal or larger root; the restored disk keeps its partitions and filesystem at their backed-up size, and the platform never changes them. Every catalog image runs cloud-init’s growpart and resizefs on every boot, so a system restored from one grows into the larger disk on its next boot while cloud-init is still installed and enabled. Other systems must grow the partition and filesystem themselves, for example with growpart /dev/vda 1 and then resize2fs or xfs_growfs.

A running VM can always be backed up, and keeps running meanwhile. Its host keeps 16 GiB for the blocks the VM overwrites during the backup. A VM that overwrites more than that before the backup has read its disk is paused until it has, and resumed then: nothing it wrote is lost, but it does not answer meanwhile, for as long as the rest of the backup takes. The job’s result says whether that happened (upload_parts.guest_paused). Back up a VM that writes a lot while it is stopped, or when it writes least.

A host makes two backups or restores at a time, of running VMs or stopped ones; a further one is accepted and waits for its turn, and its job says so and how many are ahead of it, for example waiting for a backup slot on its host, which works on 2 backups or restores at a time; 1 ahead. The backups of running VMs also take turns at the host’s two 16 GiB scratches, so a third such backup can wait for one. A backup reads queued while it waits, staging once the host has begun it, uploading while a hosted backup uploads and finishes, and available when it is done. A stopped VM’s backup needs no room for writes, so its hosted backup keeps nothing on the host; the VM cannot be started until its backup has finished.

A VM takes no other job while its backup waits or runs. ghost backup cancel withdraws a backup that still waits: it is cancelled at once and costs nothing, and the VM takes other jobs again. The backup then counts as deleted, and its job, which the command names, as cancelled. A backup the host has begun is refused with backup_started; it runs to its end, and you can delete it then. Only the platform’s operator can cancel a backup the host has begun: if one holds your VM too long, open a support ticket. Such a backup reads failed, its job is cancelled with cancelled_by_operator, and nothing is charged for it.

A local backup reserves room for its artifact, about the root disk, in the host’s backup storage. Each offering’s backup_limits says which backups fit on every one of its hosts while nothing else holds that storage: local_running, local_stopped, and hosted_running. A local backup the host cannot take is refused with a message that names the sizes and what would fit:

  • local_backups_unavailable: the host has no backup storage and keeps no local backup of any size. Use --destination hosted.
  • backup_too_large_for_host: no backup storage of the host could hold a local backup’s artifact even when empty. Use --destination hosted.
  • backup_storage_busy: other local backups fill that storage. When some are still running, retry once they finish. Stored local backups keep their room until they are deleted, so otherwise delete a local backup of yours on that host, or use --destination hosted.

To create a separate VM from a backup, find an offering that can take it, then create the VM with --backup in place of --image; --for-backup reads your backup, so unlike the rest of the catalog it needs your account key:

Terminal window
ghost catalog offerings --for-backup <BACKUP_UUID>
ghost instance create \
--offering '<OFFERING_ID>' \
--backup <BACKUP_UUID> \
--wait

The selected root disk must be at least the backup’s disk size. Hosted backups can restore onto any architecture-compatible offering. A local backup can only use capacity on the physical host that stores it. --for-backup keeps the offerings with a large enough root and, for a local backup, with that host among their host_ids; the backend still checks the architecture when you create. --host <HOST_UUID> alone keeps the offerings that can place a VM on one host. The server applies both, as its minimum_root_disk_bytes and host_id filters.

An offering whose hosts do not include a local backup’s host is refused with backup_host_mismatch, which names that host and its location. While that host is full or takes no new rentals, creation from the backup is refused with capacity_unavailable, or with offering_unavailable if it was the offering’s only host.

The new VM keeps the restored guest’s users and SSH keys, so the SSH-key and startup-command options are unavailable with --backup. Put anything else the new VM needs in a --cloud-init-file, which runs once at its first boot. Gigahost supplies the new network assignment and hostname and boots only after restore finishes. At its first boot the new VM also gets new SSH host keys and, on guests with systemd, a new /etc/machine-id; a restore in place keeps both.

Completed backups remain in their selected storage until you request deletion. An automatic backup made immediately before an insufficient-funds VM deletion has purpose: final_recovery in ghost backup list output. Treat this as the clearly identified restoration source for a replacement instance; its marker does not change if the source VM no longer exists. Use it with ghost instance create --backup as shown above. Deletion is a background job and keeps an audit tombstone after storage confirms removal. The website creates hosted backups itself, encrypted to the account’s current recovery public key; restoring a hosted backup needs the account key’s recovery private key, so the website asks the CLI to approve it (ghost account approve).

After migrating an account to a new account key, authenticate with the new account key and select the older account key only when restoring a backup encrypted to its recovery key. The active --seed-file still authorizes the new restore charge. Here the replacement is account.next.seed, as in the account key migration walkthrough, and the older account key is still the default account.seed:

Terminal window
ghost \
--seed-file "$HOME/.config/ghost/account.next.seed" \
--recovery-seed-file "$HOME/.config/ghost/account.seed" \
backup restore <BACKUP_UUID> --yes --wait

A hosted backup whose key does not match the account key is refused before anything is sent, with recovery_key_mismatch and the fingerprint of the key the backup was encrypted for: without --recovery-seed-file, the message says that a backup made before an account key migration needs the account key it was made with; with it, that the given account key does not match. ghost key inspect shows an account key’s recovery_key_fingerprint.

Commands that change state create an asynchronous job. Pass --wait to follow its step events on stderr and receive the final result on stdout, or inspect it later. --wait prints the job’s ID first, so Ctrl-C stops only the waiting and ghost job watch picks it up again. Each event line names its step, and a running step’s line is printed again as its progress grows.

Terminal window
ghost job list
ghost job list --instance <INSTANCE_UUID> --state active
ghost job list --kind create_backup --state failed
ghost job get <JOB_UUID>
ghost job watch <JOB_UUID>

ghost job list lists your jobs newest first, including those a provider’s retirement or an empty balance started on your VMs and backups. --instance keeps one VM’s jobs and its backups’ jobs; --state takes pending, running, succeeded, failed, cancelled, or active for pending and running jobs; --kind takes a job kind such as create_instance, create_backup, or rotate_gateway_keys. A page holds 50 jobs, or 1 to 200 with --limit; pass its next_cursor as --cursor for the next page. For reading, the list shows each job’s ID, kind, state, progress, and creation time, and the error code of a job that failed.

ghost job get shows a job’s steps, its newest 1,000 events with the step each belongs to, and its progress, and the current step’s own. A job’s progress is the mean of its steps’, but a step that moves a whole disk, a backup’s upload or a restore’s writing, makes nine tenths of it. The upload counts the data it has read and, far lighter, the zeros, so it moves steadily to its end, and a hosted restore’s wait for your authorization counts as part of its transfer, so its progress does not drop when you authorize it. Steps are named by their key; their IDs, which no command takes, are only in --json. The job’s result holds, by step, only the fields meant for you, such as a new backup’s size and SHA-256, or whether a restored VM is running and must grow its filesystem to use a larger disk; the host’s own paths and names are left out. ghost job get and ghost job watch with an ID that names no job of yours say so, and that ghost job list lists your jobs.

Creation remains in provisioning until the host sees the new VM’s operating system start and, for a home VM, its gateway reports a fresh WireGuard handshake; the VM’s billing starts then. A home VM’s gateway base charge starts earlier, once the gateway has its place for the VM: with shared_bandwidth once the VM’s own address has been reserved, with high_bandwidth once the VM’s own gateway has been created, which can be minutes before the VM runs. The job says what the host is doing, such as how much of the image has arrived, and the creation step’s progress follows the download up to 80% and is 90% once the disk holds the image. A home host keeps the catalog’s images downloaded ahead of time, so a creation or reinstall there rarely waits for its image. An image that starts no operating system within five minutes fails the creation with guest_boot_timeout, which is not retried. A new VM is not checked for reachability itself: a cloud VM only ever gets an address that passed its one-time check, and a home VM’s public address is its gateway’s.

A home VM uses one gateway, and its creation waits up to 15 minutes for the gateway to be created and up to 15 more for it to boot, since creating one can take the gateways’ cloud provider several minutes, and longer when its service is slow; --wait prints each wait once. If the gateway is not created or does not become ready in that time, or does not report the handshake, the creation fails, and Gigahost orders no second one. The job’s error then says in plain words what went wrong, that the creation charge is refunded, and that you can try again; gateway_quota_exhausted means that no gateway capacity is available right now, so try again later. While a gateway step waits for room among the gateways, the job says so and keeps trying.

A failed creation keeps its balance reserve, capacity, and address until its host has removed what the creation left there, which takes moments while the host is online; one that never reached its host holds nothing after the failure. A home VM’s gateway is given up at the failure. These steps survive backend or provider restarts. Unless you caused the failure, the creation charge and a home VM’s gateway base charges are refunded when it fails, as described under Charges and resource limits.

For safely retried automation, supply the same --idempotency-key each time you retry one requested change. When a request gets no answer, times out, is rate limited, or fails on the server, ghost prints the --idempotency-key that repeats exactly that change. For a VM you named by its name, it also prints the VM’s ID to repeat the change with, since by then the name may stand for another VM. A rejection prints none, since the same request would be rejected again. A conflict with a job still running on the resource ends with the ghost job watch <JOB_UUID> command that follows it; retry once that job has finished. A read that gets no answer at all, because the connection failed, timed out or closed, is sent twice more, after 1 and 2 seconds, with a notice on stderr; ghost never repeats a change by itself.

An idempotency key belongs to one kind of change on one resource: a creation to your account, and other changes to their instance, backup, or ticket. For 30 days, the same key with the same request returns the first answer again, even for a VM deleted since, and with a different request it is refused with idempotency_key_reused. ghost makes a new key for every command unless you pass --idempotency-key.

A refused request answers with a stable code and a message that says what to do; a conflict with a job still running on the resource also names it as job_id. Common codes:

Code Meaning
instance_provisioning The VM is still being created or reinstalled; wait for job_id.
operation_in_progress, backup_in_progress Another job is running on the VM; wait for job_id. Without a job_id, the VM changed state during the request: retry it.
instance_stopped A stopped VM cannot be rebooted or reset; start it instead.
instance_failed The VM’s creation or last reinstall failed; reinstall or delete it, as the message says.
instance_deleting, instance_deleted The VM is being or has been deleted.
not_home_instance Gateway commands apply only to home VMs.
idempotency_key_reused The key was already used for a different request.
invalid_offering_token The offering ID is not of2_ and 22 base64url characters, such as a longer of1_ ID from before; the message says whether the prefix, a character, the length, or the last character is wrong. Copy one from ghost catalog offerings.
offering_unavailable The offering’s terms changed, or none of its hosts takes new rentals; list the offerings again.
capacity_unavailable No host of the offering has room now, or a local backup’s host is full or takes no new rentals. The message says what the hosts lack: a free public IPv4 address (and until when a resting one rests), cores, memory, or root storage, or that a host keeps its last address for the largest size that fits it. For a home VM it names what the gateways lack: a free public address at the location, with when one is free again, or a gateway that can be set up or has room.
offering_not_on_host The upgrade names a size of another host; ghost instance upgrades lists the sizes the VM can take.
not_an_upgrade The offering is the VM’s own size, or smaller in what the message names.
insufficient_balance Your available balance does not cover the request; the message names what it needs and what is available.
backup_host_mismatch The offering cannot place a VM on the host that stores the local backup; find one with --for-backup.
backup_started The host has begun the backup, so you cannot cancel it any more; wait for job_id and delete the backup then, or ask support, since only the operator can cancel it.
backup_not_available Only an available backup can be restored or give a new VM, and only an available or lost one deleted; the message says what the backup is doing.
backup_pool_unavailable The local backup’s host is missing the backup pool that holds it, or checks it since it came back; try again later.
local_backups_unavailable, backup_too_large_for_host, backup_storage_busy The VM’s host keeps no local backups, or has no room for this one now; the message says what would fit, as under Backups.
account_not_activated No account is registered for this account key yet; activate it first.
activation_unavailable This deployment takes no MMX deposits, so its operator registers accounts; see Create and activate your account.
auth_key_retired An account key migration retired the account key that signed the request; select the new account key.
ticket_closed Reopen the ticket before replying.
review_revision_conflict Pass the review’s current revision.

Ask for JSON with --json, or with --compact for JSON on one line:

Terminal window
ghost --json instance list
ghost --compact instance list

With either, a command that fails also prints {"error": {...}} on stdout, whose fields are always there, null where they do not apply: code, the backend’s code, also for a refusal ghost makes itself before it sends anything, where the backend would refuse the same (with status null), recovery_key_mismatch for an account key that does not match the key a hosted backup was encrypted for, which only ghost can tell, no_response when no answer came, or null for a failure of ghost itself, such as a file it cannot read; message; status, the HTTP status; job_id, the job a conflict names, or the job the backend accepted when the command failed after that, such as a restore whose key offer came too late; request_id, the request’s ID from the error’s body or its x-request-id header, for support; and transient, whether repeating the request with its idempotency key may succeed. The error text still goes to stderr, ending with (request ID) where the answer named one, and the exit status is 1. A job followed with --wait that fails prints its final state instead.

Without either, results are printed for reading: fields are aligned, and lists are one line per item, as tables where they fit the terminal and as short blocks otherwise. Values are shown in their units under labels without the unit the API sends them in, such as available $500.00 for available_microusd, memory use in memory-hours, CPU use and uptime as percentages, and network rates in bit/s; timestamps are in UTC without fractions. Colors appear only on a terminal and never when NO_COLOR is set. Never parse this form or error text in scripts. Use the command’s exit status and the JSON response instead.

Lifecycle warnings such as provider retirement and unrecoverable-host notices are durable and can be listed independently of jobs, and marked read:

Terminal window
ghost account notifications
ghost account notifications mark-read <NOTIFICATION_UUID>...
ghost account notifications mark-read --all
ghost account notifications mark-unread <NOTIFICATION_UUID>

A low-balance warning that ends, because a top-up covers more than seven days again or nothing was billed for a day, is marked read, and a funding_warning_resolved notice says why.

Create a ticket from any activated renter or provider account:

Terminal window
ghost support create \
--category networking \
--priority normal \
--subject 'Public address is unreachable' \
--message-file ./details.txt \
--instance <INSTANCE_UUID>

A ticket may link to one owned instance, backup, ledger transaction, provider host, or provider incident. Replace --instance with --backup, --ledger-transaction, --host, or --provider-incident as appropriate; --ledger-transaction takes an entry’s transaction_id from ghost --json account history. The backend validates ownership and does not accept arbitrary cross-account links. A message of several lines is shown on several lines.

List or read the ordered conversation, reply, close it, or reopen it:

Terminal window
ghost support list
ghost support get <TICKET_UUID>
ghost support reply <TICKET_UUID> --message 'The issue still occurs.'
ghost support state <TICKET_UUID> closed
ghost support state <TICKET_UUID> open

You can set your own ticket only to open or closed; waiting and resolved are set by support, who use ghost support list --all for the global queue. Reopen a closed ticket before replying to it: a reply to a closed ticket is refused with ticket_closed. Ticket creation, simultaneous open tickets, message count, message bytes, and retention are bounded. File attachments are not accepted yet; --message-file reads message text rather than uploading that file.

Global configuration options include --api-url, --seed-file, --recovery-seed-file, --json, and --compact. Their environment-variable equivalents are GHOST_API_URL, GHOST_SEED_FILE, and GHOST_RECOVERY_SEED_FILE.