2026-03-30 · HetznerLinuxSecurityDevOps

Hetzner Production-Ready Server Checklist (Ubuntu 22.04)

A fresh Hetzner server has no firewall, password SSH enabled, no fail2ban, and no log rotation. Here is everything to do before you put real traffic on it.

Hetzner Cloud is one of the best-value Linux VPS providers in 2026. The hardware is good, the network is fast, and the pricing is a fraction of AWS or GCP for comparable specs. The default setup is not production-ready. Here's what to do before you put real traffic on it.

1. Firewall — default is wide open

A fresh Hetzner server has no firewall rules. Every port is accessible from the internet. The first thing to do after SSH access is confirmed:

sudo apt update && sudo apt install ufw -y

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

sudo ufw status verbose

Docker users: UFW does not protect Docker container ports. Bind containers to 127.0.0.1 — use 127.0.0.1:PORT:PORT in docker-compose.yml. See the Docker UFW bypass fix.

2. SSH hardening

# Disable password authentication (key-only):
sudo sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config

# Disable root login:
sudo sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config

sudo systemctl restart sshd

Before disabling password auth: confirm your SSH key works in a second terminal session. Locking yourself out of a Hetzner server requires a rescue boot.

3. fail2ban

sudo apt install fail2ban -y

# Create local config (never edit jail.conf directly):
sudo tee /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 3
backend  = systemd

[sshd]
enabled  = true
port     = ssh
maxretry = 3
bantime  = 24h

[recidive]
enabled  = true
logpath  = /var/log/fail2ban.log
banaction = %(banaction_allports)s
bantime  = 1w
findtime = 1d
maxretry = 5
EOF

sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd

4. SSL certificate

# Install certbot (snap method — recommended on Ubuntu 22.04):
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

# Obtain certificate (Nginx):
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

# Verify auto-renewal timer:
sudo systemctl status snap.certbot.renew.timer

# Test renewal:
sudo certbot renew --dry-run

5. Docker setup (if using containers)

# Install Docker:
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER

# Critical: bind all container ports to localhost:
# In docker-compose.yml use:
# ports:
#   - "127.0.0.1:PORT:PORT"
# NOT:
#   - "PORT:PORT"

# Set default log rotation in /etc/docker/daemon.json:
sudo tee /etc/docker/daemon.json << 'EOF'
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}
EOF

sudo systemctl restart docker

6. Unattended security updates

sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure -pmedium unattended-upgrades

# Verify it's enabled:
cat /etc/apt/apt.conf.d/20auto-upgrades

Should show APT::Periodic::Unattended-Upgrade "1";.

7. Swap (for low-RAM servers)

Hetzner's CX11 (2GB RAM) and CX21 (4GB RAM) servers can run out of memory under load. Add swap as a safety net:

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

8. Basic monitoring

# Check current resource usage:
htop
df -h
free -h

# Watch Nginx error log:
sudo tail -f /var/log/nginx/error.log

# Watch fail2ban:
sudo fail2ban-client status
sudo journalctl -u fail2ban --since "1 hour ago"

Production-ready checklist

Audit your Hetzner server config — firewall rules, Docker port exposure, SSL expiry, and reverse proxy setup.

Related Hetzner guides