Why trust this guide
By Victus Cloud · Reviewed by Victus Cloud · No individual author claimed. Verify paths, versions, and backups before changing a live service.
Evidence-led, product-agnostic
Before the first login: know the recovery path
Record the VPS identifier, assigned addresses, installed Ubuntu release, and how to reach console, rescue, password reset, or reinstall controls. These controls vary by provider and plan; verify the current Victus panel rather than assuming any feature is present. Keep the original session open while changing SSH. If you lock yourself out, a tested recovery path is safer than weakening the firewall in a panic.
Commands need context
Examples target a recent supported Ubuntu Server using OpenSSH and UFW. Read each command, adjust usernames and ports, and keep an authenticated session open. Do not disable password or root login until a second key-based session works. Backups, snapshots, network firewalls, and console access are plan-dependent.
1. Confirm the system and install security updates
cat /etc/os-release
uname -r
sudo apt update
sudo apt upgradeReview the proposed packages before accepting them. A kernel or critical library update may require a reboot; needrestart may report affected services when installed. Do not run an unattended distribution release upgrade during initial setup. If the image is already end-of-life, choose a supported image or plan a documented upgrade. Reboot when required, reconnect, and confirm services before continuing.
2. Create a named administrator and install an SSH key
A named account gives clearer audit history than routine root login. Generate a modern key on your local device and protect its private half. Ed25519 is a good default for current OpenSSH clients; organizations with specific compatibility or compliance needs should follow their policy. Never paste a private key into the VPS panel, chat, or a ticket.
ssh-keygen -t ed25519 -a 64 -C "admin-workstation-2026"
ssh-copy-id admin@203.0.113.10The example IP is documentation-only and will not be your server. If ssh-copy-id is unavailable, create the user first and add the single public .pub line to that user's ~/.ssh/authorized_keys, with directory mode 700 and file mode 600. On the VPS, create the account and grant Ubuntu's administrative group only if that person needs it.
adduser admin
usermod -aG sudo admin
id adminNow open a new terminal and log in as admin using the key. Run sudo -v to confirm administrative access. Keep both sessions open. If the new login fails, fix the public key, home ownership, SSH logs, or network rule first. Do not proceed by turning off authentication broadly.
3. Harden SSH with a tested configuration drop-in
Ubuntu supports configuration snippets in /etc/ssh/sshd_config.d/. Existing cloud-image snippets can override assumptions, so inspect the effective configuration using sshd -T. Disabling direct root login and password authentication reduces common credential attacks only after key login works. Consider whether automation or another administrator still relies on a password before changing policy.
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
MaxAuthTries 4sudo sshd -t
sudo systemctl reload ssh
sudo sshd -T | grep -E 'permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication'If sshd -t reports anything, do not reload until corrected. Reload is less disruptive than restart, but keep the existing connection. Test a fresh key login and sudo again before closing the root session. Changing the SSH port is optional obscurity, not a substitute for key authentication and patching; it can complicate firewalls and monitoring.
4. Apply a default-deny host firewall without cutting SSH
List listening services first. Only expose ports users need. UFW changes the host firewall; a provider network firewall, if available on your plan, is a separate layer with separate state. Docker and other container tools can alter packet-filter behavior, so revisit firewall design before deploying them.
sudo ss -lntup
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw limit OpenSSH
sudo ufw enable
sudo ufw status verboseIf SSH uses a custom port, add that exact TCP port before enabling UFW and test a new connection. Add web ports only when a web service is ready: sudo ufw allow 80/tcp and sudo ufw allow 443/tcp. Do not expose a database globally by default. Bind internal services to loopback or a private network and use narrow source rules when remote access is genuinely required.
5. Configure security updates deliberately
sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
systemctl status unattended-upgrades --no-pagerReview /etc/apt/apt.conf.d/50unattended-upgrades and local policy. Automatic security updates reduce exposure but can restart services or require a reboot. Decide how reboot-required state is monitored, choose a maintenance process, and test application health after updates. If you cannot tolerate automatic package changes, you still need an owned, frequent manual patch process; doing nothing is not a strategy.
6. Remove or disable what you do not use
systemctl --type=service --state=running
sudo ss -lntup
sudo journalctl -p warning -b --no-pagerDo not copy a list of services to disable from an unrelated tutorial. Identify package ownership, purpose, dependencies, and recovery before changing it. Uninstall sample apps and close their ports. Run applications as dedicated unprivileged users, keep secrets outside source control, and give each service access only to the files and network endpoints it needs. Never run a public application as root merely to avoid a permission error.
7. Verify time, logs, and basic monitoring
Correct time makes certificates, authentication, and incident logs reliable. timedatectl should show synchronized time. Review SSH and system logs using journalctl; log locations vary by Ubuntu configuration. Set alerts for service health, disk space, memory pressure, failed backups, certificate expiry, and unexpected listening ports. A dashboard no one checks is not monitoring, and a host-based metric does not replace application health checks.
timedatectl
df -h
free -h
sudo journalctl -u ssh --since "1 hour ago" --no-pager
sudo ss -lntup8. Establish backup and recovery before deployment
Back up application data, databases with a consistent method, configuration, and a version/dependency manifest. Do not assume a VPS snapshot is application-consistent or included. Verify current Victus plan controls, export an independent copy of critical data, and conduct an isolated restore. Encrypt sensitive backups and store recovery keys separately. Test panel console or rescue access now, while the server is healthy.
What the first hour does not finish
Baseline hardening is not a one-time certificate. Next, model the application's threats, configure TLS, isolate databases, manage secrets, add multi-factor authentication where control planes support it, scan dependencies, and establish a patch and incident-response cadence. Tools such as fail2ban can reduce log noise from repeated attempts, but are optional defense-in-depth; they do not replace keys, patches, a firewall, and monitoring.
Before declaring the host ready, close the original privileged session and prove you can reconnect, elevate, reach only intended ports, read logs, apply updates, and recover from your independent backup. Record every deviation and owner. If access or panel behavior differs from this guide, stop and consult current Victus documentation or support rather than improvising destructive commands.