Welcome to the essential security guide for any new Debian server. A fresh installation is a blank slate, and taking the right steps immediately after setup is crucial for protecting your server from threats. This guide provides a logical, step-by-step process to establish a strong security baseline.

We will cover user management, hardening SSH access, configuring a firewall, setting up automatic intrusion prevention, and enabling automatic security updates.

โ„น๏ธ FOLLOW THE ORDER

These steps are designed to be followed sequentially. For example, we will set up SSH key authentication before disabling password logins to ensure you donโ€™t lock yourself out.

Changelog

DateChange
2026-03-03Comprehensive update: hardened SSH config, added backup strategy, monitoring, and WireGuard.
2025-07-10Initial version of the comprehensive Debian hardening guide.

Step 1: Initial Login, Update and Hostname

First, log into your new server as the root user. Your cloud provider will have supplied you with the initial IP address and password.

The very first action should always be to update the package list and upgrade all installed packages. For a fresh server, we use full-upgrade to ensure even kernel updates and new dependencies are handled.

# Log in as root
ssh root@your_server_ip

# Update package lists
apt update

# Upgrade all installed packages (including kernel updates)
apt full-upgrade

# Reboot to ensure the new kernel and all updates are active
reboot

After the reboot, log back in. Itโ€™s best practice to set the hostname immediately so all subsequent logs reflect the correct name.

# Set your server's hostname
sudo hostnamectl set-hostname my-awesome-server

You should also edit /etc/hosts to associate the new hostname with 127.0.1.1.


Step 2: Create a Dedicated Admin User

Operating directly as the root user is risky. We will create a new user account with administrative privileges via the sudo command. This improves security and provides better auditing.

Replace adminuser with a username of your choice.

# Create a new user
adduser adminuser

# You will be prompted to set a password and fill in user information.

# Add the new user to the 'sudo' group
usermod -aG sudo adminuser

# Log out from the root session
exit
โ„น๏ธ SUDO PASSWORD SECURITY

By default, sudo will ask for your user password before executing administrative commands. Do not configure NOPASSWD for your admin user. This password prompt prevents accidental destructive commands and is an additional authentication layer if your SSH session is compromised.

From now on, you will log in as this new user and prefix any administrative commands with sudo.


Step 3: Harden SSH Access

Securing SSH is the single most effective thing you can do to protect your server. While we will change the default port to reduce log noise from automated scanners, the real security comes from key-only authentication and the hardening settings below.

3.1. Set Up SSH Key Authentication

SSH keys are far more secure than passwords. A key pair consists of a private key (which you keep on your local computer) and a public key (which you place on the server).

On your local computer (not the server): If you donโ€™t have an SSH key pair yet, generate one. The ed25519 algorithm is modern and highly secure.

# This command is run on your local machine
ssh-keygen -t ed25519 -C "your_email@example.com"

Press Enter to accept the default file location and set an optional (but recommended) passphrase for your key.

Copy your public key to the server: The ssh-copy-id command is the easiest way to do this. It will automatically add your public key to the correct file on the server.

# Replace with your new username and server IP
ssh-copy-id adminuser@your_server_ip

After this, you should be able to log into your server as adminuser without being asked for a password (you might be asked for your keyโ€™s passphrase if you set one).

3.2. Configure and Secure the SSH Daemon

Now we will edit the main SSH configuration file to improve security.

โš ๏ธ DO NOT CLOSE YOUR TERMINAL!

Keep your current SSH session open while performing these steps. If you make a mistake, you can revert the changes. Only close the terminal after you have successfully tested the new login method in a separate terminal window.

Open the configuration file with a text editor:

sudo vim /etc/ssh/sshd_config

Make the following changes to harden the configuration:

# Change port (optional, reduces log noise from bots)
Port 8496

# Authentication
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
LoginGraceTime 30

# Restrict to specific user(s)
AllowUsers adminuser

# Disable unnecessary features
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no

# Restrict key algorithms to modern, secure options
PubkeyAcceptedAlgorithms ssh-ed25519,sk-ssh-ed25519@openssh.com

