Complete Guide: Setting Up a Secure Home Server Infrastructure
- Alexander Wahl
- Tutorials , Networking
- January 11, 2025
Table of Contents
This article is the result of my research dedicated to solving the problem of using a dynamic IP address when hosting a website or application on a home server.
This guide will walk you through creating a professional-grade home server infrastructure. We’ll set up everything from basic SSH security to advanced multi-server configurations. By following this guide, you’ll have a secure, remotely accessible home server system that’s both robust and expandable.
High level process
Let’s start with understanding the data flow of our setup:
Internet -> Cloudflare -> VPS (Reverse Proxy) -> SSH Tunnel -> Raspberry Pi
Prerequisites
Before we begin, make sure you have all these components ready:
- VPS with public IP (get 20 Euro from Hetzner right now check the link)
- Raspberry Pi
- Domain name
- Cloudflare account
- Local machine (laptop/desktop)
DNS Configuration (Cloudflare)
Now we’ll set up our domain routing through Cloudflare. I use Cloudflare not only because they provide an additional layer of protection but also because they offer free DNS record management.
This provides an additional layer of security and prepares us for SSL setup:
- Main domain:
- Type: A
- Name: your_domain
- Content: vps_ip
- Proxy: Enabled (orange cloud)
- SSH subdomain:
- Type: A
- Name: ssh
- Content: vps_ip
- Proxy: Disabled (gray cloud)
Note: Wait 5-10 minutes for DNS propagation
VPS
Initial SSH Key Setup (Local Machine -> VPS)
First, let’s secure our VPS access. This step is crucial for security as it eliminates password-based authentication:
# Generate SSH key pair
ssh-keygen -t rsa -b 4096 -f ~/.ssh/vps_key
# Copy to VPS (one-time password use)
ssh-copy-id -i ~/.ssh/vps_key.pub username@vps_ip
Now let’s configure SSH for easier access:
# Configure SSH
nano ~/.ssh/config
Add these settings to your SSH config:
Host vps
HostName vps_ip
User username
Port 22
IdentityFile ~/.ssh/vps_key
You can test your connection with:
ssh vps
VPS Base Setup
Next, we’ll prepare our VPS with all necessary security measures and software. This forms the foundation of our secure infrastructure:
# Update system
sudo apt update && sudo apt upgrade -y
# Install essential packages
sudo apt install nginx certbot python3-certbot-nginx ufw -y
# Configure firewall
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 80/tcp # HTTP for certificate validation
sudo ufw allow 443/tcp # HTTPS
sudo ufw allow 22/tcp # SSH
sudo ufw allow 8080/tcp # Reverse proxy
sudo ufw allow 2222/tcp # SSH tunnel
sudo ufw enable
SSL Certificate Setup (VPS)
With DNS configured, we can now secure our connection with SSL certificates:
# Get certificate (domain must be pointing to VPS)
sudo certbot --nginx -d your_domain -d www.your_domain
# Verify auto-renewal
sudo certbot renew --dry-run
Nginx Configuration (VPS)
Our next step is setting up Nginx as a reverse proxy. This configuration will handle all incoming web traffic:
sudo nano /etc/nginx/sites-available/your_domain
Add this comprehensive Nginx configuration:
# HTTPS Server
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name your_domain;
# SSL configuration (certbot added these)
ssl_certificate /etc/letsencrypt/live/your_domain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your_domain/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Proxy settings
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
}
# HTTP to HTTPS redirect
server {
listen 80;
listen [::]:80;
server_name your_domain www.your_domain;
return 301 https://$host$request_uri;
}
Stream Configuration
sudo nano /etc/nginx/nginx.conf
Add a stream configuration path:
include /etc/nginx/stream.conf;
to /etc/nginx/nginx.conf direct after the block http, it will look like this one:
http {
...
...
include /etc/nginx/sites-enabled/*;
}
include /etc/nginx/stream.conf;
#mail {
# # See sample authentication script at:
Create a stream configuration file with the content:
stream {
# First server
upstream raspi1 {
server 127.0.0.1:2222;
}
server {
listen 9222;
proxy_pass raspi1;
}
# Second server
# upstream raspi2 {
# server 127.0.0.1:2223;
# }
# server {
# listen 9223;
# proxy_pass raspi2;
# }
}
you can always add a new server and increase the port number by 1 for exmaple.
Activate the configuration:
sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
SSH Server Configuration (VPS)
For secure and stable SSH tunneling, we need to modify the SSH server configuration. These settings ensure reliable connections:
sudo nano /etc/ssh/sshd_config
Add or modify these essential settings:
GatewayPorts yes # Allow remote port forwarding
AllowTcpForwarding yes # Enable tunneling
TCPKeepAlive yes # Keep connections alive
ClientAliveInterval 15 # Send keepalive every 15 seconds
ClientAliveCountMax 3 # Allow 3 missed keepalives
Apply the changes:
sudo systemctl restart sshd
Raspberry Pi
Now we’ll set up our Raspberry Pi. This step ensures that it’s ready for secure and stable SSH tunneling:
Initial Setup
First, update the system and install necessary packages:
sudo apt update && sudo apt upgrade -y
sudo apt install nginx ufw -y
# Basic firewall
sudo ufw allow 80/tcp
sudo ufw allow 22/tcp
sudo ufw enable
SSH Key for VPS Connection
Next, we’ll create a secure tunnel between the Raspberry Pi and VPS:
# Generate key
ssh-keygen -t rsa -b 4096 -f ~/.ssh/tunnel_key
# Copy to VPS
ssh-copy-id -i ~/.ssh/tunnel_key.pub username@vps_ip
Configure SSH for easy VPS access:
# Configure SSH
nano ~/.ssh/config
Add these settings:
Host vps
HostName vps_ip
User username
Port 22
IdentityFile ~/.ssh/tunnel_key
SSH Tunnel Service
Now we’ll create a persistent tunnel service. This is crucial for maintaining a stable connection:
sudo nano /etc/systemd/system/ssh-tunnel-ssh.service
Add this service configuration:
[Unit]
Description=SSH Reverse Tunnel for SSH access
After=network.target
StartLimitIntervalSec=0
[Service]
Environment="HOME=/home/your_username"
ExecStart=/usr/bin/ssh -N -R 0.0.0.0:2222:localhost:22 -i /home/your_username/.ssh/tunnel_key vps
Restart=always
RestartSec=10
Type=simple
User=your_username
StandardOutput=append:/var/log/ssh-tunnel.log
StandardError=append:/var/log/ssh-tunnel.log
[Install]
WantedBy=multi-user.target
Set up logging and start the service:
sudo touch /var/log/ssh-tunnel.log
sudo chown your_username:your_username /var/log/ssh-tunnel.log
# Enable and start service
sudo systemctl daemon-reload
sudo systemctl enable ssh-tunnel-ssh
sudo systemctl start ssh-tunnel-ssh
Nginx Setup on Raspberry Pi
Let’s configure the web server on your Raspberry Pi:
sudo nano /etc/nginx/sites-available/default
Add this configuration:
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm;
server_name _;
location / {
try_files $uri $uri/ =404;
}
}
Create a test page to verify everything works:
sudo nano /var/www/html/index.html
Add some test content:
<!DOCTYPE html>
<html>
<head>
<title>Raspberry Pi Web Server</title>
</head>
<body>
<h1>Hello from Raspberry Pi!</h1>
</body>
</html>
Set proper permissions and restart Nginx:
sudo chown -R www-data:www-data /var/www/html
sudo nginx -t
sudo systemctl restart nginx
Local Machine Setup
To make accessing your home server easy from anywhere, configure your local machine:
nano ~/.ssh/config
Add these settings:
Host homeserver
HostName ssh.your_domain
User your_username
Port 2222
IdentityFile ~/.ssh/id_rsa
ServerAliveInterval 15
ServerAliveCountMax 3
ConnectTimeout 10
TCPKeepAlive yes
Testing & Verification
After setting up all components, let’s verify everything works correctly. We’ll test each part of the system:
Web Access
First, test your HTTPS setup:
# Test HTTPS
curl -I https://your_domain
# Expected: HTTP/2 200
SSH Access
Verify SSH connectivity:
# Test SSH connection
ssh homeserver
# Expected: Successfully connects to Raspberry Pi
Service Status
Check all services are running properly:
# On VPS
sudo systemctl status nginx
sudo nginx -t
# On Raspberry Pi
sudo systemctl status ssh-tunnel-ssh
sudo tail -f /var/log/ssh-tunnel.log
Maintenance
Regular maintenance is crucial for keeping your server secure and running smoothly.
Regular Updates
Keep both systems updated:
# Both VPS and Raspberry Pi
sudo apt update && sudo apt upgrade -y
SSL Renewal
Check SSL certificate renewal:
# On VPS
sudo certbot renew --dry-run
Monitoring
Regularly check your logs for any issues:
# Check logs
sudo tail -f /var/log/nginx/error.log
sudo tail -f /var/log/auth.log
# Check tunnel
sudo systemctl status ssh-tunnel-ssh
Troubleshooting
Here’s how to handle common issues that might arise:
Connection Issues
- Verify tunnel:
# On Raspberry Pi
sudo systemctl restart ssh-tunnel-ssh
sudo tail -f /var/log/ssh-tunnel.log
- Check Nginx:
# On VPS
sudo nginx -t
sudo systemctl restart nginx
- SSL issues:
sudo certbot certificates
sudo certbot --force-renewal
By following these troubleshooting steps, you should be able to resolve most connection issues.
Security
Monitor login attempts:
sudo grep "Failed password" /var/log/auth.log
System Logging Setup (Raspberry Pi) Set up comprehensive logging:
sudo apt install rsyslog
sudo systemctl enable rsyslog
sudo systemctl start rsyslog
Monitor authentication logs:
# Monitor auth logs
sudo grep "Failed password" /var/log/auth.log
# or
sudo journalctl | grep "Failed password"
Check your firewall status:
sudo ufw status numbered
Verify SSL:
curl -vI https://your_domain
Adding Additional Server (Second Raspberry Pi)
When you’re ready to expand your setup with a second Raspberry Pi, follow these steps:
Cloudflare DNS Configuration
Add a new subdomain record:
Type: A
Name: subdomain (e.g., n8n)
Content: vps_ip
Proxy: Enabled (orange cloud)
VPS Configuration
- Get SSL certificate for the new subdomain:
sudo certbot --nginx -d subdomain.your_domain
- Create Nginx configuration:
sudo nano /etc/nginx/sites-available/subdomain.your_domain
Add this configuration:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name subdomain.your_domain;
ssl_certificate /etc/letsencrypt/live/subdomain.your_domain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/subdomain.your_domain/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://127.0.0.1:8081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
}
server {
listen 80;
listen [::]:80;
server_name subdomain.your_domain;
return 301 https://$host$request_uri;
}
SSH Tunnel Service (Enhanced Configuration)
Configure tunnel services for both Raspberry Pis:
First Raspberry Pi configuration:
[Unit]
Description=SSH Reverse Tunnel for SSH access
After=network.target
StartLimitIntervalSec=0
[Service]
Environment="HOME=/home/your_username"
ExecStart=/usr/bin/ssh -N \
-R 2222:localhost:22 \
-R 8080:localhost:80 \
-i /home/your_username/.ssh/id_rsa.private \
-o ServerAliveInterval=15 \
-o ServerAliveCountMax=3 \
-o ExitOnForwardFailure=yes \
-o TCPKeepAlive=yes \
-o ConnectTimeout=10 \
user@vps_ip
Restart=always
RestartSec=10
Type=simple
User=your_username
StandardOutput=append:/var/log/ssh-tunnel.log
StandardError=append:/var/log/ssh-tunnel.log
[Install]
WantedBy=multi-user.target
Second Raspberry Pi configuration:
[Unit]
Description=SSH Reverse Tunnel for SSH access
After=network.target
StartLimitIntervalSec=0
[Service]
Environment="HOME=/home/your_username"
ExecStart=/usr/bin/ssh -N \
-R 2223:localhost:22 \
-R 8081:localhost:80 \
-i /home/your_username/.ssh/id_rsa.private \
-o ServerAliveInterval=15 \
-o ServerAliveCountMax=3 \
-o ExitOnForwardFailure=yes \
-o TCPKeepAlive=yes \
-o ConnectTimeout=10 \
user@vps_ip
Restart=always
RestartSec=10
Type=simple
User=your_username
StandardOutput=append:/var/log/ssh-tunnel.log
StandardError=append:/var/log/ssh-tunnel.log
[Install]
WantedBy=multi-user.target
Reload and restart services on both Raspberry Pis:
sudo systemctl daemon-reload
sudo systemctl restart ssh-tunnel-ssh
Port Reference
Keep track of your ports:
- Raspberry Pi 1:
- SSH: 2222
- Web: 8080
- Raspberry Pi 2:
- SSH: 2223
- Web: 8081
Verification Steps
- Check tunnel status:
sudo systemctl status ssh-tunnel-ssh
- Verify ports on VPS:
sudo netstat -tulpn | grep '8080\|8081\|2222\|2223'
- Test web access:
curl -I https://your_domain
curl -I https://subdomain.your_domain
Important Notes
Each additional server needs unique ports for both SSH and web traffic
- Each subdomain needs its own SSL certificate
- Each Raspberry Pi needs its own tunnel configuration with unique ports
- Monitor logs on both servers and VPS for any issues
- Keep all configurations backed up
Troubleshooting SSL and Nginx Setup
Certbot and Nginx Configuration Issues If you encounter SSL or Nginx issues, follow these steps:
- Comment out stream configuration:
sudo nano /etc/nginx/nginx.conf
Find and comment out:
# include /etc/nginx/stream.conf;
- Get SSL certificate with Nginx stopped:
# Stop Nginx first
sudo systemctl stop nginx
# Get certificate in standalone mode
sudo certbot certonly --standalone -d subdomain.your_domain
# Start Nginx again
sudo systemctl start nginx
- Re-enable stream configuration:
sudo nano /etc/nginx/nginx.conf
Uncomment:
# include /etc/nginx/stream.conf;
- Test and restart Nginx:
sudo nginx -t
sudo systemctl restart nginx
Common Issues and Solutions
Check for port conflicts:
sudo netstat -tulpn | grep '80\|443\|2222\|2223'
Remember:
- If Certbot fails, try standalone mode first
- Always get SSL certificates before complex Nginx configurations
- Use nginx -t between configuration changes
Referal links
1. Get 20 Euro from Hetzner if you sign up right now here
As you can see, the prices are very low, and you can choose the cheapest option because the actual load will be on your home server, while the VPS will only serve as a router.