Ubuntu Initial Server Setup and Hardening: A Complete Guide

Share on Social Media

Secure your new server in minutes. Follow our complete guide to Ubuntu initial Server setup and hardening to protect your data from day one. Expert tips inside. #CentLinux #Ubuntu #CyberSecurity



Introduction

When you spin up a fresh Ubuntu server—whether it’s a cloud VPS, a bare-metal machine, or a virtual lab instance—the first 30 minutes of configuration determine whether your server will be a secure, reliable workhorse or a liability waiting to be exploited. I’ve provisioned hundreds of Linux servers across AWS, on-premises data centers, and hybrid environments. The difference between a well-hardened server and a neglected one often comes down to a handful of commands executed in the right order.

This article is not just a command dump. It’s the exact initial server setup and hardening workflow I follow on every Ubuntu server I deploy. Each command is explained, each decision is justified, and every step reflects real-world experience from years of managing production Linux infrastructures. Whether you’re preparing for a Kubernetes certification lab, deploying a web application, or building a home lab, this guide will give you a battle-tested foundation.

Ubuntu Initial Server Setup and Hardening: A Complete Guide
Ubuntu Initial Server Setup and Hardening: A Complete Guide

Prerequisites and Assumptions

Before we begin, ensure you have:

  • A fresh Ubuntu Server installation (22.04 LTS or 24.04 LTS recommended)
  • SSH access with a user that has sudo privileges
  • A stable internet connection
  • Basic familiarity with the Linux command line

I’ll be using your_server_ip as a placeholder for your actual server IP address. Replace it accordingly.

You can watch following video tutorial to install Ubuntu Server in VirtualBox.

YouTube player

Step 1: Update and Upgrade the System

The very first thing you should do on any new Linux server is update the package lists and upgrade installed packages. This ensures you’re working with the latest security patches.

Update the package index from all configured repositories:

sudo apt update

The apt update command refreshes the local package index. It doesn’t install anything; it simply fetches the latest metadata from the repositories listed in /etc/apt/sources.list and /etc/apt/sources.list.d/.

Upgrade all installed packages to their latest versions:

sudo apt upgrade -y

The -y flag automatically answers “yes” to prompts. The upgrade command installs newer versions of packages that are already installed. It will not remove or install new packages—use full-upgrade if you need that behavior.

Remove unnecessary packages and clean up:

sudo apt autoremove -y && sudo apt autoclean

autoremove removes packages that were automatically installed to satisfy dependencies but are no longer needed. autoclean clears the local repository of retrieved package files that can no longer be downloaded.

Advice from experience: On production servers, I always run apt update && apt upgrade -y as a single chain in my initial provisioning scripts. However, I recommend testing upgrades in a staging environment first if you’re managing a fleet. A kernel upgrade can require a reboot, so plan accordingly.


Step 2: Create a Non-Root User with Sudo Privileges

Working as root directly is a security anti-pattern. Create a dedicated user account for daily operations.

Create a new user (replace deploy with your preferred username):

sudo adduser deploy

The adduser command is a friendlier front-end to useradd. It creates the user, sets up a home directory, and prompts for a password and optional user details.

Add the user to the sudo group:

sudo usermod -aG sudo deploy

The -aG flag appends the user to the supplementary group sudo without removing them from other groups. Members of the sudo group can execute commands with elevated privileges using sudo.

Verify the user’s group membership:

groups deploy

This outputs all groups the user belongs to. You should see sudo in the list.

Advice from experience: Never use usermod -G without the -a flag unless you intend to replace all supplementary groups. I’ve seen junior admins accidentally remove users from critical groups this way. Always use -aG.


Step 3: Configure SSH Key-Based Authentication

Password-based SSH authentication is vulnerable to brute-force attacks. Key-based authentication is significantly more secure.

On your local machine, generate an SSH key pair if you don’t already have one:

ssh-keygen -t ed25519 -C "deploy@your-server"

The -t ed25519 specifies the Ed25519 signature algorithm, which is faster and more secure than RSA. The -C flag adds a comment (typically your email or identifier).

