Immich vom VPS auf den Homeserver umziehen – mit WireGuard als sicherem Tunnel
Meine Fotoverwaltung läuft seit einiger Zeit als Immich-Instanz auf einem gemieteten VPS. Das hat funktioniert, aber 450 GB Fotos auf fremder Hardware zu hosten fühlt sich nicht ideal an – und die monatlichen Kosten für einen VPS mit ausreichend Speicher summieren sich. Die Lösung: Immich zieht auf den Homeserver um.
Damit das ohne Port-Forwarding am Heimrouter klappt und die Heimnetz-IP privat bleibt, kommt ein kleiner VPS als reines Relais zum Einsatz – verbunden per WireGuard-Tunnel. Der VPS terminiert TLS und leitet den Traffic weiter; die eigentliche Arbeit macht der Homeserver.
Die Zielarchitektur
Internet
│
▼
[VPS] – DNS, TLS-Terminierung, nginx
│ immich.pixelgalaxy.net → VPS-IP
│ nginx → proxy_pass → 10.10.0.2:2283
│
│ WireGuard-Tunnel (UDP 51820)
│ VPS: 10.10.0.1
│ Homeserver: 10.10.0.2
▼
[Homeserver] – Immich Docker Stack
└─ immich_server :2283
└─ immich_postgres
└─ immich_machine_learning
└─ immich_redis
Warum dieser Aufbau?
- Die Heimnetz-IP bleibt vollständig privat
- Kein Port-Forwarding am Router nötig (außer WireGuard UDP 51820)
- Der VPS kann sehr klein bleiben – 1 vCPU, 2 GB RAM reichen (IONOS, ~1 €/Monat)
- TLS-Zertifikat und DNS liegen auf dem VPS, nicht am Homeserver
Voraussetzungen
Homeserver:
- Mindestens 600 GB freier Speicher (450 GB Library + Wachstum)
- Mindestens 8 GB RAM (Machine Learning benötigt 2–4 GB)
- Docker + Docker Compose installiert
- Debian 12 oder Ubuntu 24.04 empfohlen
- Stabile Internetverbindung – die Upload-Bandbreite bestimmt, wie lange der initiale rsync dauert
VPS:
- Debian 12, öffentliche IPv4
- Ports 80, 443 und 51820 (UDP) offen
Phase 1: Homeserver vorbereiten
Docker installieren
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
Immich-Verzeichnis anlegen und Konfiguration übernehmen
mkdir -p /opt/docker-apps/immich
cd /opt/docker-apps/immich
Die docker-compose.yml und .env vom alten Server kopieren:
# Auf pixelgalaxy.net ausführen:
scp /opt/docker-apps/immich/docker-compose.yml user@homeserver:/opt/docker-apps/immich/
scp /opt/docker-apps/immich/.env user@homeserver:/opt/docker-apps/immich/
In der .env auf dem Homeserver zwei Dinge sicherstellen:
UPLOAD_LOCATION=./library
DB_DATA_LOCATION=./postgres
IMMICH_VERSION=v2.7.4 # Exakte Version pinnen – nicht "v2"!
TZ=Europe/Berlin
Wichtig: Die Datenbankpasswörter aus der alten .env exakt übernehmen. Und die Version pinnen – niemals während eines Umzugs ein Update durchführen.Immich jetzt noch nicht starten – erst nach dem Daten-Transfer.
Phase 2: Initiales rsync der Library (im laufenden Betrieb)
450 GB bewegt man nicht mal eben. Dieser Schritt läuft im Hintergrund, während Immich auf dem alten Server völlig normal weiterläuft.
# SSH-Key hinterlegen:
ssh-copy-id user@homeserver
# rsync starten – in screen oder tmux, da es Stunden dauert:
rsync -avz --progress \
/opt/docker-apps/immich/library/ \
user@homeserver:/opt/docker-apps/immich/library/
Immich legt Fotos unter unveränderlichen Pfaden ab (/upload/YYYY/MM/...), daher ist ein Live-rsync unkritisch – bestehende Dateien werden nicht angefasst.
Phase 3: VPS mit WireGuard und nginx einrichten
WireGuard-Schlüssel erzeugen
Auf dem VPS:
apt update && apt install -y wireguard
wg genkey | tee /etc/wireguard/vps_private.key | wg pubkey > /etc/wireguard/vps_public.key
chmod 600 /etc/wireguard/vps_private.key
Auf dem Homeserver:
apt update && apt install -y wireguard
wg genkey | tee /etc/wireguard/home_private.key | wg pubkey > /etc/wireguard/home_public.key
chmod 600 /etc/wireguard/home_private.key
WireGuard konfigurieren
VPS – /etc/wireguard/wg0.conf:
[Interface]
PrivateKey = <VPS_PRIVATE_KEY>
Address = 10.10.0.1/24
ListenPort = 51820
[Peer]
# Homeserver
PublicKey = <HOME_PUBLIC_KEY>
AllowedIPs = 10.10.0.2/32
Homeserver – /etc/wireguard/wg0.conf:
[Interface]
PrivateKey = <HOME_PRIVATE_KEY>
Address = 10.10.0.2/24
[Peer]
# VPS
PublicKey = <VPS_PUBLIC_KEY>
Endpoint = <VPS_IP>:51820
AllowedIPs = 10.10.0.1/32
PersistentKeepalive = 25
PersistentKeepalive = 25 ist entscheidend: Der Homeserver sendet alle 25 Sekunden ein Keepalive-Paket zum VPS, hält damit die NAT-Verbindung offen und ermöglicht es dem VPS, Antworten zurückzuschicken – ganz ohne Port-Forwarding am Heimrouter.WireGuard starten und testen
# Auf beiden Seiten:
systemctl enable --now wg-quick@wg0
# Verbindung prüfen:
ping 10.10.0.2 # auf dem VPS
ping 10.10.0.1 # auf dem Homeserver
nginx auf dem VPS einrichten
apt install -y nginx certbot python3-certbot-nginx
/etc/nginx/sites-available/immich:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name immich.pixelgalaxy.net;
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl http2;
server_name immich.pixelgalaxy.net;
ssl_certificate /etc/letsencrypt/live/immich.pixelgalaxy.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/immich.pixelgalaxy.net/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options SAMEORIGIN always;
client_max_body_size 10G;
location / {
proxy_pass http://10.10.0.2:2283;
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_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
}
ln -s /etc/nginx/sites-available/immich /etc/nginx/sites-enabled/
nginx -t
Das Zertifikat kommt in Phase 5, nachdem der DNS umgestellt ist.
Phase 4: Maintenance Window – der eigentliche Umzug
Dieser Schritt erfordert kurze Downtime von etwa 15–30 Minuten.
Immich auf dem alten Server stoppen
cd /opt/docker-apps/immich
docker compose down
Finales rsync – nur noch das Delta
rsync -avz --progress --delete \
/opt/docker-apps/immich/library/ \
user@homeserver:/opt/docker-apps/immich/library/
--delete entfernt Dateien auf dem Homeserver, die auf dem Quellserver zwischenzeitlich gelöscht wurden.
Datenbank-Dump erstellen und übertragen
# Dump erstellen:
docker run --rm \
-v /opt/docker-apps/immich/postgres:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=$(grep DB_PASSWORD /opt/docker-apps/immich/.env | cut -d= -f2) \
ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0 \
pg_dumpall -U postgres > /tmp/immich-db-dump.sql
# Übertragen:
scp /tmp/immich-db-dump.sql user@homeserver:/tmp/
Achtung: Unbedingt dasselbe Postgres-Image wie im Compose-Stack verwenden. Immich verwendet eingebettete Extensions (pgvecto.rs,pgvector) – ein Standard-postgres:14kann den Dump nicht einspielen.
Datenbank auf dem Homeserver einspielen
cd /opt/docker-apps/immich
docker compose up -d database
sleep 15
docker exec -i immich_postgres psql -U postgres < /tmp/immich-db-dump.sql
docker compose down
Immich auf dem Homeserver starten
docker compose up -d
docker compose logs -f immich-server
Warten bis der Container healthy meldet:
docker ps --filter "name=immich_server" --format "{{.Status}}"
Funktionstest über WireGuard
Bevor der DNS umgestellt wird, direkt gegen den Homeserver testen:
# Auf dem VPS:
curl -sk http://10.10.0.2:2283/api/server/ping
# Erwartete Antwort: {"res":"pong"}
Phase 5: DNS umstellen und TLS-Zertifikat holen
DNS-TTL vorab reduzieren
Mindestens 24 Stunden vor der Umstellung den TTL des A-Records für immich.pixelgalaxy.net auf 60 Sekunden senken. Nach dem Umzug wieder auf einen normalen Wert (z. B. 3600) erhöhen.
A-Record auf die VPS-IP setzen
immich.pixelgalaxy.net. A <VPS_PUBLIC_IP>
Let's Encrypt-Zertifikat beantragen
# Auf dem VPS – sobald DNS propagiert ist:
certbot --nginx -d immich.pixelgalaxy.net
nginx -t && systemctl reload nginx
Abschließender Browser-Test
- Login funktioniert
- Fotos werden vollständig angezeigt
- Upload eines Testfotos klappt
- Mobile App verbindet sich
- HTTPS-Zertifikat ist gültig und korrekt ausgestellt
Phase 6: Aufräumen auf dem alten Server
Erst nach mehreren Tagen stabilem Betrieb auf dem Homeserver.
# nginx-Vhost entfernen:
sudo rm /etc/nginx/conf.d/immich.pixelgalaxy.net.conf
sudo rm -rf /etc/nginx/conf.d/immich.pixelgalaxy.net.d/
sudo nginx -t && sudo systemctl reload nginx
# Immich-Daten löschen (450 GB freigeben):
sudo docker volume rm immich_model-cache 2>/dev/null
sudo rm -rf /opt/docker-apps/immich/
Zeitplan (Richtwerte)
| Schritt | Dauer | Downtime? |
|---|---|---|
| Phase 1: Homeserver vorbereiten | 1–2 Stunden | Nein |
| Phase 2: Initiales rsync (450 GB) | 4–24 Stunden | Nein |
| Phase 3: VPS + WireGuard + nginx | 1–2 Stunden | Nein |
| Phase 4: Finaler Umzug | 15–30 Minuten | Ja |
| Phase 5: DNS + TLS | 5–30 Minuten + Propagation | Nein |
| Phase 6: Aufräumen | 10 Minuten | Nein |
Wichtige Hinweise auf einen Blick
Postgres-Image muss exakt übereinstimmen. Das Immich-eigene Postgres-Image enthält spezielle Extensions. Niemals ein Standard-postgres:14 für den Restore verwenden – das Image aus der docker-compose.yml muss identisch sein.
Immich-Version pinnen. Vor dem Umzug IMMICH_VERSION=v2.7.4 (oder die eigene aktuelle Version) in der .env fest eintragen. Floating Tags wie v2 sind während eines Umzugs ein Risiko.
Machine Learning Model Cache nicht manuell übertragen. Immich lädt die Modelle beim ersten Start automatisch herunter (~2–3 GB). Das verlangsamt den ersten Start etwas, ist aber kein Problem.
Backup vor Phase 4. Der Dump aus Schritt 4.3 zusammen mit der Library-Kopie auf dem Homeserver bildet ein vollständiges Backup. Sicherstellen, dass beides vorhanden ist, bevor der alte Server heruntergefahren wird.
Der Aufbau sollte nach dem Umzug stabil laufen. Der VPS kostet jetzt nur noch 1 €/Monat statt wie vorher als Vollhost – und die Fotos liegen auf eigener Hardware zu Hause.