Dedicated server security priorities for the first 30 minutes
A newly delivered bare-metal server may have a public IPv4 address, default root credentials, and every network port reachable unless you change the configuration. The first objective is not application deployment: it is establishing a safe administration path without accidentally locking yourself out.
Complete these actions from a trusted workstation on a private network. Keep the provider console, IPMI, KVM-over-IP, or rescue-console access open until you have tested a second SSH session with the new account and SSH key. Console access is the recovery method if an SSH, firewall, or network configuration change fails.
Prerequisites
- A dedicated server running Ubuntu Server 24.04 LTS with root or sudo access.
- A workstation with OpenSSH installed. Linux and macOS include it; Windows users can use PowerShell or Windows Terminal.
- Your server IPv4 address and, if configured, IPv6 address.
- A static public IP address for your office or home connection if you plan to restrict SSH by source IP.
- At least 4 physical CPU cores, 16 GB RAM, and 480 GB SSD or NVMe storage for a small production dedicated-server workload. Security tooling itself uses little capacity, but production services, logs, snapshots, and backups do not.
Do not expose a database, Docker API, Redis, Elasticsearch, or control panel to the public internet during initial setup. Bind private services to 127.0.0.1, a private VLAN, or a VPN interface unless public access is explicitly required.
Production sizing after the security baseline
Security controls must leave enough room for system logs, package updates, backups, monitoring agents, and the services the server will host. The following sizing assumes Linux web applications, databases, game servers, self-hosted apps, or CI runners with encrypted remote backups.
A useful starting point is 4 cores, 16 GB RAM, and 480 GB SSD for a small public service; use more memory and mirrored NVMe storage as request volume and database activity rise.
| Public HTTP requests per second | CPU | RAM | Disk | Monthly bandwidth |
|---|---|---|---|---|
| Up to 50 requests/sec | 4 physical cores | 16 GB | 480 GB SSD or NVMe | 5 TB |
| 50-300 requests/sec | 8 physical cores | 32 GB | 960 GB NVMe | 10 TB |
| 300-1,000 requests/sec | 16 physical cores | 64 GB | 2 × 1.92 TB NVMe in RAID 1 | 20 TB |
For a single lightweight VPN, monitoring node, or low-traffic self-hosted application, a VPS with 2 vCPU, 4 GB RAM, and 80 GB NVMe can be more economical. Choose dedicated hardware for sustained database I/O, game-server CPU load, large CI/CD builds, high traffic, or workloads needing stronger hardware isolation.
Step 1: Record the current state and update the operating system
Log in using the credentials supplied at delivery. Confirm the distribution, active network listeners, disk layout, and current login history before changing anything.
ssh root@SERVER_IP
hostnamectl
uname -a
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS
ss -tulpn
last -a | head
apt update && apt -y full-upgrade
rebootReconnect after the reboot and confirm that the expected kernel is running:
ssh root@SERVER_IP
uname -r
apt autoremove -y
apt install -y sudo curl ca-certificates vim ufw fail2ban unattended-upgrades auditdReview unexpected listening ports. A fresh Ubuntu server commonly has only SSH on TCP port 22. If ss -tulpn shows services you did not install, identify their packages before removing them. Do not stop a remote-management agent supplied by your provider without confirming that console access remains available.
Step 2: Create a named administrator and install an SSH key
Daily administration should use a named account with sudo, not direct root login. On your workstation, create an Ed25519 key if you do not already have one. Protect the private key with a passphrase.
ssh-keygen -t ed25519 -a 100 -C "admin@workstation"
ssh-copy-id root@SERVER_IPThe second command copies your public key to root temporarily. On the server, create the administrator account and copy the authorized key to it:
adduser admin
usermod -aG sudo admin
install -d -m 700 -o admin -g admin /home/admin/.ssh
cp /root/.ssh/authorized_keys /home/admin/.ssh/authorized_keys
chown admin:admin /home/admin/.ssh/authorized_keys
chmod 600 /home/admin/.ssh/authorized_keysOpen a second terminal and test the new account before touching SSH settings:
ssh admin@SERVER_IP
sudo -v
sudo whoamiThe final command must print root. Leave the original root session open until Step 4 is complete. For teams, give every administrator a separate account and key. Do not share one private key or store keys in chat, tickets, or unencrypted password files.
Step 3: Configure a default-deny firewall
UFW controls inbound traffic at the host. Allow SSH first, then allow only services that are intentionally public. If your office has a fixed public IP, restricting SSH to that address reduces password-spraying and exploit attempts.
ufw default deny incoming
ufw default allow outgoing
ufw allow from YOUR_PUBLIC_IP to any port 22 proto tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
ufw status verboseReplace YOUR_PUBLIC_IP with a real address such as 203.0.113.10. If your IP changes frequently, use ufw allow 22/tcp temporarily, then restrict SSH later through a VPN or a stable office address. Do not enable UFW until an SSH allow rule exists.
For applications that need UDP, add only the required port. For example, a WireGuard server commonly needs:
ufw allow 51820/udp
ufw status numberedDo not use broad rules such as ufw allow 1:65535/tcp. A reverse proxy normally needs TCP 80 and 443; PostgreSQL, MySQL, Redis, and Docker should not be opened publicly for ordinary deployments.
Step 4: Harden SSH without losing access
Disable root login and password authentication only after confirming that the new administrator can log in with a key. Use an SSH drop-in file rather than editing the vendor configuration directly.
cat > /etc/ssh/sshd_config.d/99-hardening.conf <<'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
X11Forwarding no
MaxAuthTries 3
LoginGraceTime 30
AllowUsers admin
EOF
sshd -t
systemctl reload sshThe sshd -t check must return no output. Open a third terminal and verify both expected success and expected failure:
ssh -o PreferredAuthentications=publickey admin@SERVER_IP
ssh -o PreferredAuthentications=password admin@SERVER_IPThe key-based command should connect; the password-only command should fail. If key login fails, use the still-open root session or provider console, remove the drop-in file, run systemctl reload ssh, and inspect journalctl -u ssh -n 50.
Step 5: Enable brute-force protection and automatic security updates
Fail2ban reads authentication logs and blocks repeated failed attempts. It is a supplement to SSH keys and firewall restrictions, not a replacement for them.
cat > /etc/fail2ban/jail.d/sshd.local <<'EOF'
[sshd]
enabled = true
backend = systemd
maxretry = 4
findtime = 10m
bantime = 1h
EOF
systemctl enable --now fail2ban
fail2ban-client status sshdEnable unattended security updates so known vulnerabilities receive patches between maintenance windows:
dpkg-reconfigure -plow unattended-upgrades
systemctl enable --now apt-daily-upgrade.timer
systemctl status apt-daily-upgrade.timer --no-pagerReview reboot requirements regularly. Kernel and low-level library updates may need a scheduled reboot even if packages installed successfully:
test -f /var/run/reboot-required && cat /var/run/reboot-required || echo "No reboot required"Step 6: Establish logs, time synchronization, and basic auditing
Accurate time makes incident investigation possible. Ubuntu uses systemd-timesyncd by default; verify synchronization and enable audit logging for changes to privileged files and commands.
timedatectl status
systemctl enable --now auditd
systemctl status auditd --no-pager
auditctl -lInspect authentication activity with these commands:
journalctl -u ssh --since "30 minutes ago"
last -a | head -20
sudo ausearch -m USER_LOGIN --start todayFor production servers, send logs to a separate system using syslog, a monitoring platform, or a security information and event management service. An attacker who gains root access can alter local logs; off-server retention improves evidence quality.
Step 7: Check storage health and create a backup plan
RAID improves availability after a disk failure but is not a backup. A deleted database, compromised application, or ransomware event can replicate across a mirror immediately. Maintain encrypted copies outside the server and test restoration.
apt install -y smartmontools
smartctl -a /dev/sda | less
systemctl enable --now smartd
mkdir -p /root/backup-test
tar -czf /root/backup-test/etc-$(date +%F).tar.gz /etcDevice names vary. Use lsblk to identify disks; NVMe devices commonly appear as /dev/nvme0n1. For application data, back up databases with database-native tools such as pg_dump or mariadb-dump, then send encrypted backups to an independent storage location. Retain multiple restore points and perform a test restore at least quarterly.
Step 8: Verify the exposed attack surface from outside
Run a port scan from your workstation after firewall configuration. The result should show only the ports you deliberately allowed.
nmap -Pn -sV -p 22,80,443,51820 SERVER_IP
curl -I http://SERVER_IP
sudo ufw status numbered
sudo fail2ban-client status sshdIf no web server is installed, ports 80 and 443 may show as filtered because UFW allows traffic but nothing listens on those ports. That is acceptable during setup; remove the UFW rules until a reverse proxy or web service is deployed. Check open listeners again with ss -tulpn after installing Docker, a game server, a mail stack, or a control panel because each can add new exposure.
Troubleshooting common first-30-minute problems
SSH stopped working after hardening
Use provider console access, validate the configuration with sshd -t, and inspect journalctl -u ssh -n 100. Confirm your public key is in /home/admin/.ssh/authorized_keys, the directory mode is 700, and the file mode is 600. Remove AllowUsers admin if your actual login name differs.
UFW blocked your connection
From the console, run ufw status numbered. Add the correct rule using ufw allow 22/tcp or your correct source IP, then delete the mistaken rule with ufw delete NUMBER. Keep console access available for every remote firewall change.
Fail2ban banned your office IP
Check the jail and remove the ban with:
fail2ban-client status sshd
fail2ban-client set sshd unbanip YOUR_PUBLIC_IPAdd trusted administration IP addresses to an ignoreip list only if they are stable and controlled. Do not whitelist broad ISP ranges.
Automatic updates did not apply
Check timer activity with systemctl list-timers apt-daily-upgrade.timer and review /var/log/unattended-upgrades/unattended-upgrades.log. Confirm DNS and outbound HTTPS connectivity before assuming a package problem.
Security work after the first 30 minutes
The initial baseline is only the start. Add a reverse proxy with current TLS settings, deploy monitoring for disk usage, load, memory, certificate expiry, and backup failures, separate production from development, patch applications promptly, and periodically review users, SSH keys, firewall rules, and running containers. For mail servers, payment systems, healthcare data, or regulated workloads, document access controls and consider an independent security review.