Aller au contenu principal
Nicolas Cousin Tech SolutionsNicolas Cousin Tech Solutions

Hardening a VPS Before Installing AI Agents

Before installing the first AI agent on my VPS, I hardened it: key-only SSH, a firewall, Tailscale, and a safety net in case something goes wrong. Second article in the series on my agentic environment.

By Nicolas CousinPublished on September 22, 2026

Hardening a VPS Before Installing AI Agents

TL;DR

Before installing a single AI agent on my VPS, I hardened it: system updates, key-only SSH, a UFW firewall set to deny-everything-except-what's- needed, a Tailscale VPN so SSH is no longer publicly exposed, and a safety net (a recovery key, a KVM console) in case I lock myself out. An agent capable of running commands raises the stakes of hardening considerably: it's no longer just me who can get something wrong.


Table of contents


Why harden before installing anything

Order matters. I hardened the VPS before installing Docker, Ollama, or any agent on it, not after. An AI agent that can run shell commands isn't just a tool you use anymore: it's a process with potentially the same rights I have on the machine. A bad network configuration or a root account reachable by password is already risky with a single human at the controls. With an agent capable of acting on its own, the margin for error shrinks further.

None of what follows is specific to AI: it's the standard basics of hardening an Ubuntu server exposed to the internet. I'm spelling it out because it becomes, I think, a prerequisite you can no longer skip once autonomous agents enter the picture.

Updating the system

The first command, before anything else:

sudo apt update && sudo apt upgrade -y

Nothing sophisticated, but an up-to-date system closes a good chunk of known vulnerabilities before you even touch the configuration.

SSH by key, nothing else

Two settings in /etc/ssh/sshd_config:

PermitRootLogin no
PasswordAuthentication no

The order in which you apply this matters, and it's the easiest mistake to make: disabling password authentication before you've copied your own public key to the server, and locking yourself out. So I did it in this exact order:

  1. Generate a key pair locally if you don't already have one.
  2. Copy the public key to the VPS:
    ssh-copy-id ubuntu@my-vps
    
  3. Reconnect once with the key to confirm it works, before touching anything else.
  4. Only then, edit sshd_config and restart the service:
    sudo systemctl restart ssh
    

If step 3 is skipped and key-based login doesn't work, you end up with a VPS reachable only through the host's emergency console (see below) until it's fixed.

The firewall: deny by default, allow what's needed

UFW set to "deny everything incoming, allow everything outgoing":

sudo ufw default deny incoming
sudo ufw default allow outgoing

Another classic trap here: enabling UFW before explicitly allowing the SSH port cuts access on the next reconnect, not necessarily the current session, but better not to count on that. The SSH rule needs to be added before enabling, not after:

sudo ufw allow OpenSSH
sudo ufw enable

At this point the VPS only accepts incoming SSH connections, and lets everything else out freely (updates, API calls, and so on).

The safety net

Before touching anything else, a question worth asking: what happens if the next firewall rule locks me out? Closing public access later in this article means giving myself the means to get badly wrong if a configuration goes sideways. Two safety nets, checked beforehand, not after:

  • a recovery SSH key, generated separately and stored in a secure password manager rather than on the machine used day to day, protected by a passphrase like any other private key;
  • the host's KVM console (OVH in my case), which gives direct access to the machine independent of the network, including if both SSH and Tailscale become unreachable.

I thankfully haven't needed either of them so far, but having them checked and ready before closing anything changes the nature of the risk: a misconfiguration becomes recoverable in a few minutes instead of a back-and-forth with the host's support.

Tailscale: closing SSH to the public

With the safety net checked, the next step is to stop exposing SSH on the public IP at all. I use Tailscale, a VPN that creates a private network between my machines (the "tailnet"):

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

Once connected to the tailnet and the SSH connection over the Tailscale IP verified, I close public access:

sudo ufw allow in on tailscale0 to any port 22
sudo ufw delete allow OpenSSH

Port 22 is no longer reachable from the internet at all, only from the private Tailscale network. An external port scan of the VPS no longer sees anything open for SSH.


The VPS is now reachable only by key, only over Tailscale, with a verified safety net. Next: installing Docker, Ollama, and Hermes on this base, and the first trap that wasn't on the original plan.