Security

VPS Configuration and Hardening Guide

A comprehensive guide on how to secure your Linux server, configure SSH, and set up a basic firewall.

Santiago Bugnón

Santiago Bugnón

CTO @ Nebula Solutions

|2026-02-18·7 min read
VPS Configuration and Hardening Guide

VPS Configuration and Hardening Guide

Base reference: [Ubuntu Server Guide](https://ubuntu.com/server/docs), [CIS Benchmarks](https://www.cisecurity.org/cis-benchmarks), [SSH Hardening - Mozilla](https://infosec.mozilla.org/guidelines/openssh)

Nota

Note: Throughout this document there are example users and placeholder values which you'll need to replace with your own username or custom options.

Example server data

This is the data your VPS provider gives you when creating the server instance.

FieldValue
IP`203.0.113.42`
User`root`
Password`Xk9#mP2@vL5nQw`
`203.0.113.x` is a range reserved for documentation (RFC 5737), it's not a real IP.

First connection:

ssh [email protected]

Step 1 - Update the system

apt update && apt upgrade -y

Nota

Why? A newly created server has outdated packages from the base image. Updating closes known vulnerabilities before doing anything else. It's the first step in any serious hardening guide (CIS, NIST).

Step 2 - Create an unprivileged user with sudo

adduser santikz
usermod -aG sudo santikz

To skip the password prompt when using sudo:

visudo

Add at the end of the file:

santikz ALL=(ALL) NOPASSWD:ALL

(create a user with any name of your preference, e.g.: admin, dev, sysadmin, marcelo, etc…)

Nota

Why? Always operating as `root` is bad practice. If someone compromises your session, they have total control of the system. An unprivileged user limits the radius of action of an attacker. `NOPASSWD` in sudo is a balance between security and acceptable convenience in dev environments where key-based authentication is already used.

Step 3 - Generate SSH keys and configure public key access

How SSH keys work

SSH uses asymmetric cryptography: two mathematically linked keys are generated. The private key stays on your local machine and never leaves it. The public key is copied to the server. When you try to connect, the server sends a challenge encrypted with your public key; only whoever has the corresponding private key can resolve it. If it matches, you get in, without needing to type a password. This eliminates the risk of someone intercepting or guessing your password, since it never travels over the network. `ed25519` is the algorithm currently recommended by Mozilla and NIST: shorter, faster, and more secure than traditional RSA.

Generate the keys (on your local machine, not the server)

# Linux / macOS
ssh-keygen -t ed25519 -f ~/.ssh/myserver

# Windows (PowerShell)
ssh-keygen -t ed25519 -f C:\Users\santikz\.ssh\myserver

This generates two files:

  • `myserver` - private key (never share, never upload to git)
  • `myserver.pub` - public key (this goes to the server)
  • Atención

    Note: give the file a descriptive name so you can differentiate between keys from different servers.

    Copy the public key to the server with SCP

    First we copy the `.pub` file to the server using `scp` (ssh copy). At this point we still use password because we haven't configured key-based access yet:

    # Linux / macOS
    scp ~/.ssh/myserver.pub [email protected]:/home/santikz/
    
    # Windows (PowerShell)
    scp C:\Users\tuusuario\.ssh\myserver.pub [email protected]:/home/santikz/

    Add the public key to authorized_keys (on the server)

    Connect to the server and run:

    ssh [email protected]
    mkdir -p ~/.ssh
    cat ~/myserver.pub >> ~/.ssh/authorized_keys
    rm ~/myserver.pub

    The `authorized_keys` file is the list of public keys the server accepts for authentication. The `>>` operator appends the key to the end of the file without deleting existing ones, which allows having multiple keys (e.g., from different team members).

    Verify permissions on the server

    SSH is strict about the permissions of these files. If they're misconfigured, it rejects the key even if everything else is correct.

    chmod 700 ~/.ssh
    chmod 600 ~/.ssh/authorized_keys

    Configure the local SSH file

    This file allows defining aliases for connecting without typing username, IP and key every time.

    Linux / macOS - file: `~/.ssh/config`

    Host myserver
        User santikz
        HostName 203.0.113.42
        IdentityFile ~/.ssh/myserver

    Windows - file: `C:\Users\tuusuario\.ssh\config`

    Host myserver
        User santikz
        HostName 203.0.113.42
        IdentityFile C:\Users\tuusuario\.ssh\myserver

    With this you'll be able to connect simply with:

    ssh myserver

    If public key login doesn't work

    Verify it's enabled on the server:

    sudo nano /etc/ssh/sshd_config

    Make sure these lines exist and are not commented out:

    PubkeyAuthentication yes
    AuthorizedKeysFile .ssh/authorized_keys

    Step 4 - Configure SSH: disable password and root login

    sudo nano /etc/ssh/sshd_config

    Find and modify (or add) these lines:

    PermitRootLogin no
    PasswordAuthentication no
    ChallengeResponseAuthentication no
    UsePAM no

    Restart SSH:

    sudo systemctl restart sshd
    **Before closing your current session**, open a second terminal and verify you can connect with the key. If something fails and you close the session, you'll be locked out of the server.

    Nota

    Why? Disabling `root` login via SSH is one of the first steps in any hardening checklist. Disabling passwords eliminates the most common attack vector: brute force and credential stuffing. From here on, only the correct private key can get in.

    Importante

    Note: this disables SSH login using a password, which means if we lose the private key we'll lose access to the server, so make sure to secure the key with a backup. If you feel unsure, leave root login and password login enabled, but set a long and secure password for root.

    Step 5 - Install Fail2Ban

    sudo apt install fail2ban -y
    sudo systemctl enable fail2ban
    sudo systemctl start fail2ban

    Basic configuration (create override file):

    sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
    sudo nano /etc/fail2ban/jail.local

    Verify that the `[sshd]` section is enabled:

    [sshd]
    enabled = true
    maxretry = 5
    bantime = 3600
    findtime = 600

    Restart:

    sudo systemctl restart fail2ban

    See banned IPs:

    sudo fail2ban-client status sshd

    Nota

    Why? Even though we've disabled password login, the SSH port is still exposed to automated scanners that try thousands of connections. Fail2Ban monitors system logs and automatically bans IPs that exceed the number of failed attempts. Reduces noise in logs and protects against enumeration attacks.

    Step 6 - Configure the Firewall with UFW

    sudo apt install ufw -y
    **Important:** Allow SSH **before** activating the firewall or it will lock you out.
    sudo ufw allow 22/tcp   # SSH
    sudo ufw allow 80/tcp   # HTTP
    sudo ufw allow 443/tcp  # HTTPS

    Activate:

    sudo ufw enable

    See status:

    sudo ufw status verbose

    Nota

    Why? A newly created server has all ports exposed to the internet. UFW (Uncomplicated Firewall) is the standard frontend for `iptables` on Ubuntu. The rule is simple: only what is explicitly allowed is open. This drastically reduces the attack surface.

    Optional Steps

    Docker

    Follow the official guide, it's the most up-to-date and varies by Ubuntu version:

    https://docs.docker.com/engine/install/ubuntu/

    After installing, add your user to the docker group so you don't have to use sudo each time:

    sudo usermod -aG docker santikz
    newgrp docker

    Monitoring tools and utilities

    sudo apt install -y htop btop tmux curl git wget unzip
    ToolWhat it's for
    `htop`Interactive process monitor
    `btop`Modern resource monitor (CPU, RAM, network)
    `tmux`Terminal multiplexer, keeps SSH sessions active
    `curl`Make HTTP requests from the terminal
    `git`Version control
    `wget`Download files from the terminal

    Share

    Santiago Bugnón

    Written by

    Santiago Bugnón

    CTO @ Nebula Solutions

    SecurityLinuxDevOps

    Related Articles

    VPS Configuration and Hardening Guide | Nebula Solutions