One command. Five layers of protection.
Just like you'd use a SaaS boilerplate to skip setting up auth and payments, use VPS Armor to skip the security basics every sysadmin does manually. What every experienced sysadmin does on day one—automated.
More indie hackers are spinning up VPS instances on OVHCloud, Hetzner, and DigitalOcean. That's fantastic—everyone should be able to self-host and escape the merchants of complexity. But a fresh VPS is a blank canvas with no guardrails. Your VPS deserves better than "I'll secure it later."
VPS Armor gives you the sensible defaults. One philosophy: KISS.
Run this as root on your VPS:
curl -sSL https://raw.githubusercontent.com/flegoff/vpsarmor/main/armor.sh | bash
Not running as root? Prefix with sudo
What does it do?
OS Check
Verifies you're on a supported Ubuntu LTS or Debian Stable release. No surprises on weird distros.
Full Update
Runs apt update and apt dist-upgrade to patch all the things.
Unattended Upgrades
Installs unattended-upgrades and uses distro defaults for automatic security updates.
Fail2Ban
Bans IPs that try to brute-force your SSH. Goodbye, script kiddies.
UFW Firewall
Ensures SSH/HTTP/HTTPS are allowed and preserves any existing UFW rules.
Don't trust, verify
Piping curl to bash? We get it. That's why the script is short, readable, and does exactly what it says:
View the full script on GitHub →The script (armor.sh)
#!/bin/bash
# VPS Armor - Basic security hardening for Ubuntu LTS and Debian Stable
# https://vpsarmor.com
# License: BSD 3-Clause
set -euo pipefail
echo "🛡️ VPS Armor - Hardening your server..."
echo ""
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Check if running as root
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}Error: Please run as root (or use sudo)${NC}"
exit 1
fi
# Load OS information
if [[ ! -f /etc/os-release ]]; then
echo -e "${RED}Error: Cannot detect OS. /etc/os-release not found.${NC}"
exit 1
fi
. /etc/os-release
# Check for supported OS
echo "🔍 Checking OS compatibility..."
if [[ "$ID" == "ubuntu" ]]; then
case "$VERSION_ID" in
22.04|24.04)
echo -e "${GREEN}✓ Ubuntu $VERSION_ID LTS detected${NC}"
;;
*)
echo -e "${RED}Error: Ubuntu $VERSION_ID is not an LTS release.${NC}"
echo "Supported: 22.04, 24.04"
exit 1
;;
esac
elif [[ "$ID" == "debian" ]]; then
case "$VERSION_CODENAME" in
bookworm|trixie)
echo -e "${GREEN}✓ Debian $VERSION_CODENAME detected${NC}"
;;
*)
echo -e "${RED}Error: Debian $VERSION_CODENAME is not a stable release.${NC}"
echo "Supported: bookworm (12), trixie (13)"
exit 1
;;
esac
else
echo -e "${RED}Error: Unsupported OS '$ID'.${NC}"
echo "VPS Armor only supports Ubuntu LTS and Debian Stable."
exit 1
fi
# Update system
echo ""
echo "📦 Updating system packages..."
apt-get update -qq
DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade -y -qq
# Install security packages
echo ""
echo "🔧 Installing security tools..."
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
unattended-upgrades \
apt-listchanges \
fail2ban \
ufw
# Configure fail2ban
echo ""
echo "🚫 Configuring fail2ban..."
systemctl enable fail2ban --quiet
systemctl start fail2ban
# Configure UFW
echo ""
echo "🧱 Setting up firewall (UFW)..."
if ufw status | grep -q "^Status: active"; then
ufw allow 22/tcp comment 'SSH' > /dev/null
ufw allow 80/tcp comment 'HTTP' > /dev/null
ufw allow 443/tcp comment 'HTTPS' > /dev/null
echo -e "${GREEN}✓ UFW already active. Existing rules preserved.${NC}"
else
ufw default deny incoming > /dev/null
ufw default allow outgoing > /dev/null
ufw allow 22/tcp comment 'SSH' > /dev/null
ufw allow 80/tcp comment 'HTTP' > /dev/null
ufw allow 443/tcp comment 'HTTPS' > /dev/null
ufw --force enable > /dev/null
echo -e "${GREEN}✓ Firewall enabled. Allowed ports: 22, 80, 443${NC}"
fi
# Summary
echo ""
echo "=========================================="
echo -e "${GREEN}🛡️ VPS Armor complete!${NC}"
echo "=========================================="
echo ""
echo "Your server now has:"
echo " ✓ All packages updated"
echo " ✓ Unattended upgrades installed (distro defaults)"
echo " ✓ Fail2ban protecting SSH"
echo " ✓ UFW firewall active (existing rules preserved)"
echo ""
echo -e "${YELLOW}Note: If you need additional ports, run:${NC}"
echo " ufw allow <port>/tcp"
echo ""
echo "Stay safe out there! 🚀"
⚠️ Heads up
- By default, UFW will block other ports. If you're running services on other ports (like 3000 for Node.js) and you need to access them directly from outside (not through a reverse proxy), open them with:
ufw allow 3000/tcp - Test on a fresh VPS first. We've tested this, but your setup might be... unique.
- This is baseline security. It's a great start, not a complete solution. Consider SSH keys, Docker security, etc.
License
BSD 3-Clause License
Copyright (c) 2024-2025, VPS Armor Contributors
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.