Copy your public key to the server:

ssh-copy-id deploy@your_server_ip

This command appends your public key to ~/.ssh/authorized_keys on the remote server. It handles permissions automatically.

Test key-based login before disabling password authentication:

ssh deploy@your_server_ip

You should log in without being prompted for a password (unless your key is passphrase-protected).

Advice from experience: Always test your key-based login in a separate terminal session before disabling password authentication. Locking yourself out of a remote server is a painful lesson that only needs to be learned once.

Read Also: Ultimate Fail2ban Configuration Guide


Step 4: Harden SSH Configuration

Now that key-based authentication works, disable password authentication and root login.

Edit the SSH daemon configuration:

sudo nano /etc/ssh/sshd_config

Make the following changes:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
UsePAM yes
X11Forwarding no
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
  • PermitRootLogin no — Prevents direct root login via SSH.
  • PasswordAuthentication no — Disables password-based authentication entirely.
  • PubkeyAuthentication yes — Enables key-based authentication.
  • ChallengeResponseAuthentication no — Disables challenge-response authentication.
  • X11Forwarding no — Disables X11 forwarding, which is rarely needed on servers.
  • MaxAuthTries 3 — Limits authentication attempts to 3 before disconnecting.
  • ClientAliveInterval 300 — Sends a keepalive message every 300 seconds.
  • ClientAliveCountMax 2 — Disconnects after 2 unanswered keepalive messages.

Test the SSH configuration for syntax errors:

sudo sshd -t

The -t flag runs a configuration syntax test. If there are no errors, you’ll see no output.

Restart the SSH service:

sudo systemctl restart ssh

Verify the SSH service is running:

sudo systemctl status ssh

Advice from experience: On Ubuntu 22.04 and later, the SSH service is called ssh, not sshd. Also, keep your existing SSH session open until you’ve confirmed you can log in with a new session. If something goes wrong, you can revert changes from the open session.


Step 5: Configure a Firewall with UFW

Ubuntu ships with UFW (Uncomplicated Firewall), a user-friendly front-end to iptables.

Allow SSH connections:

sudo ufw allow OpenSSH

This creates a rule allowing traffic on port 22 (or your custom SSH port).

Enable the firewall:

sudo ufw enable

You’ll be prompted to confirm. Type y and press Enter.

Check the firewall status:

sudo ufw status verbose

This displays all active rules with verbose output, including default policies.

Advice from experience: Always allow SSH before enabling UFW. If you enable UFW without allowing SSH, you’ll lock yourself out of remote servers. On cloud instances, you may also need to configure security groups or network ACLs at the provider level.


Step 6: Set Up Automatic Security Updates

Keeping your server patched is critical. Ubuntu’s unattended-upgrades package automates security updates.

Install unattended-upgrades:

sudo apt install unattended-upgrades -y

Enable automatic updates:

sudo dpkg-reconfigure --priority=low unattended-upgrades

This opens a configuration dialog. Select “Yes” to enable automatic updates.

Verify the configuration:

sudo cat /etc/apt/apt.conf.d/20auto-upgrades

You should see:

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

Advice from experience: Automatic security updates are a double-edged sword. They keep you patched but can occasionally break services. For production servers, I recommend configuring email notifications for update failures and testing critical updates in staging first.


Step 7: Configure Time Synchronization

Accurate time is essential for logs, certificates, and distributed systems.

Check the current time synchronization status:

timedatectl status

This shows whether NTP synchronization is active, the current timezone, and the system clock.

Set the timezone (replace Asia/Karachi with your timezone):

sudo timedatectl set-timezone Asia/Karachi

Enable NTP synchronization:

sudo timedatectl set-ntp true

Advice from experience: On cloud instances, the provider’s metadata service often handles time synchronization. However, explicitly enabling systemd-timesyncd or chrony ensures consistency across reboots and migrations.

Watch Now:

YouTube player

Step 8: Install Essential Packages

A minimal Ubuntu server installation lacks many tools you’ll need for administration and troubleshooting.

Install a curated set of essential packages:

