Ahnii!

This is part 1 of the Production Linux series. It covers the first steps after creating a new VPS.

This post walks you through provisioning a fresh DigitalOcean droplet — from your first root SSH connection to a locked-down baseline with a non-root deploy user, a UFW firewall, and automatic security patches. This covers Ubuntu 24.04 LTS only; the commands assume a clean droplet with no prior configuration.

Prerequisites

  • A DigitalOcean account (or any VPS provider running Ubuntu 24.04)
  • An SSH key pair on your local machine (~/.ssh/id_ed25519 and ~/.ssh/id_ed25519.pub)

Create the Droplet

The fastest path is doctl, the DigitalOcean CLI:

doctl compute droplet create my-server \
  --image ubuntu-24-04-x64 \
  --size s-1vcpu-1gb \
  --region tor1 \
  --ssh-keys $(doctl compute ssh-key list --format ID --no-header) \
  --wait

This creates a 1 vCPU / 1 GB droplet in Toronto using all SSH keys in your account. Swap tor1 for your nearest region (nyc3, sfo3, ams3, etc.). The --wait flag blocks until provisioning is complete, then prints the droplet’s public IP.

If you prefer the web console, create the droplet through the DigitalOcean dashboard and select your SSH key during setup. Either way, note the public IP before continuing.

First SSH Connection

Connect as root using your SSH key:

ssh root@your-server-ip

On first login you will see the Ubuntu welcome banner, MOTD, and a summary of pending updates. Cloud-init has already configured the server with PasswordAuthentication no and your SSH public key in /root/.ssh/authorized_keys, so password login is disabled out of the box.

Create a Deploy User

Running everything as root is a bad habit. Create a dedicated deploy user now:

adduser deployer
usermod -aG sudo deployer

adduser creates the home directory, prompts for a password, and sets up the default shell. usermod -aG sudo deployer grants the user passwordless-capable sudo access via the sudo group.

Copy root’s authorized keys to the new user so you can SSH in as deployer:

rsync --archive --chown=deployer:deployer /root/.ssh /home/deployer/

rsync copies the .ssh directory with correct ownership in one step — no manual chown or chmod needed.

Before closing your root session, open a second terminal and verify the new user works:

ssh deployer@your-server-ip
sudo whoami

You should see root returned by sudo whoami. Only close the root session after confirming this. Locking yourself out of a new server is a rite of passage you can skip.

Set Up UFW

UFW (Uncomplicated Firewall) is the standard iptables front end on Ubuntu:

ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
ufw status verbose

The first two lines set the baseline policy: block all inbound, allow all outbound. The three allow rules open SSH, HTTP, and HTTPS. Enabling UFW applies the rules immediately — your existing SSH session stays connected because UFW handles established connections gracefully.

ufw status verbose confirms the active rules. You should see Status: active at the top and the three ALLOW IN entries listed.

Enable Unattended Upgrades

Security patches should apply automatically. Install and configure the package:

apt update && apt install -y unattended-upgrades
dpkg-reconfigure --priority=low unattended-upgrades

When prompted, select Yes to enable automatic updates. This writes the configuration to /etc/apt/apt.conf.d/20auto-upgrades.

Verify the file looks correct:

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

You should see:

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

Both values set to "1" mean: refresh the package list daily and apply security upgrades daily.

Verify Your Baseline

At this point your server has a working security foundation:

LayerWhat’s in place
NetworkUFW default-deny inbound; ports 22, 80, 443 open
SSHKey-only authentication; password login disabled
UserNon-root deployer user with sudo access
PatchesAutomatic daily security updates via unattended-upgrades

If you want to automate this entire process with Ansible, see Manage DigitalOcean Infrastructure With Ansible.

Baamaapii