Key explanations for these options:

  • MaxAuthTries 3 โ€” Limits login attempts per connection to 3.
  • LoginGraceTime 30 โ€” Disconnects after 30 seconds without successful authentication.
  • AllowUsers adminuser โ€” Only the specified user(s) can log in via SSH.
  • X11Forwarding no โ€” Disables GUI forwarding, reducing attack surface.
  • AllowTcp/AgentForwarding no โ€” Prevents SSH tunneling and potential agent exploitation.
  • PubkeyAcceptedAlgorithms โ€” Restricts to Ed25519 and FIDO2/Hardware keys (YubiKey).

Save the file and exit the editor.

3.3. Test and Restart SSH

โš ๏ธ CRITICAL: TEST BEFORE RESTART

Always validate your configuration before restarting the SSH daemon. A syntax error can lead to a permanent lockout.

# ALWAYS test configuration before restarting
sudo sshd -t

# Only if no errors are shown, restart the service:
sudo systemctl restart sshd.service

Now, open a new terminal window and try to connect using the new port and your key.

# Use your username, IP, and the new port number
ssh adminuser@your_server_ip -p 8496

If the login is successful, your SSH hardening is complete. You can now safely close the old terminal window.

Step 4: Configure a Firewall with UFW

A firewall is essential for controlling network traffic. We will use UFW (Uncomplicated Firewall) because it is user-friendly and effective.

# Install UFW
sudo apt install ufw

Next, we will set up some basic rules. By default, we will deny all incoming traffic and allow all outgoing traffic. Then we will explicitly allow traffic for the services we need.

# Set default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow standard web traffic
sudo ufw allow http  # Port 80
sudo ufw allow https # Port 443

# IMPORTANT: Allow your new SSH port
# We use 'limit' to help protect against brute-force attacks
sudo ufw limit 8496/tcp # Use the port you chose

Now, enable the firewall. It will ask for confirmation to proceed.

sudo ufw enable

# Check the status of the firewall at any time
sudo ufw status verbose
๐Ÿ’ก CLOUD FIREWALL

Cloud providers like Hetzner, DigitalOcean, or AWS offer cloud-level firewalls in their web panel. These filter traffic before it reaches your server. If available, mirror your UFW rules there as an additional layer.


Step 5: Prevent Intrusion with Fail2Ban

With key-only SSH authentication, brute-force attacks against SSH are already neutralized. So why Fail2Ban? Because your server will likely run more than SSH. Once you deploy a web server, mail server, or other services, Fail2Ban protects those too. It is a general-purpose intrusion prevention tool.

# Install Fail2Ban
sudo apt install fail2ban

Fail2Banโ€™s configuration should be done in a local file, which overrides the default settings without being changed during package updates.

# Create a local configuration file
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Now, edit the new local file to configure it for our custom SSH port.

sudo vim /etc/fail2ban/jail.local

Find the [sshd] section. Most settings can be left as default, but you must update the port to match your custom SSH port and ensure itโ€™s enabled.

[sshd]
enabled  = true
port     = 8496
maxretry = 3
bantime  = 1h
findtime = 10m

Save the file and restart Fail2Ban to apply the changes.

sudo systemctl restart fail2ban

# Check the status of the SSH jail
sudo fail2ban-client status sshd
๐Ÿ’ก VERSATILE PROTECTION

Fail2Ban is incredibly powerful. You can configure it to protect many other services, such as web servers (Nginx, Apache) or mail servers, by creating custom jails.


Step 6: Automate Security Updates

Even with a hardened server, new vulnerabilities are discovered all the time. It is vital to install security updates promptly. The unattended-upgrades package can do this for you automatically.

# Install the package
sudo apt install unattended-upgrades apt-listchanges

Now, run the configuration wizard to enable it.

# This will open a simple text-based interface
sudo dpkg-reconfigure -plow unattended-upgrades

Select โ€œYesโ€ to enable automatic updates. This will create a configuration file that tells the system to automatically install packages from Debianโ€™s security repository.

To further harden this, you can configure email notifications and an automatic reboot policy (essential for kernel updates) in /etc/apt/apt.conf.d/50unattended-upgrades:

# Email notification (requires a working mail setup like msmtp)
Unattended-Upgrade::Mail "your@email.com";

# Automatic reboot policy (e.g., at 04:00 AM)
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";

Step 7: Final System Housekeeping

