The moment you spin up a fresh Ubuntu server, it’s under attack. Within minutes, automated scanners probe your infrastructure for default credentials, unpatched services, and misconfigurations. If you’re tasked with deploying production servers—whether on-premises, in AWS, Azure, or any other cloud platform—you need to harden your Ubuntu server properly from day one. This isn’t optional security theater; it’s foundational operational hygiene that separates mature infrastructure from compromised systems.
In 2026, hardening has evolved. The threat landscape has expanded beyond password brute-forcing and unpatched vulnerabilities to include supply chain attacks, containerized workloads, and increasingly sophisticated adversaries. The good news? Ubuntu’s security tooling has matured alongside these threats. This guide walks you through the practical, battle-tested steps to harden an Ubuntu server in production environments—from kernel hardening to application-level security controls.
The Current Ubuntu Security Landscape
Before diving into hardening steps, understand what you’re protecting against. Ubuntu 22.04 LTS (currently the enterprise standard) and Ubuntu 24.04 LTS (the latest stable release) ship with different security baselines than their predecessors. AppArmor is enabled by default (not SELinux), and systemd handles most security-critical services.
The NIST Cybersecurity Framework and CIS Benchmarks provide standardized hardening guidance, but they often feel disconnected from real operational needs. Your hardening strategy should balance:
- Security posture — reducing attack surface
- Operational overhead — automation to avoid manual configuration drift
- Performance impact — some hardening incurs measurable cost
- Compliance requirements — whether you need PCI-DSS, HIPAA, SOC 2, etc.
This guide focuses on practical, immediately deployable hardening that most infrastructure teams should implement regardless of compliance mandates.
Step 1: Patch Everything, Immediately
This sounds obvious, but I’ll say it anyway: update your system before any other hardening step.
sudo apt update
sudo apt upgrade -y
sudo apt autoremove
Check what kernel version you’re running and whether a restart is needed:
uname -r
sudo needrestart -r a
The -r a flag tells needrestart to automatically handle restarts. In 2026, Ubuntu’s automatic security updates are reliable enough for most production workloads, but verify this in your environment.
Configure unattended security updates so your systems stay patched between your maintenance windows:
sudo apt install unattended-upgrades apt-listchanges
sudo dpkg-reconfigure unattended-upgrades
Edit /etc/apt/apt.conf.d/50unattended-upgrades to customize behavior:
// Enable automatic restarts if required
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "02:00";
// Include security updates from Ubuntu security advisories
Unattended-Upgrade::Package-Blacklist {};
For critical production systems, consider staged rollouts: harden a canary instance, validate patches, then apply to your fleet. Ansible or Terraform can automate this at scale.
Step 2: Disable Unnecessary Services and Network Interfaces
Every running service expands your attack surface. Audit what’s actually running:
sudo systemctl list-units --type=service --state=running
sudo ss -tlnp
The ss command shows all listening ports and their associated processes. Disable anything you don’t need. Common candidates for removal in production:
- Avahi (mDNS discovery):
sudo systemctl disable avahi-daemon - CUPS (printing service):
sudo systemctl disable cups - Bluetooth:
sudo systemctl disable bluetooth - IMAP/POP services: Only needed if you’re running mail servers
Don’t just disable—actually remove if you’re certain you won’t need it:
sudo apt remove avahi-daemon cups bluetooth -y
If you’re running a server that doesn’t need IPv6, consider disabling it (though this is becoming less necessary as IPv6 becomes standard). Most modern deployments benefit from keeping IPv6 enabled. Instead, focus on what’s actually listening:
# Verify nothing is unexpectedly listening on port 22 (SSH) or other services
sudo netstat -tlnp | grep LISTEN
Step 3: SSH Hardening
SSH is your primary attack vector. Secure it ruthlessly.
Generate a Strong Host Key (If Not Already Present)
# Check existing keys
sudo ls -la /etc/ssh/ssh_host_*
Modern Ubuntu generates strong Ed25519 keys during installation. If you’re retrofitting, you can generate additional strong keys:
sudo ssh-keygen -A
Harden SSH Configuration
Edit /etc/ssh/sshd_config:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
sudo nano /etc/ssh/sshd_config
Apply these critical changes:
# Disable password authentication entirely
PasswordAuthentication no
PubkeyAuthentication yes
# Disable root login
PermitRootLogin no
# Disable empty passwords
PermitEmptyPasswords no
# Restrict to protocol 2
Protocol 2
# Use only strong key exchange algorithms
KexAlgorithms curve25519-sha256,[email protected]
Ciphers [email protected],[email protected]
MACs [email protected],[email protected]
# Limit authentication attempts
MaxAuthTries 3
# Disable X11 forwarding unless you specifically need it
X11Forwarding no
# Set client alive interval to disconnect hung sessions
ClientAliveInterval 300
ClientAliveCountMax 2
# Reduce login grace time
LoginGraceTime 30
# Limit concurrent connections
MaxStartups 10:30:60
# Disable user environment override
PermitUserEnvironment no
# Only allow specific users if possible
AllowUsers deploy ubuntu
Validate the configuration:
sudo sshd -t
Only restart SSH after confirming the syntax is correct:
sudo systemctl restart ssh
Important: Keep your original terminal session open until you verify SSH login works. Never disconnect and risk locking yourself out.
Implement Public Key Authentication Only
On your workstation (not the server), generate a strong keypair if you haven’t already:
ssh-keygen -t ed25519 -C "[email protected]" -f ~/.ssh/id_server
Copy the public key to the server:
ssh-copy-id -i ~/.ssh/id_server.pub ubuntu@your-server-ip
Configure your local SSH config (~/.ssh/config) for convenience:
Host production-server
HostName 192.0.2.100
User ubuntu
IdentityFile ~/.ssh/id_server
AddKeysToAgent yes
IdentitiesOnly yes
Step 4: Configure the Firewall
Ubuntu includes UFW (Uncomplicated Firewall), a wrapper around iptables. Enable it:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
Check your rules:
sudo ufw status verbose
If you’re running application-specific services, add them explicitly:
sudo ufw allow 3000/tcp # Node.js app
sudo ufw allow 5432/tcp # PostgreSQL (if accessible externally—usually not recommended)
Never block outbound SSH entirely—you’ll need to manage the system somehow. Ensure at least port 22 is accessible from your trusted networks.
For cloud deployments (AWS, Azure, GCP), remember that security groups or network ACLs provide an additional layer before reaching UFW. UFW is still valuable for defense-in-depth.
Step 5: Enable and Configure SELinux or Harden AppArmor
Ubuntu ships with AppArmor by default (not SELinux). AppArmor is effective and less verbose than SELinux.
Check that AppArmor is running:
sudo systemctl status apparmor
sudo aa-status
AppArmor profiles ship for common services. Ensure they’re in enforcing mode (not complain mode):
sudo aa-enforce /etc/apparmor.d/usr.sbin.sshd
sudo aa-enforce /etc/apparmor.d/usr.sbin.mysqld # if running MySQL
If you want to develop custom AppArmor profiles for your applications, the process is similar to SELinux but simpler. For most infrastructure, the default profiles are sufficient.
Step 6: Harden the Kernel
Modern Linux kernels expose dangerous settings by default. Adjust /etc/sysctl.conf to close these gaps:
sudo nano /etc/sysctl.conf
Add or modify these kernel parameters:
# IP forwarding (disable if not needed)
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
# Disable source packet routing
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
# Enable SYN cookies for SYN flood protection
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 4096
# Log suspicious packets
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
# Disable ICMP redirects
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
# Restrict kernel module loading
kernel.modules_disabled = 1
# Restrict access to kernel logs
kernel.dmesg_restrict = 1
# Restrict ptrace for additional process isolation
kernel.yama.ptrace_scope = 2
# Restrict access to kptr in /proc
kernel.kptr_restrict = 2
# Restrict access to kernel pointers
kernel.perf_event_paranoid = 3
# Restrict unprivileged user namespaces (breaks Docker if enabled—choose carefully)
# kernel.unprivileged_userns_clone = 0
# Restrict unprivileged eBPF
kernel.unprivileged_bpf_disabled = 1
kernel.bpf_stats_enabled = 0
Apply the changes:
sudo sysctl -p
Verify they took effect:
sudo sysctl -a | grep kernel.dmesg_restrict
Step 7: File System Hardening
Mount filesystems with restrictive permissions:
# Check current mounts
mount | grep -E 'ext4|xfs|btrfs'
Edit /etc/fstab to add security options:
# Find your root and other partitions
sudo blkid
# Edit /etc/fstab
sudo nano /etc/fstab
Add or modify entries with these flags:
# For /tmp (create if not present, or modify existing entry)
tmpfs /tmp tmpfs defaults,nosuid,nodev,noexec,relatime,mode=1777 0 0
# For /var/tmp
tmpfs /var/tmp tmpfs defaults,nosuid,nodev,noexec,relatime,mode=1777 0 0
# For /dev/shm
tmpfs /dev/shm tmpfs defaults,nosuid,nodev,noexec,relatime,mode=1777 0 0
Remount to apply:
sudo mount -o remount,nosuid,nodev,noexec /tmp
sudo mount -o remount,nosuid,nodev,noexec /var/tmp
sudo mount -o remount,nosuid,nodev,noexec /dev/shm
These flags prevent execution of binaries from temporary filesystems—a common attack vector for deploying malware.
Step 8: User and Permission Hardening
Enforce Strong Password Policies
Install and configure PAM (Pluggable Authentication Modules):
sudo apt install libpam-pwquality
sudo nano /etc/security/pwquality.conf
Configure password requirements:
minlen = 14
dcredit = -1
ucredit = -1
ocredit = -1
lcredit = -1
maxrepeat = 3
difok = 4
usercheck = 1
enforce_for_root
Disable Unnecessary User Accounts
List all users:
cut -d: -f1 /etc/passwd
Disable accounts you don’t need:
sudo usermod -L username # Lock the account
sudo usermod -s /usr/sbin/nologin username # Disable login shell
Configure sudo Properly
Edit /etc/sudoers using visudo (never edit directly):
sudo visudo
Ensure users requiring elevated privileges are in the sudo group, and consider adding this line to log all sudo commands:
Defaults use_pty
Defaults logfile="/var/log/sudo.log"
Step 9: Implement Audit Logging
The auditd daemon captures detailed system events. Install it:
sudo apt install auditd audispd-plugins
sudo systemctl enable auditd
sudo systemctl start auditd
Add audit rules for critical files:
sudo nano /etc/audit/rules.d/audit.rules
Example audit rules:
# Monitor SSH configuration changes
-w /etc/ssh/sshd_config -p wa -k sshd_config_changes
# Monitor system calls
-a always,exit -F arch=b64 -S execve -F uid=0 -k root_commands
# Monitor sudo usage
-w /etc/sudoers -p wa -k sudoers_changes
-w /etc/sudoers.d/ -p wa -k sudoers_changes
Load rules and verify:
sudo augenrules --load
sudo auditctl -l
Query audit logs:
sudo ausearch -k sshd_config_changes
For centralized logging, forward audit events to a SIEM or logging platform.
Step 10: Enable Automatic Security Updates and Monitoring
Configure unattended security updates (covered earlier), but add monitoring. Install and enable apt-listchanges to see what’s being updated:
sudo apt install apt-listchanges
Set up systemd timer notifications or integrate with your monitoring platform (Datadog and similar services can track patch compliance across your infrastructure).
Create a simple monitoring script to verify hardening is maintained:
#!/bin/bash
# verify_hardening.sh
echo "Checking SSH configuration..."
sudo sshd -T | grep -E "PasswordAuthentication|PermitRootLogin|X11Forwarding"
echo "Checking firewall status..."
sudo ufw status | grep Status
echo "Checking for listening services..."
sudo ss -tlnp | wc -l
echo "Checking for unpatched packages..."
sudo apt list --upgradable 2>/dev/null | wc -l
Run this script regularly (e.g., via cron) and alert if settings drift.
Hardening Automation with Infrastructure as Code
Manual hardening doesn’t scale. Automate using Ansible, Terraform, or cloud-init:
Cloud-Init Example
If deploying on AWS or Azure, use user data to automate initial hardening:
#!/bin/bash
apt-get update
apt-get install -y unattended-upgrades auditd
# Configure SSH
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
systemctl restart ssh
# Enable firewall
ufw default deny incoming
ufw allow ssh
ufw enable
Ansible Playbook Snippet
---
- name: Harden Ubuntu Server
hosts: all
become: yes
tasks:
- name: Update all packages
apt:
update_cache: yes
upgrade: dist
- name: Install security tools
apt:
name:
- unattended-upgrades
- auditd
- fail2ban
state: present
- name: Disable password SSH authentication
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PasswordAuthentication'
line: 'PasswordAuthentication no'
validate: /usr/sbin/sshd -T -f %s
notify: restart ssh
- name: Enable UFW
ufw:
state: enabled
default: deny
direction: incoming
handlers:
- name: restart ssh
systemd:
name: ssh
state: restarted
This approach ensures consistent hardening across your infrastructure and makes compliance audits straightforward.
Additional Considerations for 2026
Container Security
If running Docker or Kubernetes, harden the container runtime and host separately. Even hardened Ubuntu hosts need additional controls at the container level (seccomp profiles, network policies, image scanning).
Secrets Management
Never embed credentials in configurations. Use 1Password Teams or similar tools to manage SSH keys, API tokens, and database passwords across your team.
Regular Security Audits
Hardening is not a one-time task. Schedule regular:
- Vulnerability scans using tools like Trivy or Qualys
- Configuration drift checks to ensure hardening persists
- Log reviews to detect suspicious activity
- Penetration testing in non-production environments
Compliance Documentation
If you’re subject to compliance requirements (PCI-DSS, HIPAA, SOC 2), document which hardening steps address specific controls. The CIS Benchmarks for Ubuntu provide a mapping to common compliance frameworks.
Conclusion and Next Steps
Hardening an Ubuntu server in 2026 requires balancing comprehensive security with operational efficiency. Start with the fundamentals—patching, SSH hardening, and firewall configuration—then layer in additional controls based on your threat model and compliance needs.
Your immediate action items:
- Patch your system and enable automatic security updates
- Harden SSH by disabling password authentication and restricting root login
- Enable the firewall and explicitly allow only necessary ports
- Configure kernel hardening via sysctl
- Automate everything with Ansible, Terraform, or cloud-init
If you’re managing multiple servers, invest time in infrastructure-as-code tooling now. The hours spent automating hardening will return exponentially as your infrastructure scales.
Remember: hardening is a process, not a product. The threat landscape evolves constantly, and your security posture must evolve alongside it. Set calendar reminders to review and update your hardening standards quarterly. Subscribe to Ubuntu security advisories and stay engaged with the security community.
The systems you harden today may protect your organization for years. Make them count.