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
Table of Contents
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.

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.
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 updateThe 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 -yThe -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 autocleanautoremove 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 deployThe 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 deployThe -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 deployThis 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_ipThis 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_ipYou 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_configMake the following changes:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
UsePAM yes
X11Forwarding no
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2PermitRootLogin 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 -tThe -t flag runs a configuration syntax test. If there are no errors, you’ll see no output.
Restart the SSH service:
sudo systemctl restart sshVerify the SSH service is running:
sudo systemctl status sshAdvice 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 OpenSSHThis creates a rule allowing traffic on port 22 (or your custom SSH port).
Enable the firewall:
sudo ufw enableYou’ll be prompted to confirm. Type y and press Enter.
Check the firewall status:
sudo ufw status verboseThis 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 -yEnable automatic updates:
sudo dpkg-reconfigure --priority=low unattended-upgradesThis opens a configuration dialog. Select “Yes” to enable automatic updates.
Verify the configuration:
sudo cat /etc/apt/apt.conf.d/20auto-upgradesYou 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 statusThis 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/KarachiEnable NTP synchronization:
sudo timedatectl set-ntp trueAdvice 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:
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-commoncurlandwget— Download files from the internet.git— Version control.vim— Text editor.htop— Interactive process viewer.net-tools— Legacy networking tools likeifconfigandnetstat.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.localAdd 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 = 3bantime— 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 fail2banCheck the status:
sudo fail2ban-client status sshdAdvice 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 -hCreate a 2GB swap file (adjust size as needed):
sudo fallocate -l 2G /swapfileSet correct permissions:
sudo chmod 600 /swapfileFormat the file as swap:
sudo mkswap /swapfileEnable the swap file:
sudo swapon /swapfileMake the swap permanent by adding it to /etc/fstab:
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstabVerify:
sudo swapon --showAdvice 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.confAdd 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 = 1Apply the settings:
sudo sysctl -p /etc/sysctl.d/99-hardening.confAdvice 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 rsyslogInstall logwatch for daily log summaries:
sudo apt install logwatch -yConfigure logwatch to email daily reports:
sudo nano /etc/cron.daily/00logwatchAdd:
/usr/sbin/logwatch --output mail --mailto your-email@example.com --detail highAdvice 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 /backupsCreate a simple backup script:
sudo nano /usr/local/bin/backup.shAdd:
#!/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 -deleteMake it executable:
sudo chmod +x /usr/local/bin/backup.shSchedule it with cron:
sudo crontab -eAdd:
0 2 * * * /usr/local/bin/backup.shAdvice 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 rebootAfter 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 sshBonus 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
- Ubuntu Documentation for Ubuntu Server hardening
- RHEL Documentation for Red Hat Linux hardening
- MongoDB Documentation for database-specific hardening
Now go build something secure.
Recommended Course
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.









Leave a Reply
You must be logged in to post a comment.