sudo apt install -y curl wget git vim htop net-tools ufw fail2ban apt-transport-https ca-certificates gnupg lsb-release software-properties-common
  • curl and wget — Download files from the internet.
  • git — Version control.
  • vim — Text editor.
  • htop — Interactive process viewer.
  • net-tools — Legacy networking tools like ifconfig and netstat.
  • ufw — Firewall (already configured).
  • fail2ban — Intrusion prevention.
  • apt-transport-https — Allows apt to fetch packages over HTTPS.
  • ca-certificates — SSL certificate authorities.
  • gnupg — GNU Privacy Guard for package signing.
  • lsb-release — Linux Standard Base release information.
  • software-properties-common — Manage software repositories.

Advice from experience: Don’t install packages you don’t need. Every package is a potential attack surface. I keep a minimal baseline and install additional tools only when required.


Step 9: Configure Fail2Ban

Fail2Ban monitors log files and bans IPs that show malicious signs, such as repeated failed login attempts.

Create a local configuration file:

sudo nano /etc/fail2ban/jail.local

Add the following content:

[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
backend = systemd

[sshd]
enabled = true 
port = ssh 
filter = sshd 
logpath = /var/log/auth.log 
maxretry = 3
  • bantime — Duration of the ban in seconds (1 hour).
  • findtime — Time window for counting failures (10 minutes).
  • maxretry — Number of failures before banning (5 for default, 3 for SSH).
  • backend = systemd — Uses systemd journal for log parsing.

Restart Fail2Ban:

sudo systemctl restart fail2ban

Check the status:

sudo fail2ban-client status sshd

Advice from experience: Fail2Ban is not a substitute for key-based authentication, but it’s an excellent additional layer. I’ve seen it reduce SSH brute-force attempts by over 90% on public-facing servers.


Step 10: Set Up Swap Space (If Not Present)

Swap space provides a safety net when physical memory is exhausted. Many cloud instances come without swap.

Check current swap:

sudo swapon --show
free -h

Create a 2GB swap file (adjust size as needed):

sudo fallocate -l 2G /swapfile

Set correct permissions:

sudo chmod 600 /swapfile

Format the file as swap:

sudo mkswap /swapfile

Enable the swap file:

sudo swapon /swapfile

Make the swap permanent by adding it to /etc/fstab:

echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

Verify:

sudo swapon --show

Advice from experience: On servers with sufficient RAM (16GB+), swap is less critical but still useful for handling memory spikes. On small VPS instances (1-2GB RAM), swap is essential. Adjust vm.swappiness to 10 for servers to reduce swap usage unless absolutely necessary.

Read Also: How to change Swappiness in Linux


Step 11: Configure Kernel Hardening Parameters

Sysctl parameters control kernel behavior. Several settings improve security.

Create a hardening configuration file:

sudo nano /etc/sysctl.d/99-hardening.conf

Add the following:

# IP Spoofing protection
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Ignore send redirects
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0

# Disable source packet routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# Log Martians
net.ipv4.conf.all.log_martians = 1

# Ignore ICMP ping requests
net.ipv4.icmp_echo_ignore_all = 1

# Ignore Directed pings
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Enable TCP SYN Cookie Protection
net.ipv4.tcp_syncookies = 1

# Disable IPv6 (if not needed)
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1

Apply the settings:

sudo sysctl -p /etc/sysctl.d/99-hardening.conf

Advice from experience: Disabling IPv6 can cause issues if your applications rely on it. Test thoroughly before applying in production. Also, disabling ICMP ping requests can interfere with monitoring tools. Adjust based on your environment.


Step 12: Set Up Logging and Monitoring

Centralized logging and basic monitoring help you detect issues before they become outages.

Install and configure rsyslog (usually pre-installed):

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

Install logwatch for daily log summaries:

sudo apt install logwatch -y

Configure logwatch to email daily reports:

sudo nano /etc/cron.daily/00logwatch

Add:

/usr/sbin/logwatch --output mail --mailto your-email@example.com --detail high

Advice from experience: For production servers, I recommend shipping logs to a centralized system like Elasticsearch, Loki, or a SIEM. Local logs are lost if the server is compromised or fails.


Step 13: Configure a Basic Backup Strategy

Backups are non-negotiable. Even a simple rsync or tar script is better than nothing.

Create a backup directory:

sudo mkdir -p /backups

Create a simple backup script:

sudo nano /usr/local/bin/backup.sh

Add:

#!/bin/bash
BACKUP_DIR="/backups"
DATE=$(date +%Y-%m-%d_%H-%M-%S)
tar -czf "$BACKUP_DIR/etc-backup-$DATE.tar.gz" /etc /home /var/www 2>/dev/null
find "$BACKUP_DIR" -type f -mtime +7 -delete

Make it executable:

sudo chmod +x /usr/local/bin/backup.sh

Schedule it with cron:

sudo crontab -e

Add:

0 2 * * * /usr/local/bin/backup.sh

Advice from experience: This is a minimal backup strategy. For production, use dedicated backup solutions like Borg, Restic, or cloud-native snapshots. Always test your restores.

Read Also: How to Master cron Command in Linux


Step 14: Reboot and Verify

After all changes, reboot the server to ensure everything persists.

Reboot:

sudo reboot

After reboot, verify:

# Check uptime
uptime

# Check firewall
sudo ufw status

# Check fail2ban
sudo fail2ban-client status sshd

# Check swap
sudo swapon --show

# Check time sync
timedatectl status

# Check SSH
sudo systemctl status ssh

Bonus Material: Ansible Playbook for Server Hardening


Frequently Asked Questions

1. How do I recover access if I lock myself out via SSH?

If you still have an active SSH session, revert your changes immediately. If not, use your cloud provider’s console access (VNC or serial console) to log in and fix the SSH configuration. Always keep a root password or console access as a fallback.

2. Should I disable IPv6 on all servers?

Not necessarily. Disable IPv6 only if you’re certain your applications and network don’t use it. Many modern applications and cloud services rely on IPv6. Test in a staging environment first.

3. Is UFW sufficient for server hardening?

UFW is a good starting point, but it’s not a complete security solution. Combine it with fail2ban, key-based authentication, regular updates, and intrusion detection systems like AIDE or OSSEC for comprehensive protection.

4. How often should I update my server?

Security updates should be applied as soon as possible. Configure unattended-upgrades for automatic security patches and schedule regular maintenance windows for kernel and major package updates.

5. What’s the best way to manage multiple servers?

Use configuration management tools like Ansible, Puppet, or Chef. For smaller deployments, shell scripts and cron jobs work fine. For cloud environments, consider infrastructure-as-code tools like Terraform and cloud-init.


Conclusion

Setting up and hardening a Linux server is a skill that improves with practice. The steps outlined here—updating packages, creating users, configuring SSH, enabling firewalls, setting up fail2ban, and tuning kernel parameters—form a solid foundation for any Ubuntu server deployment.

As a RHCE, CKA, ISC2 CC, and AWS Engineer with over 15 years of experience, I can tell you that the time invested in initial server setup pays dividends in reliability, security, and peace of mind. Don’t skip steps. Don’t take shortcuts. And always test your changes before applying them to production.

For further reading, consult the

Now go build something secure.


If you are new to Linux and want a solid starting point, I highly recommend Ubuntu Linux Server Basics by Cody Ray Miller. This beginner-friendly guide walks you step by step through setting up and managing Ubuntu servers, making it perfect for students, sysadmins, and developers who want practical hands-on knowledge. With clear explanations and real-world examples, this resource can fast-track your Linux learning journey and save you countless hours of trial and error.

Disclaimer: This post contains affiliate links. If you purchase through these links, I may earn a small commission at no extra cost to you.



About the Author

Ahmer M
Ahmer M

Ahmer M

Sr. DevOps Engineer | CKA | RHCE | Freelancer | Blogger
I am a technology enthusiast with over 15 years of experience designing and scaling Linux, DevOps, and cloud environments. My expertise lies in container orchestration and automation, bridging the gap between development and operations to build resilient systems.


Leave a Reply