Having a server at home has an obvious weak point: if the power goes out, the router fails, or the disk dies, your website disappears. The solution is to have a replica in the cloud ready to activate in minutes.
The architecture#
[servidor-casa] → rsync cada 6h → [VPS réplica]
servicios activos réplica en espera
TTL DNS: 5 min Uptime Kuma vigilandoThe home server pushes content to the VPS every 6 hours. If the server goes down, I change the DNS and in 5 minutes the VPS serves the site.
Why push and not pull?#
The home server is behind a residential router (Digi). The router only has ports 80 and 443 open. The VPS cannot connect via SSH to the home server directly.
The solution: the home server pushes to the VPS (it has free outgoing SSH), the VPS only receives.
VPS preparation#
The VPS (Debian 12, 2 CPUs, 4GB RAM, 30GB disk) already had Docker installed. First, security:
# UFW: solo lo necesario
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
# Fail2ban SSH
sudo apt-get install -y fail2ban# /etc/fail2ban/jail.local
[DEFAULT]
bantime = 7d
findtime = 1h
maxretry = 3
ignoreip = 127.0.0.1/8 <IP_PUBLICA_CASA>
[sshd]
enabled = true# SSH: solo clave pública
sudo sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl reload sshdTraefik as reverse proxy#
Same Traefik v2.11 as on the home server, with automatic Let’s Encrypt:
services:
traefik:
image: traefik:v2.11
command:
- --providers.docker=true
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --certificatesresolvers.letsencrypt.acme.email=tu@email.com
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
ports:
- "80:80"
- "443:443"Uptime Kuma: external monitoring#
Uptime Kuma monitors the home server from outside. If it doesn’t respond, immediate email alert:
uptime-kuma:
image: louislam/uptime-kuma:1
volumes:
- ./data:/app/data
labels:
- traefik.enable=true
- traefik.http.routers.uptime.rule=Host(`uptime.serviciosrogeliowar.com`)
- traefik.http.routers.uptime.entrypoints=websecure
- traefik.http.routers.uptime.tls.certresolver=letsencryptStandby replicas#
The web and blog services are running on the VPS but with traefik.enable=false — Traefik ignores them, they’re not accessible from the internet. They only activate in an emergency:
web-replica:
image: nginx:alpine
labels:
- traefik.enable=false # ← cambiar a true en emergenciaRsync script from the home server#
#!/bin/bash
# ~/infra/sync-to-vps.sh
VPS="usuario@<IP_PUBLICA_VPS>"
SSH_KEY="~/.ssh/id_ed25519"
rsync -az --delete \
-e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" \
~/infra/blog/public/ \
${VPS}:~/infra/blog/public/
rsync -az --delete \
-e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" \
~/infra/web/html/ \
${VPS}:~/infra/web/html/Crontab on the home server:
0 */6 * * * ~/infra/sync-to-vps.shEmergency failover procedure#
When the home server goes down:
- Edit on the VPS: change
traefik.enable=falsetotraefik.enable=truein web and blog docker compose up -din each directory- In the DNS panel, change the A record of your domain from the home server IP to the VPS IP
- With a 5-minute TTL, in less than 10 minutes the site is back
When the home server recovers, reverse process: restore DNS, change back to traefik.enable=false on the VPS.
Result#
- Uptime Kuma monitoring from outside at
uptime.serviciosrogeliowar.com - rsync automatic every 6 hours — maximum 6 hours of content lost in a failure
- Recovery time (RTO): ~5 minutes
- Maximum data loss (RPO): ~6 hours
- Additional cost: just the VPS (I already had it)
For a home server, this architecture is more than sufficient.
Recommended equipment#
- Raspberry Pi 3 B+ — Lightweight low-power server to start your homelab
- Raspberry Pi 4 (4GB) — The perfect foundation for homelab, Docker and monitoring
Affiliate links. No extra cost to you.