Part of the DigitalOcean deploy series. Tested on Ubuntu 24.04, June 2026.

This is the step most guides skip, and the one that keeps your server out of trouble. By the end the box has a normal working user, root login is off, only the ports you need are open, brute force attempts get banned, there is swap to fall back on, and security updates install themselves.

Start as root, which is where step one left you.

Update the system

apt update && apt upgrade -y

If it asks about an updated sshd_config during the upgrade, choose to keep the locally installed version. We are about to add our own SSH settings and do not want them overwritten.

Create the deploy user

Working as root all day invites accidents. Make a normal user that can use sudo. I call mine deploy.

adduser deploy
usermod -aG sudo deploy

Set a strong password when prompted. That password is only ever used for sudo, never for SSH login.

Give the user your SSH key, with the right ownership and permissions.

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

Now open a second terminal and confirm you can log in as deploy before you change anything else.

ssh deploy@your_droplet_ip
sudo whoami      # should print: root

If both work, carry on. If not, fix it now while your root session is still open.

Lock down SSH

Rather than editing the main config, drop your overrides in a dedicated file. It is tidier and survives package updates.

sudo nano /etc/ssh/sshd_config.d/99-hardening.conf
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
KbdInteractiveAuthentication no

Validate the config before you apply it, then reload.

sudo sshd -t
sudo systemctl reload ssh

Your current session stays alive. New root logins are now refused. Test a fresh deploy login in another terminal before closing this one.

Turn on the firewall

Allow SSH first, so you do not lock yourself out, then HTTP and HTTPS, then enable.

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status

Ban brute force with fail2ban

sudo apt install fail2ban -y

The default config already protects SSH, so there is nothing else to set.

Automatic security updates

sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades

Choose Yes. Security patches now land nightly on their own.

Add swap

A 2 GB droplet benefits from a swap file as a safety net when memory spikes, for example during a Composer install or an asset build.

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

SSDs prefer to avoid heavy swapping, so lower the swappiness.

sudo sysctl vm.swappiness=10
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
free -h

Confirm the clock

Keep the server on UTC, which is the safest base for any Laravel app, and check time sync is on.

timedatectl

It should show the timezone as UTC and NTP as active.

That is the hard part done. The server is now a much smaller target, with a working user, a firewall, swap, and updates on autopilot. From here on, never log in as root again. Next we install the stack that runs your app.