CATALOGUE Rates in USD · provisioned manually · capacity confirmed per order
VPS Guides

How to Secure a New Linux VPS: SSH Keys, Firewall, Updates and Backups

A freshly installed Linux server is a machine on the public internet with a login prompt. On a busy network that prompt is found by scanners within minutes. The work of turning it into something you can leave running unattended takes an hour or two, and almost all of it is a small number of decisions made in a specific order.

This guide covers the baseline for a new KVM VPS: key-based SSH, a second non-root account, a default-deny firewall, automatic security updates, and a backup you have actually restored. It assumes Ubuntu 24.04 or Debian 12 with apt, and notes where AlmaLinux 9 or Rocky 9 with dnf differ. The commands are deliberately plain; adjust names and ports to match your own conventions.

One rule matters more than any command here: never close the session you are working in until you have opened a second one and logged in successfully with the new settings. Almost every lockout story is someone who changed sshd_config, reloaded, and disconnected.

Before you connect

Know three things before you touch the machine: the IPv4 address, the root password or key you were given, and which operating system image was installed. On this catalogue the connection details appear in the customer dashboard under the service once the order is marked ready, and the VPS catalogue lists the images and the hardware each configuration includes.

The allowlisted images are Ubuntu 24.04, Debian 12, AlmaLinux 9 and Rocky 9, plus Windows Server 2022 and a custom ISO option. The steps below assume one of the four Linux images and a server with no data on it yet. If the image supports reinstalling from your panel, note that a reinstall is a clean slate, not a repair.

Step 1: create your own key pair

Generate the key on the machine you will connect from, not on the server:

ssh-keygen -t ed25519 -a 100 -C "laptop-to-offshore"

ed25519 is the current default choice for new keys: short, fast and not dependent on the strength of a random prime. The -a 100 flag sets key-derivation rounds for the passphrase, which is worth the fraction of a second it costs at login. Give the passphrase somewhere safe; a key with no passphrase on a stolen laptop is an open door.

Copy the public key to the server. If the image has ssh-copy-id available on your side:

ssh-copy-id -i ~/.ssh/id_ed25519.pub root@SERVER_IP

If not, append the contents of ~/.ssh/id_ed25519.pub to /root/.ssh/authorized_keys on the server, then fix the permissions, which is where most failures come from:

mkdir -p /root/.ssh && chmod 700 /root/.ssh
chmod 600 /root/.ssh/authorized_keys

Open a new session and confirm you get in without a password prompt. Only then continue.

Step 2: create a non-root account

Running daily work as root is the habit that turns a small mistake into a rebuild. Create a personal account with sudo:

On Ubuntu or Debian:

adduser deploy
usermod -aG sudo deploy

On AlmaLinux or Rocky:

useradd -m -s /bin/bash deploy
passwd deploy
usermod -aG wheel deploy

Copy the same public key into the new account so it can log in the same way:

rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy

Test it: ssh deploy@SERVER_IP, then sudo -v. If both work, you no longer need root logins for normal administration.

Step 3: harden the SSH daemon

Modern OpenSSH reads a drop-in directory, so put your changes in a single file instead of editing the distribution's configuration. Create /etc/ssh/sshd_config.d/10-hardening.conf with:

PermitRootLogin prohibit-password
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
LoginGraceTime 20
X11Forwarding no
ClientAliveInterval 300
ClientAliveCountMax 2

A few notes on the choices:

  • PermitRootLogin prohibit-password allows the root account by key only. Once your deploy account is comfortable, no is stricter still, and is the better end state.
  • PasswordAuthentication no is the single highest-value line in the file. It removes online password guessing entirely as an attack path, which is what most of the noise in your authentication log is.
  • KbdInteractiveAuthentication no closes the PAM path that some distributions still leave open for password logins even when PasswordAuthentication is off.
  • X11Forwarding no removes a service you almost certainly do not need on a server.

Validate the configuration before reloading. This step catches syntax errors that would otherwise leave you with a daemon that refuses to start:

sshd -t

Then reload rather than restart, so existing sessions survive:

systemctl reload ssh

On AlmaLinux and Rocky the unit is called sshd:

systemctl reload sshd

Open a second terminal and log in as deploy with the key before you close the first one.

Step 4: a default-deny firewall

The principle is simple: drop inbound traffic that nothing asked for, allow the services you actually run, and allow SSH before you enable anything.

On Ubuntu and Debian, ufw is the least error-prone option:

ufw default deny incoming
ufw default allow outgoing
ufw allow OpenSSH
ufw enable
ufw status verbose

If you have already moved SSH to a non-standard port, allow that port specifically and keep the rule for 22 until you have verified the new one.

On AlmaLinux and Rocky, firewalld is already present:

firewall-cmd --permanent --add-service=ssh
firewall-cmd --reload
firewall-cmd --list-all

When you add a web server later, open exactly what it needs — ufw allow 80,443/tcp or firewall-cmd --permanent --add-service=http --add-service=https — rather than disabling the firewall when something does not connect. A database port opened to the world "for a minute" is how most compromised servers begin.

