Introduction
A freshly provisioned VPS from any host arrives in a raw state: root login, password, no firewall, default locale, no monitoring. Before deploying anything on it, you need to prepare it.
This guide is the exhaustive checklist you systematically run on every new Debian 12 / Ubuntu 22.04+ VPS. Count 30-45 minutes the first time, 10-15 minutes once it's routine.
Prerequisites
- A VPS delivered by VeryCloud (Linux Debian 12 or Ubuntu 22.04+)
- Root credentials received by email
- Your local public SSH key ready (otherwise generate one, see SSH hardening guide)
Step 1: First root login
ssh root@VPS_IP
Accept the SSH signature. Update immediately:
apt update && apt upgrade -y
apt autoremove --purge -y
Step 2: Set hostname
hostnamectl set-hostname srv-web-01.verycloud.fr
echo "127.0.1.1 srv-web-01.verycloud.fr srv-web-01" >> /etc/hosts
Verify:
hostname -f
# srv-web-01.verycloud.fr
Step 3: Locale and timezone
# Timezone
timedatectl set-timezone Europe/Paris
# Locale
apt install -y locales
sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
locale-gen
update-locale LANG=en_US.UTF-8
Verify:
date
Step 4: Time synchronization (NTP)
Crucial for SSL certificates, correlated logs, and 2FA:
apt install -y chrony
systemctl enable --now chrony
chronyc tracking
Step 5: Create a non-root user
Never use root daily. Create a sudo user:
adduser mathys
# (password + optional info)
usermod -aG sudo mathys
Copy your public SSH key for this user:
mkdir -p /home/mathys/.ssh
nano /home/mathys/.ssh/authorized_keys
# (paste your ssh-ed25519 AAAAC... public key)
chown -R mathys:mathys /home/mathys/.ssh
chmod 700 /home/mathys/.ssh
chmod 600 /home/mathys/.ssh/authorized_keys
Test the connection without closing root session:
# From your PC
ssh mathys@VPS_IP
sudo -i # Should work
Step 6: SSH hardening
⚠️ Don't close the root session until you've validated you can connect via the new user.
sudo nano /etc/ssh/sshd_config
Edit:
Port 22022 # Custom port
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no
MaxAuthTries 3
MaxSessions 5
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
X11Forwarding no
AllowUsers mathys
Test:
sudo sshd -t
sudo systemctl reload sshd
To go further, see the full SSH hardening guide (Ed25519, TOTP 2FA, port knocking).
Step 7: UFW firewall
sudo apt install -y ufw
# Default policy
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH (custom port)
sudo ufw allow 22022/tcp
# Allow web if needed
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Enable
sudo ufw enable
sudo ufw status verbose
Step 8: Fail2ban (anti brute-force)
sudo apt install -y fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
Enable the SSH jail:
[sshd]
enabled = true
port = 22022
maxretry = 3
bantime = 1h
findtime = 10m
sudo systemctl restart fail2ban
sudo fail2ban-client status
For more modern protection, see the CrowdSec guide.
Step 9: Automatic updates
sudo apt install -y unattended-upgrades apt-listchanges
sudo dpkg-reconfigure -plow unattended-upgrades
# Choose "Yes"
sudo nano /etc/apt/apt.conf.d/50unattended-upgrades
Enable security + stable updates:
Unattended-Upgrade::Origins-Pattern {
"origin=Debian,codename=${distro_codename}-security";
"origin=Debian,codename=${distro_codename}-updates";
};
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";
Configure frequency:
sudo nano /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
Test:
sudo unattended-upgrades --dry-run --debug
Step 10: Swap (if not already configured)
Check:
free -h
swapon --show
If no swap and < 4 GB RAM, add some (useful during spikes):
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo "/swapfile none swap sw 0 0" | sudo tee -a /etc/fstab
Optimize usage:
echo "vm.swappiness=10" | sudo tee -a /etc/sysctl.d/99-tuning.conf
sudo sysctl --system
Step 11: Basic sysadmin tools
sudo apt install -y \
htop iotop iftop nethogs \
curl wget git vim nano \
rsync rclone \
tree ncdu \
jq net-tools dnsutils \
tmux screen \
unzip zip \
lsof tcpdump \
sudo bash-completion
Step 12: Logs and journald
# Limit systemd logs to 500 MB
sudo nano /etc/systemd/journald.conf
SystemMaxUse=500M
SystemMaxFileSize=50M
ForwardToSyslog=no
sudo systemctl restart systemd-journald
Step 13: Clean MOTD (optional)
The message at connection. For something clean:
sudo apt install -y figlet
sudo nano /etc/update-motd.d/00-custom
#!/bin/bash
figlet "VeryCloud"
echo ""
echo " Hostname : $(hostname -f)"
echo " IP : $(curl -s ifconfig.me)"
echo " OS : $(lsb_release -d | cut -f2)"
echo " Kernel : $(uname -r)"
echo " Uptime : $(uptime -p)"
echo " Load : $(cut -d' ' -f1-3 < /proc/loadavg)"
echo " Memory : $(free -h | awk '/^Mem:/ {print $3 "/" $2}')"
echo " Disk / : $(df -h / | awk 'NR==2 {print $3 "/" $2}')"
echo ""
sudo chmod +x /etc/update-motd.d/00-custom
Step 14: Deterrent SSH banner
sudo nano /etc/issue.net
*****************************************************
* Authorized access only. This system is monitored. *
* Unauthorized access is prohibited and prosecuted. *
*****************************************************
sudo nano /etc/ssh/sshd_config
Banner /etc/issue.net
sudo systemctl reload sshd
Step 15: Final reboot
To validate everything:
sudo reboot
Reconnect:
ssh -p 22022 mathys@VPS_IP
Verify:
sudo systemctl status fail2ban
sudo ufw status
free -h
df -h
Everything must be OK.
Step 16: Internal documentation
Create a notes file:
nano ~/server-info.md
Note:
- Installation date
- Hostname, IP, OS
- Custom SSH port
- Installed services
- Passwords (store off-VPS, in Bitwarden/KeePass)
- Configured backups
- Last major update
Step 17: Basic monitoring
To go further immediately, install Netdata (5 minutes) or Prometheus + Grafana (30 minutes). See dedicated guides.
For minimal without extra stack:
# Centralized session logging
sudo apt install -y auditd
# View changes to critical files
sudo auditctl -w /etc/passwd -p wa -k passwd_changes
sudo auditctl -w /etc/sudoers -p wa -k sudoers_changes
Final checklist
[ ] Hostname configured
[ ] Timezone and locale OK
[ ] Non-root user with sudo created
[ ] SSH key installed and password disabled
[ ] Custom SSH port configured
[ ] UFW enabled (allow SSH, 80, 443)
[ ] Fail2ban active
[ ] unattended-upgrades configured
[ ] Swap if needed
[ ] NTP/chrony synced
[ ] Sysadmin tools installed
[ ] Clean MOTD
[ ] Reboot validated
[ ] Installation notes saved
Troubleshooting
Locked out after SSH change
Use noVNC from VeryCloud customer area to recover.
"locale not generated" error
sudo dpkg-reconfigure locales
UFW blocks SSH after activation
Custom port wasn't allowed. Via noVNC:
sudo ufw allow 22022/tcp
Useful commands
# System summary
uname -a
lsb_release -a
hostnamectl
timedatectl
# Service status
systemctl list-units --type=service --state=running
# Who's connected
who
w
last -10
# System load
top
htop
uptime
# Disks
df -h
ncdu / # visual browser
du -sh /var/log/*
# Network
ip a
ss -tlnp # listening ports
Conclusion
Your VPS is now production-ready. The foundation is set:
- Secured access (key + custom port + fail2ban + UFW)
- Auto-updating system
- Sysadmin tools present
- Correct locale/time
From here, you can install anything: Nginx, MySQL, Docker, n8n... See dedicated guides.
Resources
- VeryCloud guide — Secure a VPS: https://verycloud.fr/docs/article/how-to-secure-vps
- VeryCloud guide — SSH key configuration: https://verycloud.fr/docs/article/configure-sshkey-linux
- VeryCloud guide — Advanced SSH hardening:
/docs/article/ssh-hardening - VeryCloud guide — Kernel tuning:
/docs/article/kernel-tuning


