A few final touches will make your server easier to manage.

1. Set the Timezone: Correct log timestamps are crucial for troubleshooting.

# Set your timezone, e.g., for Berlin
sudo timedatectl set-timezone Europe/Berlin

2. Install Useful Tools: These small utilities are incredibly helpful for administration.

sudo apt install htop ncdu curl git
  • htop: An interactive process viewer.
  • ncdu: A disk usage analyzer to easily find large files.
  • curl: A tool for transferring data with URLs.
  • git: Version control system, often needed to download software.

Step 8: Basic Monitoring and Logging

A secure server is one where you notice when something goes wrong.

8.1. Persistent Journal Logging

On some Debian installations, journal logs are lost upon reboot. This makes them persistent:

sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald

8.2. Logwatch (Optional)

Logwatch provides a daily summary of SSH attempts, disk usage, and service errors.

sudo apt install logwatch
sudo logwatch --detail Med --range yesterday --output stdout

8.3. Monitoring Disk Space

Full disks are a common cause of service failures.

df -h
sudo ncdu /

Step 9: Backup Strategy

Security = Prevent + Detect + Recover. A server without a backup is not truly secure.

Core Principles

  1. Provider Snapshots: Enable them as your first layer of defense.
  2. Application-Level Backups: Use tools like borgbackup or restic.
  3. 3-2-1 Rule: 3 copies, 2 different media, 1 offsite.
  4. Test Restores: A backup is only as good as its last successful restore.

Example with BorgBackup:

sudo apt install borgbackup
borg init --encryption=repokey /path/to/backup/repo

# Note: Adjust paths like /var/lib depending on your data volume (e.g., Docker volumes, databases).
borg create /path/to/backup/repo::$(date +%Y-%m-%d) /etc /home /var/lib

# Verify your backup
borg list /path/to/backup/repo

Step 10 (Optional): Maximum Security โ€” SSH via WireGuard

For maximum security, do not expose SSH publicly at all. Access your server through a WireGuard VPN tunnel; SSH then listens only on the VPN interface. This eliminates scanning and brute-force attacks entirely.

10.1. Setup WireGuard Server

sudo apt install wireguard

# Create directory with restricted permissions first
umask 077
wg genkey | tee /etc/wireguard/server_private.key | wg pubkey > /etc/wireguard/server_public.key

(Note: The tee command outputs the private key to your terminal. Do not share this output.)

10.2. Server Configuration

Create /etc/wireguard/wg0.conf:

[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <contents of server_private.key>

[Peer]
PublicKey = <client_public_key>
AllowedIPs = 10.0.0.2/32

Enable the service:

sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0

10.3. Restrict SSH to VPN

In /etc/ssh/sshd_config, set:

ListenAddress 10.0.0.1

Test and restart:

sudo sshd -t
sudo systemctl restart sshd.service

10.4. Update Firewall

sudo ufw allow 51820/udp
sudo ufw delete limit 8496/tcp
โš ๏ธ WARNING

After this change, SSH is ONLY reachable via WireGuard. Ensure your VPN connection works before closing your session.

10.5. Client Configuration

To connect to your newly secured server, you will need to configure a WireGuard client on your local machine using the public key from the server and a newly generated client key pair:

# /etc/wireguard/wg0.conf on client
[Interface]
Address = 10.0.0.2/24
PrivateKey = <client_private_key>

[Peer]
PublicKey = <server_public_key>
Endpoint = your_server_ip:51820
AllowedIPs = 10.0.0.1/32

Conclusion

Congratulations! You have successfully performed the essential first steps to harden your new Debian server. By creating a sudo user, securing SSH with key authentication, and setting up a firewall and intrusion prevention system, you have built a solid foundation for any application you wish to deploy.

Combined with monitoring, a solid backup strategy, and optionally a VPN-secured access layer, your server is well-prepared for production use. Your server is now significantly more resistant to common automated attacks and hardware failures.


โ„น๏ธ WHAT'S NEXT?

With this secure base, you are now ready to install your applications, such as a web server (Nginx/Apache), a database, or a Docker environment.

๐Ÿ“šOFFICIAL DEBIAN SECURITY MANUAL ๐Ÿ›ก๏ธDEBIAN WIKI: FAIL2BAN