Step 5: automatic security updates

Unpatched software is the most common way a server is lost, and the fix is boring. On Ubuntu and Debian, install the unattended upgrade service:

apt update
apt install unattended-upgrades
dpkg-reconfigure --priority=low unattended-upgrades

That writes /etc/apt/apt.conf.d/20auto-upgrades with the periodic settings. Confirm it contains:

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

Then check what is allowed to install automatically in /etc/apt/apt.conf.d/50unattended-upgrades. By default the security pocket is enabled on Ubuntu, which is the right scope: security fixes apply themselves, and feature upgrades stay a decision you make. Keep automatic reboots off unless you have planned for them; instead, watch for the reboot flag and schedule a window:

ls /var/run/reboot-required 2>/dev/null && cat /var/run/reboot-required

On AlmaLinux and Rocky, dnf-automatic does the same job:

dnf install -y dnf-automatic

In /etc/dnf/automatic.conf set upgrade_type = security and apply_updates = yes, then enable the timer:

systemctl enable --now dnf-automatic.timer
systemctl list-timers dnf-automatic.timer

Check the results later rather than assuming they happened. journalctl -u unattended-upgrades --since "7 days ago" or /var/log/dnf-automatic.log will tell you whether updates are actually being applied, and a silent failure is common enough that the log is worth a look in the first week.

Step 6: time, swap and a look at what is listening

Two things that are not security features but cause security-shaped problems:

Time. Certificates, TOTP codes and logs all assume a correct clock. timedatectl set-timezone UTC and timedatectl status will confirm System clock synchronized: yes. A server with drifting time produces log entries you cannot correlate and TLS handshakes that fail for no visible reason.

Swap. A 2 GB server running a build can hit the OOM killer and take a database with it. A small swap file is not a performance feature; it is a cushion:

fallocate -l 2G /swapfile && chmod 600 /swapfile
mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab

Then look at what is actually listening:

ss -tulpn

Every line is a service reachable from somewhere. Anything bound to 0.0.0.0 or :: is reachable from the internet unless the firewall says otherwise. A database listening on all interfaces with a weak password is the single most common finding in a post-incident review.

Step 7: a backup that has been restored

A backup you have never restored is a hypothesis. The minimum that holds up:

  • Move it off the machine. A snapshot on the same provider, in the same account, is not a backup against a billing problem or an account compromise.
  • Encrypt it before it leaves. restic and borg both encrypt client-side, so the storage provider sees ciphertext.
  • Include the configuration, not only the data. /etc, the crontabs, the web server vhosts and the database dumps.
  • Test the restore on a second machine, at least once.

A workable pattern with restic against an SFTP target or S3-compatible storage:

restic init --repo sftp:backup@backup-host:/srv/restic/server1
restic backup --repo sftp:backup@backup-host:/srv/restic/server1 /etc /home /var/www
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

For databases, dump to a file first so the backup holds a consistent snapshot rather than open files:

mysqldump --single-transaction --routines --triggers --default-character-set=utf8mb4 app > /var/backups/app-$(date +%F).sql

Then schedule it. A cron entry that runs at 03:15 and logs its exit status is enough to start with; an alert when the job fails is what makes it a backup system rather than a script.

The mistakes that undo all of this

  • Disabling password authentication before the key works. Test with a second session, always.
  • Enabling the firewall over the only SSH connection. Allow SSH first, then enable.
  • chmod -R 777 to fix a permission error. It creates the next problem while hiding this one. Fix ownership instead.
  • Running the application as root. One remote-code-execution bug then becomes a full server compromise rather than a contained one.
  • Exposing the database "just for the import". Use an SSH tunnel; ssh -L 3306:127.0.0.1:3306 deploy@SERVER_IP costs nothing.
  • Trusting a backup that has never been restored. The failure is discovered on the worst possible day.
  • Treating a reinstall as a repair. Reinstalling destroys the evidence you would need to understand how the server was compromised.

A short verification pass

Run this after the steps above and keep the output somewhere:

sshd -T | grep -E 'permitrootlogin|passwordauthentication|pubkeyauthentication'
ufw status verbose
systemctl is-enabled unattended-upgrades
ss -tulpn

If permitrootlogin says prohibit-password or no, passwordauthentication says no, the firewall is active with a short allow list, and nothing unexpected is listening, you have the baseline. From there the work is ordinary: keep the application patched, watch the logs, and rehearse the restore.

For the next decision — whether this workload belongs on a VPS or on a whole machine — see VPS vs dedicated server. If the reason you are on this provider is jurisdiction rather than hardware, the offshore hosting primer explains what the location does and does not change, and the dedicated server catalogue covers the bare-metal alternative.

INFRASTRUCTURE

Put the notes into practice.

Every server is provisioned manually after payment review. Browse the live catalogue or read how ordering works.

PAGE NOT PUBLISHED

Preview

This page is not published yet. The catalogue, anonymous accounts and reviewed USDT top-ups are live; this destination will follow in a later stage.

Orders, payments and delivered services are handled in your customer dashboard.