
Initial Server Setup on Ubuntu and Debian: Sudo and SSH Keys
A brand new VPS is a bit like a new apartment with the keys still taped to the front door. Within minutes of the server getting a public IP address, automated bots start knocking on port 22 and trying combinations like “root / 123456” and “admin / admin”, thousands of times a day. Most of them will fail, but “most” is not a security strategy. The good news is that closing that door properly takes about half an hour, and you only have to do it once.
In this initial server setup guide for Ubuntu and Debian we do exactly what we do ourselves on every fresh machine: create a regular user with sudo rights, log in with an SSH key instead of a password (from Windows, macOS or Linux), and then turn off root and password logins in a way that cannot lock you out. Everything works the same on Debian 13 and Ubuntu 24.04, and we point out the few places where they differ.
What we are going to do, and why
Before typing any commands, a few words that will come up all the time:
- root is the all-powerful administrator account on Linux. It can read, change or delete anything, so a single typo or a stolen root password means the whole server is compromised.
- sudo (“superuser do”) lets a normal user run one command at a time with root powers, after typing their own password. You stay a regular user for everyday work, raise privileges only when needed, and every sudo command is logged.
- SSH (Secure Shell) is the encrypted protocol you use to get a terminal on the server. The program that answers on the server side is called sshd (the SSH daemon) and by default it listens on port 22. A port is simply a numbered “door” on the server’s IP address; each network service listens behind its own number.
- An SSH key is a pair of files: a private key that stays on your computer and never leaves it, and a public key that you copy to the server. The server can verify that you hold the private key without the key ever crossing the network.
- A firewall is a filter that decides which ports accept connections from the internet at all.
Here is the picture we are building: one legitimate path in, through a key and a personal user, and everything else rejected.
The plan has eight short steps. The order matters: we first build the new way in (user plus key), test it, and only then close the old way in (root plus passwords). That order is the whole trick to never locking yourself out.
What you need
- A fresh DreamServer VPS (or any KVM or LXC server) running Debian 13 or Ubuntu 24.04, with its public IP address and the root password or root SSH key you received at provisioning.
- A computer with a terminal: Terminal on macOS and Linux, PowerShell or Windows Terminal on Windows 10 and 11. Both Windows versions already include the OpenSSH client, so you do not need PuTTY.
- About 30 minutes, and access to the VPS web console in your client area, just in case. You will very likely not need it, but it is your safety net if something goes wrong.
Throughout the guide we use 203.0.113.10 as the server IP and alex as the username. Replace both with your own values.
Step 1: Log in as root and update the system
Open a terminal on your computer and connect:
ssh root@203.0.113.10
The first time, SSH shows a message like this:
The authenticity of host '203.0.113.10 (203.0.113.10)' can't be established.
ED25519 key fingerprint is SHA256:x9Qm...k2Lw.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
This is the server introducing itself. Your computer has never seen it, so it asks you to confirm. Type yes; the fingerprint is saved in ~/.ssh/known_hosts, and from now on SSH will warn you loudly if the server’s identity ever changes. Then enter the root password.
Once you see a prompt ending in # (the sign that you are root), bring every package up to date:
apt update && apt upgrade -y
apt update refreshes the list of available packages, and apt upgrade -y installs the newer versions without asking for confirmation. On a fresh image this takes a minute or two. If a new kernel was installed, the file /var/run/reboot-required will exist; in that case run reboot, wait about 30 seconds and log in again.
Step 2: Create a regular user with sudo rights
This is the heart of any initial server setup on Ubuntu or Debian: you stop working as root. On a minimal Debian image sudo may not be installed yet (Ubuntu always has it), so install it first. If it is already there, apt simply tells you so:
apt install -y sudo
Now create the user:
adduser alex
adduser is the friendly, interactive tool: it creates the home directory /home/alex, copies the default shell files and asks for a password, then for optional details (full name, phone numbers) that you can skip by pressing Enter, and finally asks you to confirm. Choose a strong password and save it in your password manager. Even after we disable password logins over SSH, this is the password sudo will ask for, so it still matters.
Add the user to the sudo group, which on Debian and Ubuntu is allowed to use sudo out of the box:
usermod -aG sudo alex
The -aG flags mean “append to this group”. Forgetting the -a would remove the user from all other groups, a classic mistake. Check the result:
groups alex
You should see sudo in the list, for example alex : alex sudo (there may be other groups too, that is fine). Now verify that sudo really works by switching to the new user and asking the system who we are:
su - alex
sudo whoami
The first time, sudo may print a short lecture about responsibility, then asks [sudo] password for alex:. After you type the password, the answer should be a single word: root. That proves alex can act as root when needed. Type exit to return to the root shell, and keep this root session open for now.
Why SSH keys beat passwords
A password is a secret you send to the server every time you log in. If it is short or reused, a bot can eventually guess it, and every guess costs the attacker almost nothing. An SSH key works differently: the server sends a random challenge, your computer signs it with the private key, and the server checks the signature with the public key it already has. Nothing reusable crosses the network, and the key is so large that guessing it is simply not a realistic attack.
We use the Ed25519 key type: it is modern, fast, produces short keys and is supported by every OpenSSH version you will meet on Debian 13 or Ubuntu 24.04. RSA with 3072 or 4096 bits also works, but there is no reason to choose it for a new key unless an old system requires it.
Step 3: Generate an SSH key on your computer
This step happens on your own computer, not on the server. Open a new local terminal window and leave the server session alone.
On Linux and macOS
ssh-keygen -t ed25519 -C "alex@laptop"
On Windows 10 and 11
Open PowerShell (no administrator rights needed) and run the very same command:
ssh-keygen -t ed25519 -C "alex@laptop"
In all cases, -t ed25519 selects the key type and -C adds a comment, a label that helps you recognize this key later on the server. The tool asks where to save the key: press Enter to accept the default (~/.ssh/id_ed25519, which on Windows means C:\Users\YourName\.ssh\id_ed25519). Then it asks for a passphrase. Use one. It encrypts the private key on disk, so a stolen laptop or a leaked backup does not automatically give someone access to your server. A bit later we show how to type it only once per session.
At the end you have two files:
id_ed25519: the private key. Never copy it to a server, never send it by email or chat.id_ed25519.pub: the public key, a single line starting withssh-ed25519. This one is safe to share.
Step 4: Copy the public key to the server
The server knows which keys are allowed through a file called ~/.ssh/authorized_keys in each user’s home directory. Every line in it is one public key. We need to put your .pub line into /home/alex/.ssh/authorized_keys, with the right owner and permissions.
Linux and macOS: ssh-copy-id
ssh-copy-id -i ~/.ssh/id_ed25519.pub alex@203.0.113.10
It asks for alex’s password one last time (password logins are still allowed at this point) and should end with:
Number of key(s) added: 1
ssh-copy-id creates the .ssh folder if needed and sets the correct permissions for you.
Windows: a PowerShell one-liner
Windows does not ship ssh-copy-id, but this PowerShell command does the same job:
type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh alex@203.0.113.10 "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
Read it from left to right: type prints your public key, the pipe | sends it into an SSH connection, and on the server we create the folder, append the key to authorized_keys and lock down the permissions. Mode 700 means only the owner can enter the folder, and 600 means only the owner can read and write the file.
If root already logs in with a key
Some VPS are provisioned with your SSH key already installed for root. In that case, from the root session on the server, just copy root’s authorized keys to the new user:
mkdir -p /home/alex/.ssh
cp /root/.ssh/authorized_keys /home/alex/.ssh/
chown -R alex:alex /home/alex/.ssh
chmod 700 /home/alex/.ssh
chmod 600 /home/alex/.ssh/authorized_keys
The chown line is the important one: if the files stay owned by root, sshd refuses to use them for alex.
Step 5: Test key login in a second terminal
This is the step that keeps you from locking yourself out. Do not close the root session. Open a brand new terminal window on your computer and connect as the new user:
ssh alex@203.0.113.10
If you set a passphrase, SSH asks for it (Enter passphrase for key ...). That is your local key being unlocked, not a server password. If you land at a prompt like alex@vps:~$ without being asked for alex’s server password, key login works. Confirm that sudo works in this session too:
sudo apt update
If anything fails here, stop and read the troubleshooting section below. Nothing has been switched off yet, so you lose nothing by fixing it now.
Step 6: Disable root login and password authentication
Now we close the old doors. Both Debian 13 and Ubuntu 24.04 read extra configuration files from /etc/ssh/sshd_config.d/, which is cleaner than editing the main file and survives package upgrades. Confirm the include line is there:
grep -i '^Include' /etc/ssh/sshd_config
Expected output: Include /etc/ssh/sshd_config.d/*.conf. One subtle detail: for most settings, sshd uses the first value it finds, and drop-in files are read in alphabetical order. Some Ubuntu images ship a file called 50-cloud-init.conf that sets PasswordAuthentication yes. That is why we start our file name with 01-, so it is read first and wins:
sudo tee /etc/ssh/sshd_config.d/01-hardening.conf > /dev/null <<'EOF'
# Initial hardening: keys only, no direct root login
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
EOF
What each line does:
PermitRootLogin no: nobody can log in over SSH directly as root, not even with a key. Admin work goes through alex and sudo.PasswordAuthentication no: the server stops accepting passwords over SSH.KbdInteractiveAuthentication no: closes the “keyboard-interactive” path, which on some systems is another way of asking for a password.PubkeyAuthentication yes: keeps key login on. It is the default, we just state it explicitly for clarity.
Before restarting anything, ask sshd to check the syntax:
sudo sshd -t
No output means no errors. Then look at the settings sshd will actually use after merging every file:
sudo sshd -T | grep -Ei 'permitrootlogin|passwordauthentication|kbdinteractive'
You should see:
permitrootlogin no
passwordauthentication no
kbdinteractiveauthentication no
If any of them still says yes, another file is overriding yours; list them with ls /etc/ssh/sshd_config.d/. When everything looks right, restart the SSH service. On both Debian and Ubuntu the service is called ssh:
sudo systemctl restart ssh
Restarting sshd does not drop sessions that are already open, so your root window and your alex window both stay alive. Now, from a third terminal, prove that the old doors are really closed:
ssh root@203.0.113.10
ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password alex@203.0.113.10
Both should be refused with Permission denied (publickey). The word in brackets is the list of methods the server still accepts: only publickey. Then confirm one more time that a plain ssh alex@203.0.113.10 still works. Only now is it safe to close the old root session.
Step 7: Turn on a basic firewall
Right now the only service we want reachable from the internet is SSH, so we allow port 22 and block every other incoming connection. Ubuntu uses UFW (Uncomplicated Firewall), and the same package works perfectly on Debian:
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw enable
UFW warns that the command may disrupt existing SSH connections; since we allowed 22/tcp first, answer y. Check the result:
sudo ufw status verbose
You should see Status: active, Default: deny (incoming), allow (outgoing) and a rule allowing 22/tcp. When you later install a website, remember to also run sudo ufw allow 80/tcp and sudo ufw allow 443/tcp. One warning for the future: ports published by Docker containers bypass UFW rules, so if you run Docker, bind internal services to 127.0.0.1 instead of relying on the firewall alone.
Small extras that make daily life easier
A shortcut in your SSH config
Instead of typing the IP every time, create (or edit) the file ~/.ssh/config on your computer. On Windows it is C:\Users\YourName\.ssh\config, with no extension:
Host myvps
HostName 203.0.113.10
User alex
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
From now on, ssh myvps is enough. IdentitiesOnly yes tells SSH to offer only this key, which avoids “Too many authentication failures” when you have several keys on the same computer.
Type the passphrase once per session
The SSH agent keeps your unlocked key in memory, so you type the passphrase once instead of at every connection. On macOS, ssh-add --apple-use-keychain ~/.ssh/id_ed25519 also stores it in the Keychain. On most Linux desktops an agent already runs, so ssh-add is enough. On Windows, run this once in an administrator PowerShell:
Get-Service ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
Then, in a normal PowerShell window, run ssh-add $env:USERPROFILE\.ssh\id_ed25519.
Time zone and automatic security updates
Back on the server, set the correct time zone so log timestamps make sense, and let the system install security patches on its own:
sudo timedatectl set-timezone Europe/Bucharest
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
Answer Yes when asked whether to automatically download and install stable updates.
Troubleshooting and common mistakes
“Permission denied (publickey)” for alex. Almost always an ownership or permissions problem. sshd is strict on purpose: if ~/.ssh or authorized_keys can be written by someone else, it ignores them. From the root session (or the web console), run:
chown -R alex:alex /home/alex/.ssh
chmod 700 /home/alex/.ssh
chmod 600 /home/alex/.ssh/authorized_keys
chmod go-w /home/alex
Then watch what sshd says while you try to connect: sudo journalctl -u ssh -n 50 --no-pager. Lines such as Authentication refused: bad ownership or modes point straight at the problem. Debian 13 does not write /var/log/auth.log by default, so journalctl is the reliable place to look on both systems.
Your key is not the one being offered. Run ssh -v alex@203.0.113.10 and read the lines that start with Offering public key. If your key is missing, add -i ~/.ssh/id_ed25519 or use the IdentityFile line from the config above.
“alex is not in the sudoers file”. Group changes only apply to new logins, so log out and back in. If it still fails, check that you typed usermod -aG sudo alex and not a different group name.
Password login still works after Step 6. Another drop-in file is winning. Run sudo sshd -T | grep passwordauthentication, then sudo grep -ri passwordauthentication /etc/ssh/sshd_config /etc/ssh/sshd_config.d/ to find the culprit, and make sure your file sorts first.
“WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!” Expected after you reinstall the VPS, because the new system generated new host keys. Remove the old fingerprint with ssh-keygen -R 203.0.113.10 and connect again. If you did not reinstall anything, stop and find out why before typing any password.
“ssh-copy-id is not recognized” on Windows. Normal, use the PowerShell one-liner from Step 4.
Locked out anyway. Open the web console of the VPS from your client area. It behaves like a monitor and keyboard plugged into the server and does not depend on SSH. Log in as root with the password there (PermitRootLogin only affects SSH), fix the file or the permissions, and run systemctl restart ssh.
Security notes
- Always use a passphrase on your private key, and keep a backup of it in your password manager. If you lose the only copy, you will need the console to add a new one.
- One key per device. Generate a separate key on your laptop and on your desktop, each with a clear
-Ccomment. If a device is lost, you delete only its line fromauthorized_keysand the others keep working. - Do not set sudo to NOPASSWD for your everyday user. The sudo password is a second barrier if someone ever gets hold of your key.
- Keep the root password, long and stored safely. It no longer works over SSH, but you need it for the emergency console.
- Changing the SSH port is not security. Moving SSH from 22 to another port reduces log noise from bots, but a scanner finds the new port in seconds. With keys only, the real protection is already in place. Tools like Fail2ban can be added later, mostly to keep logs quieter.
- Look at who logs in from time to time:
last -n 20lists recent logins andsudo journalctl -u ssh --since todayshows today’s SSH activity.
So, is your server ready?
After these 30 minutes your VPS has a personal account with sudo, a key-based login that bots cannot guess, no direct root access, no passwords over SSH and a firewall that only lets SSH in. That is the solid foundation every other tutorial on this blog assumes, whether you install Docker, a web server or a database next.
If you do not have a server yet, you can get a VPS from our Romanian data center and walk through this guide over a coffee. And if you would rather have someone else harden, patch and watch your servers, our server administration team does exactly this every day.
FAQ
Is this guide also valid for Ubuntu 22.04 and Debian 12?
Yes. Every command works on Ubuntu 22.04 and Debian 12 as well. The drop-in folder /etc/ssh/sshd_config.d/ exists on both, and the service is still called ssh.
Should I use Ed25519 or RSA keys?
Ed25519 for anything new: shorter, faster and secure. Use RSA (at least 3072 bits) only for very old systems that do not understand Ed25519.
Can I still do things as root?
Yes, through sudo. sudo command runs a single command as root, and sudo -i opens a full root shell when you need several commands in a row. Type exit when you are done.
What happens if I lose my laptop?
If the key has a passphrase, the thief cannot use it right away. Log in from another device (or the web console), delete the line with that key’s comment from ~/.ssh/authorized_keys, and generate a new key on your new machine.
Do I need Fail2ban if passwords are disabled?
It is not required for safety, because there is no password left to guess. It is still useful for keeping logs quieter and for protecting other services, such as a mail server or a web login page.