Logo

Debian VPS setup from A to Z: from first login to production

 Debian VPS setup from A to Z: from first login to production

The complete checklist to prepare a freshly delivered Debian 12 VPS from VeryCloud. Non-root user, SSH key, firewall, auto updates, NTP, timezone, swap, basic monitoring. Follow this once and copy-paste it for every new VPS.

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

Join our Discord community server

For any questions, suggestions, or just to chat with the community, join us on Discord!

900+Members