I like to self-host my web applications and software such as blog platforms and Git servers. This gives you more independence from third-party services and the freedom to install and configure software the way you want.
Managing your own server requires more effort than using a managed service. There is initial setup work, and you need to maintain and update the server regularly.
Even with these drawbacks, running your own server is interesting, and you can learn a lot while improving your Linux and networking skills.
You do not need a server rack in your apartment to run Internet-connected services. Instead, you can rent a VPS from a hosting provider. Examples include OVH, Linode, DigitalOcean, Scaleway, and Vultr.
A VPS is a virtual machine running its own operating system where you usually have root access. Most VPS systems run Linux, but Windows options are also available.
VPS hosting is affordable. Entry-level plans often cost only a few US dollars per month, and many providers let you rent month-to-month. That is a good way to test whether self-hosting is right for you.
In this post, I describe the first configuration steps after renting a VPS: updating the operating system, enabling a firewall, and hardening SSH.
For this post, I use a VPS from OVH. I am not affiliated with OVH; it is simply the provider I have used most often.
Rent VPS ¶
Visit your preferred VPS provider and choose a plan. Plans differ in CPU, RAM, storage, and network speed. Many low-cost plans are not vertically scalable, so think ahead about what you want to run.
If you only need one lightweight service (for example, a self-hosted Git server or a small WordPress site), a small plan may be enough. If you plan to run multiple services, choose more RAM and storage from the beginning.
During checkout, you usually choose:
- data center location
- operating system image
- billing period
- optional add-ons (backups, snapshots, extra storage)
After payment, the provider provisions the VPS and sends connection details by email.
Connect with SSH ¶
The provider email usually includes your VPS IP address and initial SSH credentials. SSH provides an encrypted channel from your computer to the server.
From a shell, connect to the server (replace with your IP):
ssh root@51.38.124.133
On first connect, SSH asks you to trust the host fingerprint:
The authenticity of host '51.38.124.133 (51.38.124.133)' can't be established.
ED25519 key fingerprint is SHA256:....
Are you sure you want to continue connecting (yes/no/[fingerprint])?
If your provider exposes the expected fingerprint in the control panel, compare it before accepting.
After login, immediately change the initial password because credentials sent via email should be treated as compromised:
passwd
If you get locked out, use your provider rescue mode (or equivalent) to reset access.
Update operating system ¶
On Debian/Ubuntu, update package metadata and upgrade installed packages:
apt update
apt full-upgrade
Reboot if a new kernel is installed:
reboot
Keeping packages updated is one of the most important security controls for an Internet-facing server.
Enable unattended security updates ¶
Install unattended upgrades:
apt install unattended-upgrades
dpkg-reconfigure --priority=low unattended-upgrades
Then verify configuration in:
/etc/apt/apt.conf.d/50unattended-upgrades
On modern Ubuntu versions, Origins-Pattern is often used. If your goal is security-only automatic updates, keep only the security origins enabled.
You can also check periodic settings in:
/etc/apt/apt.conf.d/20auto-upgrades
Typical values:
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
Official docs: https://ubuntu.com/server/docs
IPv6 ¶
Many providers assign IPv6 by default, but configuration may still be required.
Quick check:
ping6 www.google.com
If you get connect: Network is unreachable, IPv6 is not active.
On Ubuntu, netplan manages interfaces. Edit:
nano /etc/netplan/50-cloud-init.yaml
Example:
network:
version: 2
ethernets:
ens3:
dhcp4: true
addresses:
- 2001:41d0:701:1100:0:0:0:e54/64
routes:
- to: ::/0
via: 2001:41d0:0701:1100:0000:0000:0000:0001
match:
macaddress: fa:16:3e:dd:b2:03
set-name: ens3
Apply safely with rollback support:
netplan try
Then verify:
ip -6 addr
ping6 www.google.com
Harden SSH server ¶
SSH is the main administrative entry point. Hardening it should be a priority.
Change default port (optional) ¶
Changing port 22 does not stop targeted attacks, but it reduces noise from basic scanners.
Edit:
nano /etc/ssh/sshd_config
Set:
Port 44933
Check for conflicts before choosing a port:
ss -tulpn | grep LISTEN
Reload and verify SSH:
systemctl restart ssh
systemctl status ssh
Reconnect using the new port:
ssh -p 44933 root@51.38.124.133
Create admin user and disable root login ¶
Create a non-root admin user:
adduser manager
usermod -aG sudo manager
Test login:
ssh -p 44933 manager@51.38.124.133
sudo ls -al /root
Then update SSH config:
sudo nano /etc/ssh/sshd_config
Set:
PermitRootLogin no
AllowUsers manager
Restart SSH and keep a second session open while testing to avoid lockout.
Disable password login (use SSH keys) ¶
Use Ed25519 keys for client authentication.
Create a key pair on your local machine:
ssh-keygen -t ed25519 -C "mydesktopcomputer"
Copy the public key to the server (Linux/macOS):
ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 44933 manager@51.38.124.133
Manual method:
ssh -p 44933 manager@51.38.124.133
mkdir -p ~/.ssh
chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
Then enforce key-based auth in sshd_config:
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
You can also add:
MaxAuthTries 3
LoginGraceTime 30
X11Forwarding no
Validate and restart SSH:
sudo sshd -t
sudo systemctl restart ssh
Test key login before closing your current session:
ssh -i ~/.ssh/id_ed25519 -p 44933 manager@51.38.124.133
Simplify client access ¶
To avoid long commands, configure local SSH client options in ~/.ssh/config:
Host myvps
HostName 51.38.124.133
User manager
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
Port 44933
Now connect with:
ssh myvps
Use ssh-agent so you do not enter the key passphrase every time.
Install firewall ¶
Install UFW:
sudo apt install ufw
Set defaults first:
sudo ufw default deny incoming
sudo ufw default allow outgoing
Allow SSH on your custom port before enabling the firewall:
sudo ufw allow 44933/tcp
sudo ufw limit 44933/tcp
Enable and verify:
sudo ufw enable
sudo ufw status verbose
Reconnect with SSH to confirm the rule works.
Official UFW docs: https://help.ubuntu.com/community/UFW
Optional additional hardening ¶
If you want stronger baseline protection, consider:
- installing
fail2banfor SSH abuse mitigation - enabling MFA for SSH where feasible
- disabling unused services and ports
- configuring regular backups and restore tests
- using provider snapshots before risky changes
Wrap-up ¶
After these steps, your VPS has a safer baseline: updated packages, automatic security updates, hardened SSH, and a host firewall.
If you find errors or have additional hardening tips, feel free to send me a message.
If you cannot wait to install software, this is a great list of self-hosted projects: https://github.com/awesome-selfhosted/awesome-selfhosted
See also my related posts:
Self-hosted Git server with Gitea:
https://blog.rasc.ch/2018/06/self-hosted-git-server.html
Self-hosted Google Drive alternative with SparkleShare:
https://blog.rasc.ch/2018/06/self-hosted-gdrive-onedrive-dropbox-alternative.html
Sending emails:
https://blog.rasc.ch/2018/06/send-only-email.html
Self-hosted tile server:
https://blog.rasc.ch/2018/07/self-hosted-tile-server.html