Individual skill
vps-forge
Set up, harden, audit and polish a Linux VPS (Debian/Ubuntu) to production grade without ever losing access — SSH keys and crypto, firewall, fail2ban/CrowdSec, automatic security updates, kernel hardening, Docker, Caddy/nginx with TLS, WireGuard/Tailscale, restic backups, phone alerts, monitoring and a final verified score. Use this skill whenever the user mentions a VPS, VDS, cloud server, droplet, Hetzner/DigitalOcean/Vultr/Linode/OVH/Oracle Cloud/AWS EC2 or Lightsail box (x86 or ARM), a "fresh server", "secure/harden/lock down my server", SSH hardening, ufw, fail2ban, "set up Docker/Caddy/nginx on my server", preparing a server for Coolify, Dokploy, CapRover or Kamal, server backups or monitoring, or asks to audit or check an existing server — even if they don't say "harden". Not for RHEL/Fedora-family servers, Kubernetes nodes (k3s, kubeadm), Proxmox or other hypervisor hosts, managed or shared hosting without root, or the user's own desktop.
skills/vps-forge/SKILL.md
Source commit de8b1c084559b4175be7aaabf5cb61a4d5a19580
What was checked
- Matches the observed repository HEAD
- Yes
- Scanner rules are current
- No
- Repository scan complete
- No
- Skill scan complete
- No
- Observed behavior
- Not checked yet
- Last checked
- Nov 5, 2026
These are observations at the recorded commit, not a guarantee of safety. Review the source before running it.
Install this skill
Choose an agent in your Stack or copy a command below. Commands fetch the pinned commit and copy only this skill directory. Existing destinations are refused. Copying a command does not install anything.
An install command is unavailable: current rules, complete repository and skill scans, an eligible behavior level and a permitted license are required.
Repository context
- Repository stars (not skill ratings)
- 39
- Repository last updated
- Oct 26, 2026
- License
- MIT
Observed agent integration: No specific integration evidence. Standard SKILL.md installation does not imply verified compatibility.
- agent-skills Repository topic
- ai-agents Repository topic
- claude-code Repository topic
- claude-skills Repository topic
- codex Repository topic
- cursor Repository topic
- debian Repository topic
- devops Repository topic
- devsecops Repository topic
- docker Repository topic
- devops Derived category
- security Derived category
- devops-containers Derived category
Files and excerpt
File metadata and permitted, redacted excerpts come from the recorded scan. Source links are pinned to that commit.
Browse files
SKILL.mdcore · 19,640 bytesSKILL.mdcore · 19,640 bytesSKILL.mdcore · 19,640 bytesassets/SERVER.mddoc · 3,153 bytesassets/alerts/notify.env.exampleasset · 1,151 bytesassets/apt/20auto-upgradesasset · 179 bytesassets/apt/52vps-forge-unattendedasset · 630 bytesassets/caddy/Caddyfileasset · 1,620 bytesassets/cloud-init.yamlasset · 6,324 bytesassets/docker/daemon.jsonasset · 205 bytesassets/fail2ban/vps-forge.localasset · 1,930 bytesassets/firewall/ufw-cloudflare-sync.serviceasset · 224 bytesassets/firewall/ufw-cloudflare-sync.shruns · 3,042 bytesassets/firewall/ufw-cloudflare-sync.timerasset · 158 bytesassets/journald/10-vps-forge.confasset · 216 bytesassets/motd/60-vps-forgeruns · 2,426 bytesassets/needrestart/50-vps-forge.confasset · 257 bytesassets/restic/env.exampleasset · 871 bytesassets/restic/excludeasset · 379 bytesassets/restic/includeasset · 247 bytesassets/restic/vps-forge-backup-maint.serviceasset · 354 bytesassets/restic/vps-forge-backup-maint.timerasset · 166 bytesassets/restic/vps-forge-backup.serviceasset · 288 bytesassets/restic/vps-forge-backup.shruns · 5,399 bytesassets/restic/vps-forge-backup.timerasset · 159 bytesassets/shell/vps-forge.bashrcasset · 1,090 bytesassets/ssh/00-vps-forge.confasset · 944 bytesassets/sysctl/99-vps-forge.confasset · 2,495 bytesevals/audit-production-shop/case.yamlasset · 481 bytesevals/audit-production-shop/files/audit.jsonasset · 2,564 bytesevals/audit-production-shop/graders/01-the-response-rates-the-publicly-publishe.mddoc · 154 bytesevals/audit-production-shop/graders/02-the-response-flags-the-adminer-container.mddoc · 142 bytesevals/audit-production-shop/graders/03-the-response-flags-the-unknown-or-old-ol.mddoc · 173 bytesevals/audit-production-shop/graders/04-the-response-explains-that-the-password.mddoc · 185 bytesevals/audit-production-shop/graders/05-the-response-mentions-the-missing-backup.mddoc · 128 bytesevals/audit-production-shop/graders/06-the-response-does-not-run-or-instruct-im.mddoc · 176 bytesevals/audit-production-shop/place-files.shruns · 62 bytesevals/audit-production-shop/prompt.mddoc · 295 bytesevals/compromised-box/case.yamlasset · 495 bytesevals/compromised-box/files/audit.txtdoc · 900 bytesevals/compromised-box/graders/01-the-response-identifies-that-the-server.mddoc · 240 bytesevals/compromised-box/graders/02-the-response-does-not-proceed-with-routi.mddoc · 148 bytesevals/compromised-box/graders/03-the-response-recommends-rebuilding-from.mddoc · 137 bytesevals/compromised-box/graders/04-the-response-recommends-rotating-credent.mddoc · 138 bytesevals/compromised-box/graders/05-the-response-recommends-preserving-evide.mddoc · 135 bytesevals/compromised-box/place-files.shruns · 62 bytesevals/compromised-box/prompt.mddoc · 265 bytesevals/coolify-host/case.yamlasset · 627 bytesevals/coolify-host/graders/01-root-key-login-kept.mddoc · 248 bytesevals/coolify-host/graders/02-panel-ssh-not-banned.mddoc · 336 bytesevals/coolify-host/graders/03-no-second-proxy.mddoc · 200 bytesevals/coolify-host/graders/04-dashboard-port.mddoc · 246 bytesevals/coolify-host/graders/05-backs-up-coolify.mddoc · 144 bytesevals/coolify-host/prompt.mddoc · 390 bytesevals/fresh-ubuntu-docker-web/case.yamlasset · 616 bytesevals/fresh-ubuntu-docker-web/graders/01-the-sshd-hardening-drop-in-is-named-so-i.mddoc · 234 bytesevals/fresh-ubuntu-docker-web/graders/02-password-authentication-and-root-login-a.mddoc · 194 bytesevals/fresh-ubuntu-docker-web/graders/03-ssh-or-firewall-changes-are-wrapped-in-a.mddoc · 218 bytesevals/fresh-ubuntu-docker-web/graders/04-the-app-container-is-published-on-127-0.mddoc · 205 bytesevals/fresh-ubuntu-docker-web/graders/05-apt-commands-are-non-interactive-debian.mddoc · 159 bytesevals/fresh-ubuntu-docker-web/graders/06-fail2ban-is-configured-with-the-systemd.mddoc · 140 bytesevals/fresh-ubuntu-docker-web/graders/07-the-telegram-bot-token-is-placed-in-a-ro.mddoc · 162 bytesevals/fresh-ubuntu-docker-web/prompt.mddoc · 492 bytesevals/k3s-node-out-of-scope/case.yamlasset · 285 bytesevals/k3s-node-out-of-scope/graders/01-stops.mddoc · 150 bytesevals/k3s-node-out-of-scope/graders/02-explains-why.mddoc · 217 bytesevals/k3s-node-out-of-scope/prompt.mddoc · 290 bytesevals/local-nginx-no-trigger/case.yamlasset · 196 bytesevals/local-nginx-no-trigger/graders/01-no-skill.mddoc · 132 bytesevals/local-nginx-no-trigger/graders/02-answers.mddoc · 191 bytesevals/local-nginx-no-trigger/prompt.mddoc · 256 bytesevals/lockout-trap/case.yamlasset · 406 bytesevals/lockout-trap/graders/01-the-response-warns-that-disabling-passwo.mddoc · 137 bytesevals/lockout-trap/graders/02-the-response-includes-generating-an-ssh.mddoc · 185 bytesevals/lockout-trap/graders/03-the-response-creates-or-uses-a-non-root.mddoc · 131 bytesevals/lockout-trap/graders/04-the-response-requires-verifying-login-fr.mddoc · 162 bytesevals/lockout-trap/graders/05-any-sshd-change-is-validated-with-sshd-t.mddoc · 98 bytesevals/lockout-trap/prompt.mddoc · 283 bytesevals/narrow-ssh-port/case.yamlasset · 437 bytesevals/narrow-ssh-port/graders/01-does-only-what-was-asked.mddoc · 202 bytesevals/narrow-ssh-port/graders/02-offers-the-rest.mddoc · 184 bytesevals/narrow-ssh-port/graders/03-port-change-is-safe.mddoc · 225 bytesevals/narrow-ssh-port/graders/04-socket-activation.mddoc · 196 bytesevals/narrow-ssh-port/graders/05-no-self-ban.mddoc · 152 bytesevals/narrow-ssh-port/prompt.mddoc · 286 bytesreferences/alerts-monitoring.mddoc · 4,869 bytesreferences/audit-mode.mddoc · 4,659 bytesreferences/backups.mddoc · 6,878 bytesreferences/base.mddoc · 12,449 bytesreferences/comfort.mddoc · 2,228 bytesreferences/databases.mddoc · 3,596 bytesreferences/distro-notes.mddoc · 5,878 bytesreferences/docker.mddoc · 8,485 bytesreferences/firewall.mddoc · 14,532 bytesreferences/kernel.mddoc · 6,980 bytesreferences/paas.mddoc · 7,654 bytesreferences/reproducibility.mddoc · 3,179 bytesreferences/script-mode.mddoc · 6,607 bytesreferences/ssh.mddoc · 21,751 bytesreferences/troubleshooting.mddoc · 7,344 bytesreferences/vpn.mddoc · 5,237 bytesreferences/web.mddoc · 5,198 bytesscripts/audit.shruns · 64,534 bytesscripts/deadman.shruns · 30,926 bytesscripts/external-check.shruns · 18,901 bytesscripts/install-alerts.shruns · 20,166 bytes
Read excerpt
# VPS Forge
Take a fresh or existing Debian/Ubuntu VPS to a state the user will be proud of: locked down, observable, backed up, documented, reproducible and pleasant to live on. Do it with a safety net under every risky step, so access to the server is never lost.
Speak the user's language. Before every change, say in one line what you are changing and why. When the user asks "why?", answer properly: the reasoning matters more than the command.
## 1. Rules that keep the user safe
These exist because one wrong `sshd` or `ufw` change can make a server unreachable, and the user may have no idea how to use the provider's rescue console.
1. **Never cut off access.** Every SSH, firewall, sudo or PAM change goes inside a dead-man switch: `scripts/deadman.sh arm` → change → validate → test from a **new, unmultiplexed** connection → disarm. If the new connection fails, do nothing and let the timer roll back (default 5 min). Arm longer (`arm 10`) when the change needs several edits or a package install, and if it runs late, `vps-forge-deadman extend <min>` moves the rollback while the snapshot stays the same. Never extend after a failed test login.
- **Before the first risky step,** make sure the user knows where the provider's web console (VNC/serial) is. Keep the existing session open throughout.
- **The test must be a new connection.** A plain `ssh host` reuses the live ControlMaster, which never touches the new sshd, port or firewall. Always test with `-o ControlPath=none`.
- **Disarming.** With NOPASSWD sudo (or the temporary grant) the test is the disarm: `ssh -o ControlPath=none -o BatchMode=yes -p <port> <admin>@<host> 'sudo -n vps-forge-deadman disarm'`. Under password sudo, `sudo -n -v` must fail with `a password is required`, and you disarm from the root session (ssh.md "Test and disarm").