A practical guide to securing your VPS. These steps protect against the most common attacks: brute force, unauthorized access, and unpatched vulnerabilities.
Overview
This guide covers:
- SSH hardening (most important)
- Firewall configuration
- Automatic security updates
- Fail2ban for brute force protection
- Docker security
- Monitoring and auditing
Do these in order. SSH hardening alone blocks most attacks.
Step 1: Create a non-root user
Never use root for daily operations.
adduser deploy
usermod -aG sudo deploy
Test sudo access:
su - deploy
sudo whoami # Should output: root
Step 2: SSH key authentication
SSH keys are more secure than passwords. Generate a key on your local machine:
ssh-keygen -t ed25519 -C "[email protected]"
Copy it to your VPS:
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@YOUR_VPS_IP
Test key-based login:
ssh deploy@YOUR_VPS_IP
Step 3: Harden SSH configuration
Edit the SSH config:
sudo nano /etc/ssh/sshd_config
Make these changes:
# Disable root login
PermitRootLogin no
# Disable password authentication
PasswordAuthentication no
# Disable empty passwords
PermitEmptyPasswords no
# Use only SSH protocol 2
Protocol 2
# Limit authentication attempts
MaxAuthTries 3
# Set login grace time
LoginGraceTime 30
# Disable X11 forwarding (unless needed)
X11Forwarding no
# Disable TCP forwarding (unless needed)
AllowTcpForwarding no
# Only allow specific users
AllowUsers deploy
# Use strong ciphers
Ciphers [email protected],[email protected],[email protected]
MACs [email protected],[email protected]
KexAlgorithms curve25519-sha256,[email protected]
Test the configuration:
sudo sshd -t
Restart SSH:
sudo systemctl restart sshd
Important: Keep your current SSH session open and test a new connection before closing it.
Step 4: Change SSH port (optional)
Changing the default port reduces automated attacks.
sudo nano /etc/ssh/sshd_config
Port 2222 # Choose any port between 1024-65535
Update firewall before restarting SSH:
sudo ufw allow 2222/tcp
sudo systemctl restart sshd
Connect using the new port:
ssh -p 2222 deploy@YOUR_VPS_IP
Step 5: Configure the firewall (UFW)
UFW (Uncomplicated Firewall) is the easiest way to manage iptables.
Enable UFW
# Set default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH (use your port if changed)
sudo ufw allow 22/tcp
# or
sudo ufw allow 2222/tcp
# Allow HTTP and HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Enable the firewall
sudo ufw enable
# Check status
sudo ufw status verbose
Rate limiting
Limit connection attempts to SSH:
sudo ufw limit 22/tcp
This allows 6 connections per 30 seconds, then blocks.
Allow specific services
# PostgreSQL from specific IP only
sudo ufw allow from 10.0.0.5 to any port 5432
# Redis (local only)
sudo ufw allow from 127.0.0.1 to any port 6379
Step 6: Install Fail2ban
Fail2ban automatically blocks IPs that show malicious behavior.
Install
sudo apt update
sudo apt install fail2ban -y
Configure
Create a local config (do not edit the main config):
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
# Ban for 1 hour
bantime = 3600
# Check the last 10 minutes
findtime = 600
# Ban after 5 failures
maxretry = 5
# Email notifications (optional)
# destemail = [email protected]
# action = %(action_mwl)s
[sshd]
enabled = true
port = ssh # or your custom port
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 86400 # 24 hours for SSH
[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = /var/log/nginx/error.log
[nginx-limit-req]
enabled = true
filter = nginx-limit-req
port = http,https
logpath = /var/log/nginx/error.log
Start Fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Check status
sudo fail2ban-client status
sudo fail2ban-client status sshd
Unban an IP
sudo fail2ban-client set sshd unbanip 1.2.3.4
Step 7: Automatic security updates
Enable unattended upgrades for security patches.
Install
sudo apt install unattended-upgrades apt-listchanges -y
Configure
sudo dpkg-reconfigure -plow unattended-upgrades
Select "Yes" to enable automatic updates.
Customize (optional)
sudo nano /etc/apt/apt.conf.d/50unattended-upgrades
Key settings:
// Automatically reboot if required
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";
// Email notifications
Unattended-Upgrade::Mail "[email protected]";
// Remove unused dependencies
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Verify
sudo unattended-upgrades --dry-run --debug
Step 8: Secure shared memory
Prevent shared memory attacks:
sudo nano /etc/fstab
Add this line:
tmpfs /run/shm tmpfs defaults,noexec,nosuid 0 0
Apply:
sudo mount -o remount /run/shm
Step 9: Docker security
If running Docker, apply these security measures.
Do not run containers as root
FROM node:20-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# ... rest of Dockerfile
Use read-only filesystems
services:
app:
image: your-app
read_only: true
tmpfs:
- /tmp
- /var/run
Limit container capabilities
services:
app:
image: your-app
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE # Only if needed
security_opt:
- no-new-privileges:true
Set resource limits
services:
app:
image: your-app
deploy:
resources:
limits:
cpus: '1'
memory: 512M
reservations:
cpus: '0.25'
memory: 128M
Use Docker secrets for sensitive data
services:
app:
image: your-app
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
Keep Docker updated
sudo apt update
sudo apt upgrade docker-ce docker-ce-cli containerd.io -y
Scan images for vulnerabilities
docker scout cves your-image:latest
Or use Trivy:
sudo apt install trivy -y
trivy image your-image:latest
Step 10: Set up log monitoring
Install Logwatch
sudo apt install logwatch -y
Configure daily email reports:
sudo nano /etc/cron.daily/00logwatch
#!/bin/bash
/usr/sbin/logwatch --output mail --mailto [email protected] --detail high
Monitor auth logs
Watch for login attempts in real-time:
sudo tail -f /var/log/auth.log
Check for rootkits
Install rkhunter:
sudo apt install rkhunter -y
sudo rkhunter --update
sudo rkhunter --check
Step 11: Disable unused services
List running services:
sudo systemctl list-units --type=service --state=running
Disable unnecessary services:
sudo systemctl disable cups # Printing
sudo systemctl disable avahi-daemon # mDNS
sudo systemctl disable bluetooth # Bluetooth
Step 12: Set up intrusion detection (optional)
For more security, install AIDE:
sudo apt install aide -y
sudo aideinit
sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
Run daily checks:
sudo nano /etc/cron.daily/aide-check
#!/bin/bash
/usr/bin/aide --check | mail -s "AIDE report" [email protected]
sudo chmod +x /etc/cron.daily/aide-check
Security checklist
Use this checklist to verify your setup:
- Non-root user created with sudo access
- SSH key authentication enabled
- SSH password authentication disabled
- Root SSH login disabled
- Firewall enabled with minimal open ports
- Fail2ban installed and configured
- Automatic security updates enabled
- Docker containers running as non-root
- Unnecessary services disabled
- Log monitoring configured
Troubleshooting
Locked out of SSH
If you are locked out:
- Access via VPS provider console (web-based)
- Fix SSH config or authorized_keys
- Restart SSH
Fail2ban blocking legitimate IPs
Check bans:
sudo fail2ban-client status sshd
Unban:
sudo fail2ban-client set sshd unbanip YOUR_IP
Add to whitelist:
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
ignoreip = 127.0.0.1/8 YOUR_IP
UFW blocking needed traffic
Check current rules:
sudo ufw status numbered
Add missing rule:
sudo ufw allow PORT/tcp
Delete a rule:
sudo ufw delete NUMBER
Automatic updates causing issues
Check logs:
cat /var/log/unattended-upgrades/unattended-upgrades.log
Disable temporarily:
sudo systemctl stop unattended-upgrades
Where to go next
Tutorials:
Comparisons:
ServerCompass:
Related in the StoicSoft network
If you work in AI-assisted coding, shared terminal sessions, or agent-driven shell workflows like the ones above, 1devtool is the StoicSoft network's tool for safer AI-assisted terminal work — shared sessions with auditing, preflight policy, and tiered model routing built in.
From across the StoicSoft network
Hand-curated reads on the same topic from sister sites in the StoicSoft family.

