Initial Server Access
After provisioning your Ubuntu 24.04 server (AWS EC2, DigitalOcean, Hetzner, etc.), connect via SSH:
ssh root@your-server-ip
First thing — update everything:
apt update && apt upgrade -y
Set the timezone:
timedatectl set-timezone UTC
Using UTC avoids confusion when reading logs across different services and team members in different time zones.
Step 1 — Create a Non-Root User
Never run services as root. Create a dedicated user:
# Create user with home directory
adduser deploy
# Add to sudo group
usermod -aG sudo deploy
# Copy SSH keys to new user
mkdir -p /home/deploy/.ssh
cp /root/.ssh/authorized_keys /home/deploy/.ssh/
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys
Test the new user login before proceeding:
# From your local machine
ssh deploy@your-server-ip
sudo whoami # should output: root
Step 2 — Harden SSH
Edit the SSH configuration to lock down access:
sudo nano /etc/ssh/sshd_config
Change these settings:
# Disable root login
PermitRootLogin no
# Disable password authentication (key-only)
PasswordAuthentication no
# Limit to your user
AllowUsers deploy
# Change default port (optional but reduces noise)
Port 2222
# Disconnect idle sessions after 5 minutes
ClientAliveInterval 300
ClientAliveCountMax 0
Restart SSH:
sudo systemctl restart sshd
Important: Keep your current session open. Open a new terminal and test you can still connect before closing the old session:
ssh -p 2222 deploy@your-server-ip
Step 3 — Configure the Firewall (UFW)
Ubuntu's built-in firewall is simple and effective:
# Allow SSH (use your custom port if changed)
sudo ufw allow 2222/tcp
# Allow HTTP and HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Enable the firewall
sudo ufw enable
# Check status
sudo ufw status verbose
Output should show:
Status: active
To Action From
-- ------ ----
2222/tcp ALLOW Anywhere
80/tcp ALLOW Anywhere
443/tcp ALLOW Anywhere
Everything else is blocked by default. If you need additional ports later (e.g., 8000 for an API), add them explicitly.
Step 4 — Enable Automatic Security Updates
Security patches should be applied automatically:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
Select "Yes" when prompted. Verify it's working:
cat /etc/apt/apt.conf.d/20auto-upgrades
Should contain:
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
This automatically installs security updates daily. For more control, edit /etc/apt/apt.conf.d/50unattended-upgrades to configure email notifications and automatic reboots.
Step 5 — Install Fail2Ban
Fail2Ban blocks IP addresses that repeatedly fail authentication:
sudo apt install fail2ban
Create a local configuration:
sudo nano /etc/fail2ban/jail.local
Add:
[sshd]
enabled = true
port = 2222
filter = sshd
maxretry = 3
bantime = 3600
findtime = 600
This bans an IP for 1 hour after 3 failed SSH attempts within 10 minutes.
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# Check banned IPs
sudo fail2ban-client status sshd
Step 6 — Set Up Basic Monitoring
Install monitoring tools to catch problems early:
# System resource monitoring
sudo apt install htop iotop
# Disk usage alerts
sudo apt install logwatch
Create a simple disk space check script:
sudo nano /usr/local/bin/disk-check.sh
#!/bin/bash
USAGE=$(df / | tail -1 | awk '{print $5}' | sed 's/%//')
if [ $USAGE -gt 85 ]; then
echo "WARNING: Disk usage is ${USAGE}%" | logger -t disk-check
fi
sudo chmod +x /usr/local/bin/disk-check.sh
# Run every hour
(crontab -l 2>/dev/null; echo "0 * * * * /usr/local/bin/disk-check.sh") | crontab -
Your server is now production-ready: non-root user, key-only SSH, firewall, automatic updates, brute-force protection, and basic monitoring.