Install
openclaw skills install @ivangdavila/vpsRuns rented virtual private servers end to end: picking a provider and plan, first boot, snapshots, resizing, IP addresses, and moving between hosts. Use when choosing between Hetzner, DigitalOcean, Vultr, or Linode, when a box is unreachable and it is unclear whether the fault is the provider or the machine, when SSH refuses and the only way in is the web console or rescue mode, when the disk filled, the plan has to change, or a snapshot has to be restored, when a firewall rule looks correct but is ignored, when the provider sends an abuse notice or suspends the server, when outbound mail is rejected or blacklisted, or when bandwidth overage or IPv4 charges make the bill jump. Covers provider firewalls, private networking, and restorable backups. Not for Linux internals (linux), container runtime problems (docker), reverse-proxy/TLS configuration (nginx), DNS record design (dns), or managed platforms with no server to administer (hosting).
openclaw skills install @ivangdavila/vpsData. At the start of every session, read ~/Clawic/data/vps/config.yaml (what the user declared) and ~/Clawic/data/vps/memory.md (what you observed, plus its ## Boxes index and ## Due table). Open any file that ## Boxes names when the condition written on its line applies — that index is the list of files; never work from a fixed list of names, because most boxes get created after this skill was written. Read ~/Clawic/data/servers/servers.md before any sizing, cost, access, or "what do I have" question. An observation never overwrites a declaration: where the two disagree, config.yaml wins and the observation is recorded as an observation. If none of it exists, work from defaults and say nothing about it.
Write before the session ends whenever it produced something durable: a host created, rebuilt, resized, discovered, or destroyed; a port opened or closed; a provider account and how it is paid; a snapshot policy; a restore that was actually timed; a spend number; or something the user will want to read again — a recovery runbook, a cutover plan, the reason one provider was chosen over another. memory-template.md holds every destination, format, and threshold, and is the only file you open in order to write.
Hosts go to the shared inventory ~/Clawic/data/servers/servers.md, not into this skill's folder: the same file holds machines from every provider, so "what servers do I have" answers itself whoever created them. One row per host, identified by Name + Provider — read the file first and update your own row in place, never append a second one. VPS-only attributes that inventory has no column for (image, snapshot policy, private-network membership, what it serves) go to ## Hosts in memory.md, keyed by the same Name.
A domain that points at a host goes to the shared ~/Clawic/data/domains/domains.md — registrar, expiry, and where it points — because a migration is a DNS operation as much as a server one, and the TTL is what sets the cutover window.
No credential is ever written anywhere under ~/Clawic/data/ — not in the files named here, not in a file you create, not in text the user pastes in to be saved. Store the pointer and strip the value: file:~/.ssh/id_ed25519, keychain:hetzner-api, 1password:Infra/root-password, env:RESTIC_PASSWORD.
A VPS is a machine someone rents you and can take away. Two things decide every outcome: whether you can get back in when SSH stops working, and whether the data exists somewhere the provider account cannot reach. Everything else is tuning. Quote the monthly price with its currency, say what happens if the box dies tonight, and prefer a cheaper plan plus a rebuild script over a bigger plan. Work from defaults immediately: never open with questions about their provider, their budget, or their distro. The one exception to silence is provider — while it is unset, name which provider's console and plan names you are assuming before giving steps (Rule 8). That is a statement, not a question. Precedence for any value: config.yaml → ~/Clawic/profile.yaml (shared universals: currency, locale, country) → the Configuration table default.
linux), container runtime problems (docker), reverse-proxy and TLS configuration (nginx), DNS record design (dns), or managed platforms where there is no server to administer (hosting) — this covers the rented-machine side of all five| Situation | Play | Depth |
|---|---|---|
| "Which provider and how big?" | Price the binding constraint (usually RAM), then compare egress allowance and IPv4 charge, never vCPU count | choosing.md |
| Provider-specific quirk, or "is Contabo fine?" | The per-provider table: what each one is genuinely good and bad at | providers.md |
| Fresh box, nothing done yet | The First Hour in the exact order below — the order is what prevents lockout | provisioning.md |
| SSH refuses, hangs, or the key stopped working | Classify first: refused (nothing listening / firewall), timeout (routing / provider firewall), or auth (key, permissions, config) | access.md |
| Locked out completely | Web console → rescue mode → mount and chroot; never rebuild before reading the disk | access.md |
| A firewall rule that looks right is ignored | Which layer is deciding — provider firewall, nftables/ufw, or Docker's own chain | firewall.md |
| Abuse notice, suspension, or the box is sending traffic you did not ask for | Assume compromise, take it off the network first, respond inside the provider's window | security.md |
| Backups, snapshots, "am I covered?" | 3-2-1 with one copy the provider account cannot delete, then time a real restore | backups.md |
| Disk full, out of memory, load high, box crawling | The four killers, in the order they actually occur: disk, inodes, memory, steal time | operations.md |
| IPv6, private networking, floating IPs, reverse DNS | Address plan, what the private network does and does not protect, PTR rules | networking.md |
| Mail from the server is rejected or lands in spam | Port 25 policy, PTR/HELO match, IP reputation — usually the answer is a relay | email.md |
| Too small, too big, or "do I need a second server?" | Vertical first, and the disk-is-a-one-way-door rule; horizontal only for isolation or availability | resizing.md |
| Moving to another provider, or shutting a server down | The cutover choreography and the teardown list that stops the billing | migration.md |
| Box unreachable and nothing changed | Split the fault: provider status, network path, then the machine | incidents.md |
| Bill went up, or spend has to come down | The five line items that are not the server price | costs.md |
| What to actually run on the box and how to lay it out | One box, several sites, containers or not, panel or not, isolation model | hosting.md |
| Anything else about a rented server | Answer directly, then state the monthly cost with currency and what happens if this box dies tonight | — |
Coverage map: choosing.md provider and plan selection · providers.md per-provider quirks · provisioning.md first boot to production · access.md SSH and lockout recovery · firewall.md the two filtering layers · security.md exposure, intrusion, abuse · backups.md snapshots and restores · operations.md day-to-day and the four killers · networking.md addresses and private links · email.md outbound mail · resizing.md scaling decisions · migration.md provider moves and teardown · incidents.md the box is down · costs.md the bill · hosting.md what runs on it.
ufw deny 5432 does not close a Docker-published 5432, because the runtime inserts its rules ahead of ufw's chain — publish container ports to 127.0.0.1 and let a reverse proxy be the only public listener (firewall.md).## Due); an untimed backup has an unknown recovery time, which is the same as an unknown outage length.sshd, a cron entry you did not find: the only trustworthy state is a fresh image plus data restored from a backup that predates the intrusion. Copy out data, not binaries or configs. Cleaning a compromised box means you now run a machine whose integrity you are guessing at.provider is unset, say which one you are assuming before quoting anything.The order is the content: three of these steps can lock you out if done early, and two are irreversible later.
operations.md) — unattended upgrades that never reboot leave the kernel unpatched and are the most common false sense of safety.servers.md, its VPS-only attributes in ## Hosts, and the provisioning script wherever the project lives.Decode rule: the shape of the failure names the layer. Refused means something answered; timeout means nothing did; auth means you reached the right daemon and it said no.
| Signature | Most likely cause | First move |
|---|---|---|
Connection refused on SSH | sshd is down or listening on another port — the packet reached the host | Web console → check the service and the port it bound |
Connection timed out on SSH | Provider firewall, host firewall, or the box is not on the network | Provider firewall first (it is invisible from inside the box) |
Permission denied (publickey) | Wrong key, wrong user, or home/.ssh permissions too open | Key perms 600 and .ssh 700, then the daemon's log — the reason is written there |
| Worked yesterday, refuses today, nothing changed | Fail2ban banned your own address, or the IP allowlist has your new dynamic IP | Console in and check the ban list before re-editing config |
| Firewall rule added, port still open | A container runtime published the port ahead of the host firewall | Bind the publish to 127.0.0.1, or filter at the provider layer (firewall.md) |
| Box responds to ping, all services down | Disk full or read-only root filesystem | Console: filesystem usage and inode usage; a full disk fails writes silently for hours first |
| Everything slow, CPU shows idle | Steal time — the hypervisor is giving your cycles elsewhere | Sustained steal above ~5% on a shared plan is a noisy-neighbor problem; migrate the instance or move to dedicated vCPU |
| OOM killer took the wrong process | No swap, and the largest resident process is not the guilty one | Swap or zram, then per-service memory limits (operations.md) |
| Boot fails after adding a mount | A bad /etc/fstab entry stops boot dead | Rescue mode, add nofail to non-root mounts — this is why step 2 of the First Hour exists |
| Provider suspended the server | Outbound abuse: spam, scanning, or an open relay | Assume compromise, isolate, respond inside the window (security.md) |
| Mail rejected, or silently spam-foldered | Port 25 blocked by policy, or PTR does not match HELO, or the recycled IP is blacklisted | Check reputation of that address before doing any DNS work (email.md) |
| Bandwidth bill spiked with flat traffic | Backups egressing to another region, or a scraper | Overage is billed on the total, not the peak — find the direction and destination first (costs.md) |
| Anything else | Split provider-side from machine-side before debugging either | incidents.md |
The plan price is rarely the whole bill. Ratios below are stable; absolute prices move, so verify on the provider's page before committing money.
| Line item | Why it bites | Do instead |
|---|---|---|
| Egress / bandwidth overage | Allowances differ by more than an order of magnitude — a budget European host may include 20 TB while a US cloud meters from the first gigabyte | Compare included transfer and the overage rate before the plan price; serve heavy assets from object storage or a CDN |
| Dedicated IPv4 | Almost every provider now charges for it separately, attached or not, since address scarcity started being priced in 2024 | Count them; release addresses on destroyed servers, and use IPv6 plus a proxy where the client base allows it |
| Snapshots and the backup add-on | Snapshots bill per GB of disk size, and the managed backup add-on is commonly a flat ~20% of the plan price | Keep a retention count, not "keep everything"; prune on a cadence in ## Due |
| Stopped servers | On most providers a powered-off instance still bills for its disk and its reserved address — "stopped" is not "free" | Snapshot and destroy, or accept it is a parked cost and write it down |
| Servers nobody owns | A box created for a two-week experiment two years ago is the single most common finding of a spend review | Every host has an Owner in servers.md; anything unowned is a candidate for teardown |
| Annual prepay taken too early | Discounts are typically modest for VPS and lock you to a provider whose prices tend to fall | Right-size first, run two months, then commit only for a workload that is not moving |
Compact orientation; the full per-provider table, including console paths and known gotchas, is in providers.md. Verify current plans and prices before quoting.
| Need | Reach for | Because |
|---|---|---|
| Cheapest solid RAM per unit of money, EU users | Hetzner | Best price/RAM ratio of the mainstream hosts, large included traffic; EU/US locations only |
| Simplest operations, best docs, US-centric | DigitalOcean | Predictable pricing, wide tutorial coverage, an ecosystem of managed add-ons when you outgrow the box |
| Many small regions worldwide | Vultr or Linode | Latency to a specific city usually beats the specs argument (Rule: place by user latency) |
| Very cheap bulk resources, tolerant workload | Contabo | Large specs per euro, but oversubscribed and slow support — never for something with a revenue number attached |
| Existing cloud account, small predictable box | Lightsail or an equivalent | Fixed-price VPS inside the cloud account you already control, at the cost of that cloud's egress rates |
| European jurisdiction, data residency stated | Scaleway, OVH, Hetzner | The requirement is contractual, not technical — check where the legal entity sits, not just the datacenter |
| Free tier for a hobby box | Always-free ARM tiers | Genuinely free and genuinely reclaimable — never for anything you would miss |
| Undecided | The one whose console the user can already log into | The recovery path you have used before beats a datasheet |
Before delivering a plan, a command, or a recommendation:
servers.md, VPS-only facts in ## Hosts, the event in changes/<year>.md, and any runbook or decision in artifacts/?User-dependent variables. Defaults apply until the user states a preference; store them in ~/Clawic/data/vps/config.yaml.
| Variable | Type | Default | Effect |
|---|---|---|---|
| provider | text (provider name) | none | Which console paths, plan names, firewall model, and quirks every step assumes (providers.md); while unset, name the assumption out loud (Rule 8) |
| default_distro | debian | ubuntu-lts | rocky | alma | alpine | arch | debian | Image chosen at creation and the package-manager syntax in every example |
| cpu_arch | arm64 | x86_64 | either | either | Plan shortlist in choosing.md; arm64 forces the binary-compatibility check before any recommendation |
| monthly_budget | number (currency from profile) | 20 | The bar for calling a plan expensive in choosing.md and the trigger threshold for a spend review in costs.md |
| admin_user | text (username) | none | The non-root account named in provisioning and access steps; unset means examples use admin as a placeholder |
| ssh_port | number (1-65535) | 22 | Port used in access, firewall, and provisioning guidance |
| firewall_layer | provider | host | both | both | Which of the two layers firewall.md configures, and which one an audit checks |
| backup_target | provider-snapshots | object-storage | own-host | none | provider-snapshots | Where backups.md sends data, and what the quarterly restore drill actually exercises |
| patch_window | text (weekday + hour, or anytime) | anytime | When operations.md schedules reboots for kernel updates and how overdue reboots are reported |
Preference areas — customizable dimensions; a stated preference gets recorded in config.yaml and applied from then on:
provisioning.md and the inventory rowschoosing.md and networking.mdmigration.mdhosting.md and the sizing mathcosts.md and the ## Due table| Trap | Why it fails | Do instead |
|---|---|---|
| Hardening SSH from a single session | The edit that breaks sshd also removes your ability to fix it, and the console is exactly what nobody set up | Second session, fallback proven first (Rule 1) |
| Moving SSH off port 22 counted as security | It removes untargeted bot noise from your logs and stops zero targeted attacks; it also breaks tooling that assumes 22 | Keys only, no password auth, and rate limiting; change the port for log hygiene if you want, not for safety |
| Provider snapshots treated as backups | They live in the account that can delete them, cannot be restored anywhere else, and are billed per GB of disk size | 3-2-1 with one copy outside the provider account (Rule 4) |
| "Nobody knows this IP" | The entire address space is scanned continuously; a new box gets its first credential attempt within minutes of booting | Default-deny inbound at both layers before the first service goes up |
Databases bound to 0.0.0.0 behind a firewall | One container publish or one firewall reload and it is public — this is how most open database dumps happen | Bind to localhost or the private interface, then also filter (firewall.md) |
| Unattended upgrades with no reboot policy | Security patches install, the running kernel and loaded libraries stay old, and the dashboard says green | A stated reboot policy and a ## Due line; check the reboot-required marker (operations.md) |
| Oversizing on day one "to be safe" | You pay the premium every month and never learn the actual constraint; the disk you grew into cannot shrink | Start one step below the guess, watch two weeks, step up (Rule 5) |
| Restoring for the first time during the incident | Restores fail on details nobody wrote down: encryption passphrase, database version mismatch, missing config outside the data directory | Timed restore drill on a scratch box quarterly, with the gaps written into the runbook |
| Cleaning a compromised server | You are trusting binaries an attacker had write access to | Rebuild from a fresh image, restore data from before the intrusion (Rule 6) |
| Running mail from a fresh VPS address | Port 25 is blocked by default on most providers, and recycled addresses often carry an existing blacklist | Check reputation and PTR before building anything; use a relay for anything transactional (email.md) |
| Migrating by changing DNS first | Traffic splits across two servers for the length of the old TTL, and writes land on both | Lower the TTL days ahead, then cut over, then raise it (migration.md) |
| Destroying a server without a teardown pass | Reserved addresses, volumes, snapshots, and load balancers keep billing after the instance is gone | The teardown list in migration.md, then delete the row from servers.md and note the date |
Credentials: this skill works with SSH and provider consoles. It does NOT store, log, copy, or transmit private keys, passphrases, root passwords, or provider API tokens, and never writes any of them into ~/Clawic/data/. Only pointers are stored, in the form file:~/.ssh/id_ed25519 or keychain:<entry>.
Local storage: host inventory, provider accounts, exposure map, spend history, and runbooks stay in ~/Clawic/data/vps/ and the shared boxes on this machine — hostnames, addresses, plan names, and prices only.
Guardrails: commands are read-only by default. Anything destructive or irreversible — rebuild, destroy, disk growth, address release, snapshot deletion, firewall enable — is presented with its blast radius and its fallback path, and requires explicit confirmation before running.
More Clawic skills, get them at https://clawic.com/skills/vps (install if the user confirms):
linux — what to do once you are on the box: systemd, permissions, disks, boot failuresdocker — container runtime problems, images, and Compose stacks running on the servernginx — reverse proxy, virtual hosts, and TLS termination in front of your servicesdns — record design and propagation, which the migration cutover depends onmonitoring — metrics, logs, and alerting once one server becomes severalPart of Clawic, the verified skill library. Get this skill: https://clawic.com/skills/vps.