How to Set Up a Hetzner VPS: Complete Guide

Tested on: Hetzner Cloud CX22 · Ubuntu 24.04 LTS · Debian 12 — Last updated: June 2026
Hetzner Cloud delivers genuinely competitive infrastructure at prices that make most other cloud providers hard to justify for European workloads. A CX22 (2 vCPU, 4 GB RAM, 40 GB NVMe SSD) runs €3.99/month — roughly a third of DigitalOcean's equivalent droplet. This guide walks through every step: provisioning, securing, and running real applications on a Hetzner VPS, including firewall configuration, SSL, Docker, persistent volumes, and the hcloud CLI.
Hostinger VPS — From €3.99/month
If you want a cheaper starting point, Hostinger VPS offers KVM virtualization with full root access at a lower price than most competitors. Runs Ubuntu 24.04, Debian 12, AlmaLinux and CentOS out of the box — good fit for learning, side projects, or low-traffic servers.
- KVM virtualization — dedicated resources, full root access
- 1 vCPU / 4 GB RAM / 50 GB NVMe SSD from €3.99/month
- Ubuntu 24.04, Debian 12, CentOS, AlmaLinux supported
- Weekly automated backups included
- 1 Gbps network bandwidth
Affiliate disclosure: we earn a small commission if you sign up — at no extra cost to you.
Prerequisites
- A Hetzner Cloud account (hetzner.com/cloud) with a verified payment method
- An SSH key pair on your local machine (generated below if you need one)
- A domain name pointed at your server IP (required only for the SSL section)
- Basic familiarity with Linux terminal and SSH
Pricing at a Glance
Before provisioning anything, understand what you're getting. These are current Hetzner Cloud prices versus the competition for shared-CPU tiers:
| Provider | 2 vCPU / 4 GB RAM | 4 vCPU / 8 GB RAM | Regions |
|---|---|---|---|
| Hetzner | €3.99/month | €7.99/month | EU / US-East / Singapore |
| DigitalOcean | $24/month | $48/month | Global |
| Vultr | $20/month | $40/month | Global |
| Linode/Akamai | $12/month | $24/month | Global |
| AWS t3.small | ~$17/month | ~$34/month (t3.large) | Global |
For EU-based applications, the choice is straightforward. Hetzner's US-East (Ashburn, VA) datacenter is viable for US audiences too — latency is competitive with Linode and Vultr from the US East Coast. If you need Southeast Asia or South America coverage, you'll need to supplement with another provider for those regions.
Server type guidance: CX series uses shared AMD vCPUs — fine for most web apps, development servers, and self-hosted tools. CCX series (dedicated vCPU, from €12.99/month) is for workloads that need consistent CPU performance without contention from neighbours. For AI inference, look at Hetzner's GPU-enabled instances (GEX series).
Add Your SSH Key to Hetzner
Add your SSH public key to Hetzner before creating a server — you can then inject it automatically at provisioning time.
# Generate an Ed25519 key if you don't have one:
ssh-keygen -t ed25519 -C "hetzner-key" -f ~/.ssh/hetzner_ed25519
# Display your public key to copy:
cat ~/.ssh/hetzner_ed25519.pubIn the Hetzner Cloud Console: navigate to your project → Security → SSH Keys → Add SSH Key. Paste the output of the cat command above and give the key a recognisable name.
If you use multiple keys or machines, add them all now. You can select multiple keys at server creation time.
Create Your Server
Click Create Server in the Hetzner console and configure the following:
- Location: Falkenstein (fsn1) or Nuremberg (nbg1) for EU; Helsinki (hel1) for Finland/Nordic; Ashburn (ash) for US East; Singapore (sin) for Asia-Pacific.
- Image: Ubuntu 24.04 (recommended). Debian 12 is equally well-supported. Rocky Linux 9 if you need RHEL compatibility.
- Type: CX22 for most starting points. Scale up once you understand your actual resource usage.
- Networking: Leave IPv4 and IPv6 enabled. Note that a public IPv4 address now adds €0.001/hour (~€0.72/month) — this is standard across cloud providers now.
- SSH Key: Select the key you just added.
- Name: Something descriptive. You'll reference this name in the
hcloudCLI.
Click Create & Buy Now. The server is running within 15–20 seconds.
First Login and System Setup
Grab the public IP from the server overview page and SSH in as root:
ssh -i ~/.ssh/hetzner_ed25519 root@YOUR_SERVER_IPRun this setup sequence immediately after first login:
# Update all packages
apt update && apt full-upgrade -y
# Set your timezone
timedatectl set-timezone Europe/Berlin
# List available timezones: timedatectl list-timezones | grep Europe
# Set a proper hostname
hostnamectl set-hostname myserver
# Create a non-root deployment user
adduser deploy
usermod -aG sudo deploy
# Copy root's SSH authorized_keys to the new user
rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy
# Verify you can log in as deploy in a new terminal before continuing:
# ssh -i ~/.ssh/hetzner_ed25519 deploy@YOUR_SERVER_IP
# sudo apt update ← confirm sudo worksDon't close your root session until you've confirmed the deploy user can SSH in and use sudo. Locking yourself out at this stage means reaching for the Rescue System.
SSH Hardening
Edit the SSH daemon configuration to disable root login and password authentication:
sudo nano /etc/ssh/sshd_configSet or confirm these values:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
LoginGraceTime 20
X11Forwarding no# Validate the config before restarting
sudo sshd -t
# Restart SSH (Ubuntu 24.04 uses ssh.service)
sudo systemctl restart ssh
# Install and enable fail2ban
sudo apt install fail2ban -y
sudo systemctl enable --now fail2ban
# Check the SSH jail is active
sudo fail2ban-client status sshdExpected output from the fail2ban status check:
Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 3
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 0
|- Total banned: 0
`- Banned IP list:Configure the Hetzner Cloud Firewall
The Hetzner Cloud firewall operates at the datacenter network level — packets matching deny rules never reach your server. This is more efficient than a host-based firewall alone, and works even if your server's OS firewall is misconfigured.
In the console: Firewall → Create Firewall. Configure these inbound rules:
| Protocol | Port | Source | Purpose |
|---|---|---|---|
| TCP | 22 | Your IP only (or 0.0.0.0/0) | SSH |
| TCP | 80 | 0.0.0.0/0, ::/0 | HTTP |
| TCP | 443 | 0.0.0.0/0, ::/0 | HTTPS |
Restrict SSH to your own IP if you have a static IP — this dramatically reduces brute force noise. Leave outbound rules set to allow all. Apply the firewall to your server via the Applied to section.
Add a second layer with UFW on the server itself:
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 verboseInstall Nginx and Configure Free SSL
Point your domain's DNS A record to your server IP before running Certbot — Let's Encrypt needs to verify domain ownership over HTTP.
# Install Nginx
sudo apt install nginx -y
sudo systemctl enable --now nginx
# Verify it's serving: curl http://YOUR_SERVER_IP
# Install Certbot with Nginx plugin
sudo apt install certbot python3-certbot-nginx -y
# Obtain and install a certificate
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# Certbot installs a systemd timer for auto-renewal — verify it:
sudo systemctl status certbot.timer
sudo certbot renew --dry-runCertbot modifies your Nginx config to add SSL, redirect HTTP to HTTPS, and configure modern TLS settings. Check the result:
sudo nginx -t && sudo systemctl reload nginxRun Applications with Docker
Docker is the most practical way to deploy applications on a single VPS without dependency conflicts.
# Install Docker using the official convenience script
curl -fsSL https://get.docker.com | bash
# Add your deploy user to the docker group
sudo usermod -aG docker deploy
# Apply group membership without logging out
newgrp docker
# Verify
docker run --rm hello-worldExample: running Ollama for local LLM inference. On a CPU-only CX22, small quantised models (1B–3B parameters) run at usable speeds. For larger models, use a GPU instance.
docker run -d
--name ollama
-p 127.0.0.1:11434:11434
-v ollama_data:/root/.ollama
--restart unless-stopped
ollama/ollama
# Pull a model inside the container
docker exec ollama ollama pull llama3.2:1b
# Access from your local machine via SSH tunnel:
# ssh -L 11434:localhost:11434 deploy@YOUR_SERVER_IP
# Then locally:
curl http://localhost:11434/api/generate
-d '{"model": "llama3.2:1b", "prompt": "Explain iptables in one paragraph.", "stream": false}'Add Persistent Storage with Volumes
Hetzner Volumes are network-attached NVMe SSDs — €0.052/GB/month (100 GB ≈ €5.20/month). They survive server deletion and can be moved between servers in the same datacenter.
Create a volume in the console: Volumes → Create Volume. Attach it to your server. Then on the server:
# Identify the volume device
ls -la /dev/disk/by-id/ | grep HC_Volume
# Format on first use only — this destroys any existing data
sudo mkfs.ext4 -L data /dev/disk/by-id/scsi-0HC_Volume_XXXXXXXX
# Create mount point
sudo mkdir -p /mnt/data
# Mount it
sudo mount /dev/disk/by-id/scsi-0HC_Volume_XXXXXXXX /mnt/data
# Persist across reboots — add to /etc/fstab
echo '/dev/disk/by-id/scsi-0HC_Volume_XXXXXXXX /mnt/data ext4 defaults,nofail 0 2'
| sudo tee -a /etc/fstab
# Verify fstab entry is valid
sudo findmnt --verifyThe nofail mount option prevents boot failures if the volume is detached. Always use it with network-attached storage.
Backups and Snapshots
Automated backups — enable in the server's settings page. Cost is 20% of the server price: a €3.99 CX22 adds €0.80/month for daily backups with 7-day retention. This is the simplest protection for production workloads.
Manual snapshots — €0.01/GB/month. A 40 GB server snapshot costs €0.40/month. Take a snapshot before major changes:
# Via hcloud CLI (covered below):
hcloud server create-image --type snapshot --description "before-nginx-upgrade" myserverSnapshots are stored independently of the server — deleting the server does not delete its snapshots.
Manage Everything with the hcloud CLI
The hcloud CLI lets you script and automate your entire infrastructure. Install it on your local machine:
# Linux (amd64):
wget https://github.com/hetznercloud/cli/releases/latest/download/hcloud-linux-amd64.tar.gz
tar -xzf hcloud-linux-amd64.tar.gz
sudo mv hcloud /usr/local/bin/
hcloud version
# macOS:
brew install hcloudGenerate an API token: Hetzner Console → your project → Security → API Tokens → Generate API Token (Read & Write). Then configure the CLI:
hcloud context create myproject
# Paste your API token when prompted
# Useful commands:
hcloud server list
hcloud server describe myserver
hcloud server reboot myserver
hcloud server poweroff myserver
hcloud server ssh myserver # SSH directly by name
# Create a server entirely from CLI:
hcloud server create
--name webserver-01
