Ein frisch installierter VPS ist schnell verfügbar. Das heißt aber noch nicht, dass er sinnvoll oder sicher eingerichtet ist. In den ersten Minuten nach der Installation legst du fest, ob dein System später ruhig wartbar bleibt – oder ob du dir mit kleinen Versäumnissen dauerhaft Komplexität einhandelst.
Dieser Beitrag baut bewusst nur das Fundament: Debian 13 installieren, SSH per Schlüssel einrichten und eine saubere Linux-Basis herstellen. Keine Anwendungen, kein Docker, kein Webserver – das kommt in den nächsten Artikeln. Diese Basis soll später möglichst unverändert bleiben.
Am Ende hast du
- Debian 13 (Trixie) installiert und aktualisiert.
- Einen normalen Benutzer eingerichtet (Beispiel:
martin) mit UID 1000 - SSH-Zugriff ohne Passwort, nur per Schlüssel.
- Verstanden, was beim ersten SSH-Login passiert (Host-Key, Passphrase).
- Basispakete installiert, Zeitzone und Locale gesetzt
- SSH gehärtet, Firewall aktiviert, automatische Sicherheitsupdates konfiguriert
Voraussetzungen
-
VPS mit öffentlicher IPv4.
-
Zugriff auf das Provider-Panel (Installation, SSH-Key-Hinterlegung).
-
macOS, Linux oder Windows mit Terminal / SSH-Client.
1. SSH-Key lokal erstellen (ed25519)
SSH arbeitet mit einem Schlüsselpaar:
-
Private Key: bleibt auf deinem Rechner. Er ist dein Zugang.
-
Public Key: wird auf dem Server hinterlegt. Er erlaubt genau deinem Private Key den Login.
Das ist der entscheidende Unterschied zu Passwörtern: Der Server muss kein Passwort akzeptieren, das jemand erraten könnte. Er akzeptiert nur den Nachweis, dass du den passenden Schlüssel besitzt.
Key erzeugen
Auf deinem lokalen Rechner:
ssh-keygen -t ed25519 -a 100 -C "martin@homelab"
- Speicherort: ENTER drücken (Standard ist sinnvoll).
- Passphrase: unbedingt setzen
Warum eine Passphrase wichtig ist
Ein SSH-Key ohne Passphrase ist im schlimmsten Fall sofort nutzbar, wenn die Datei irgendwo kopiert wird – z. B. durch ein kompromittiertes Backup. Eine Passphrase ist die zweite Schutzschicht.
Danach existieren:
~/.ssh/id_ed25519(Private Key – niemals teilen!)~/.ssh/id_ed25519.pub(Public Key – dieser kommt auf den Server)
Check Prüfen ob beide Dateien existieren.
ls -l ~/.ssh/id_ed25519*
2. Public Key ≠ Fingerprint
Hier passieren die meisten Anfängerfehler, weil die Begriffe ähnlich wirken.
Fingerprint (nicht für Login geeignet)
Beispiel:
SHA256:abc123def456...
Das ist nur eine Prüfsumme zur Kontrolle. Damit loggst du dich nicht ein.
Public Key (das, was der Server braucht)
Den Public Key gibst du ins Provider-Panel oder kopierst ihn auf den Server:
cat ~/.ssh/id_ed25519.pub
Er beginnt typischerweise mit:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... martin@homelab
Merksatz
Ein SSH-Key beginnt mit
ssh-ed25519. Alles, was mitSHA256:beginnt, ist ein Fingerprint, kein Login-Key.
Check
Wenn deine Ausgabe mit ssh-ed25519 beginnt, hast du den richtigen Inhalt zum Hinterlegen.
3. Debian 13 installieren
Option A: Provider mit SSH-Key-Support (z. B. Netcup, Hetzner):
- Unter Medien Debian 13 auswählen
- Partitionen: eine große Partition für das Betriebssystem, die den kompletten freien Speicherplatz beinhaltet
- Hostname:
homelab - Zusätzlichen Benutzer erstellen aktivieren (Benutzername z. B.
martin, dazu ein Passwort wählen) - Deinen SSH-Key hinterlegen/auswählen
- Passwort-Login für SSH deaktivieren (falls angeboten)
- Keine Extras, keine Skripte
- Auf Weiter klicken
Option B: Provider ohne SSH-Key-Support
- Debian 13 (Trixie) installieren
- Per Root-Passwort einloggen (wird vom Provider generiert)
- Manuell User anlegen und SSH-Key kopieren (siehe unten)
Nach Installation: User anlegen (falls nicht vom Provider gemacht)
Falls dein Provider keinen User angelegt hat, auf dem Server als root:
# Als root einloggen
ssh root@<SERVER-IP>
# User erstellen (UID 1000 wird automatisch vergeben, wenn erster User)
adduser martin
# Sudo-Rechte geben
usermod -aG sudo martin
# SSH-Verzeichnis anlegen
mkdir -p /home/martin/.ssh
chmod 700 /home/martin/.ssh
Public Key kopieren – diesen Befehl führst du auf deinem lokalen Rechner aus:
cat ~/.ssh/id_ed25519.pub | ssh root@<SERVER-IP> "cat >> /home/martin/.ssh/authorized_keys"
Anschließend wieder als root auf dem Server – Rechte korrekt setzen:
chmod 600 /home/martin/.ssh/authorized_keys
chown -R martin:martin /home/martin/.ssh
Test – ausloggen und als martin einloggen:
exit
ssh martin@<SERVER-IP>
4. Erster SSH-Login – Meldungen verstehen
Von deinem Rechner aus:
ssh martin@<SERVER-IP>
4.1 Host-Key-Abfrage
Beim ersten Login erscheint:
The authenticity of host '...' can't be established.
ED25519 key fingerprint is SHA256:...
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
Was passiert hier?
SSH kennt diesen Server noch nicht. Der Server präsentiert seinen Host-Key. Dein Client fragt dich, ob du diesem Schlüssel vertraust. Das schützt vor Man-in-the-Middle-Angriffen.
Wenn du den Server gerade selbst installiert hast, bestätige mit yes.
Danach steht oft:
Warning: Permanently added '...' to the list of known hosts.
Der Host-Key wird lokal gespeichert: ~/.ssh/known_hosts.
4.2 Passphrase-Abfrage
Wenn du eine Passphrase gesetzt hast:
Enter passphrase for key '.../id_ed25519':
Was bedeutet das?
- Du nutzt Schlüssel-Login.
- Dein Private Key ist zusätzlich geschützt.
- Du gibst keinen Server-Login per Passwort frei.
Beim zweiten Login kommt die Host-Key-Abfrage nicht erneut (solange der Server gleich bleibt).
5. Benutzer und UID 1000
Nachdem der erste Login funktioniert, prüfen wir die Benutzer- und Rechtebasis.
Best Practice: Der primäre Admin-User (im Beispiel martin) sollte die Standard-UID/GID 1000:1000 haben.
Warum das wichtig ist
Viele Container- und NAS-Workflows arbeiten mit
PUID/PGID. Wenn dein Standardbenutzer konsistent 1000:1000 ist, werden Dateirechte bei Volumes, Backups und Migrationen deutlich einfacher.
Check
whoami
id martin
Erwartung: UID und GID sind beide 1000:
martin
uid=1000(martin) gid=1000(martin) groups=1000(martin)
Hinweis zur sudo-Gruppe
Ob
27(sudo)in der Ausgabe erscheint, hängt vom Provider ab: Manche Installer legen den User direkt mit Sudo-Rechten an (z. B. Netcup), andere nicht. Falls die Gruppe fehlt, holen wir das in Sektion 7 nach – wichtig ist hier nur, dass UID und GID stimmen.
6. Die fünf Basisschritte
Schritt 1: System aktualisieren & Basispakete installieren
Zuerst Updates:
sudo apt update && sudo apt upgrade -y
Basispakete
Ein frisch installiertes Debian ist sehr minimal gehalten. Das ist gut für Sicherheit, aber im Alltag fehlen ein paar Werkzeuge, die wir für Administration, Fehlersuche und spätere Installationen ständig brauchen.
sudo apt install -y curl ca-certificates gnupg nano htop git unzip rsync bash-completion needrestart
| Paket | Zweck |
|---|---|
curl |
HTTP(S)-Requests, Downloads, API-Checks |
ca-certificates |
Vertrauenswürdige Zertifikate für HTTPS/TLS |
gnupg |
Signaturen prüfen (z. B. bei externen Quellen) |
nano |
Konfigurationsdateien bearbeiten |
htop |
Prozesse und Ressourcen übersichtlich prüfen |
git |
Versionierung für Doku und Configs |
unzip |
ZIP-Archive entpacken |
rsync |
Effiziente Backups und Syncs |
bash-completion |
Weniger Tippfehler, bessere CLI-Erfahrung |
needrestart |
Zeigt nach Updates an, welche Dienste neu gestartet werden müssen – wichtig für unattended-upgrades |
Schritt 2: Zeitzone und Locale prüfen
Zeitzone setzen:
sudo timedatectl set-timezone Europe/Berlin
Locale auf Englisch setzen:
sudo update-locale LANG=en_US.UTF-8
Wichtig
Damit die Sprachänderung aktiv wird, musst du dich einmal abmelden und neu per SSH anmelden.
Nach erneutem Login prüfen:
timedatectl
locale
Warum das wichtig ist
Zeit ist Grundlage für Logs und Debugging. Ein sauber gesetztes Locale verhindert später Probleme mit Encoding, Sonderzeichen und konsolenbasierten Tools.
Warum Englisch als Systemsprache
Für Server und SSH-basierte Systeme ist en_US.UTF-8 Best Practice – auch in deutschsprachigen Teams. Der Grund ist praktisch: Fehlermeldungen, Logs und Dokumentation sind auf Englisch deutlich besser dokumentiert und recherchierbar. Wenn du später eine Fehlermeldung googelst oder in einem Forum postest, findest du mit englischen Systemmeldungen schneller Hilfe. Das Tastaturlayout wird davon nicht beeinflusst – deine Eingaben per SSH kommen vom Client und bleiben unverändert.
Check
timedatectl zeigt die richtige Zeitzone. locale zeigt LANG=en_US.UTF-8.
Schritt 3: SSH weiter absichern
Konfiguration öffnen:
sudo nano /etc/ssh/sshd_config
Diese Datei steuert, wie sich der SSH-Server verhält. Die folgenden Einstellungen sorgen dafür, dass nur das Nötigste erlaubt ist.
Empfohlene Kerneinstellungen
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AllowUsers martin
| Einstellung | Bedeutung |
|---|---|
| PermitRootLogin no | Niemand kann sich direkt als Root anmelden. Selbst wenn jemand einen Root-Schlüssel hätte, wäre der Login blockiert. |
| PasswordAuthentication no | Der Server akzeptiert keine Passwörter mehr. Damit fallen die meisten automatisierten Angriffe weg. |
| PubkeyAuthentication yes | Anmeldung ist nur noch mit SSH-Schlüsseln erlaubt – also mit deinem Key. |
| AllowUsers martin | Nur der Benutzer martin darf sich per SSH anmelden. Selbst wenn später weitere Nutzer existieren, kommen sie nicht automatisch per SSH rein. |
Nach der Änderung nano mit control + x beenden und mit Y speichern bestätigen.
Vor dem Reload: Syntax prüfen
Bevor du den Dienst neu lädst, prüfst du die Konfiguration auf Tippfehler:
sudo sshd -t
Wenn der Befehl ohne Ausgabe zurückkehrt, ist die Syntax in Ordnung. Bei Fehlern wirst du auf die Zeile hingewiesen – korrigieren, bevor du weitermachst.
Aktivieren:
sudo systemctl reload ssh
Warum das wichtig ist:
- Root-Login über SSH ist ein unnötiges Risiko
- Passwort-Login ist die häufigste Angriffsfläche bei VPS-Systemen
- Weniger SSH-Features bedeutet weniger Überraschungen
⚠️ Wichtiger Check – aussperren vermeiden
Öffne ein zweites Terminal und teste den Login erneut, bevor du die alte Session schließt:
ssh martin@<SERVER-IP>Erwartung: Login klappt. Ein Login mit
ssh root@<SERVER-IP>wird sofort verweigert. Erst danach die alte Session schließen.
Schritt 4: Firewall aktivieren (UFW, minimal offen)
UFW installieren und Default-Policy setzen:
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
IPv6-Schutz aktivieren:
sudo nano /etc/default/ufw
Setze (falls nicht schon gesetzt):
IPV6=yes
Speichern (Ctrl+X, dann Y).
Hintergrund:
Viele VPS haben IPv6. Ohne explizite Konfiguration schützt UFW möglicherweise nur IPv4 – IPv6-Verbindungen könnten dann ungeschützt bleiben.
SSH erlauben:
sudo ufw allow OpenSSH
Firewall aktivieren:
sudo ufw enable
UFW warnt, dass bestehende SSH-Verbindungen betroffen sein könnten. Da wir SSH oben explizit erlaubt haben, kannst du mit y bestätigen:
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
Warum
OpenSSHund nicht22/tcp?UFW kennt für viele Dienste vordefinierte „Application Profiles".
OpenSSHist eines davon. Funktional ist es bei SSH dasselbe wie22/tcp, aber die Schreibweise ist klarer und konsistent mit Profilen wieNginx Full, die später für Web-Dienste sinnvoll werden.
Warum Firewall überhaupt?
Prinzip: Alles ist gesperrt – nur explizit erlaubte Ports sind offen. So behältst du die Kontrolle, auch wenn später Dienste installiert werden.
Warum UFW und nicht nftables direkt?
UFW nutzt unter der Haube nftables (auf Debian 13), bietet aber eine deutlich einfachere Syntax für Homelab-Szenarien. Für komplexe Enterprise-Setups wäre nftables direkt besser – für Self-Hosting ist UFW perfekt.
Check
sudo ufw status verbose
Erwartung:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp (OpenSSH) ALLOW IN Anywhere
22/tcp (OpenSSH (v6)) ALLOW IN Anywhere (v6)
Schritt 5: Automatische Sicherheitsupdates aktivieren
Debian sollte automatisch Sicherheits-Patches installieren:
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure unattended-upgrades
Der Dialog fragt, ob du es aktivieren willst – bestätige mit „Yes".
Was passiert?
- Täglich werden Sicherheitsupdates automatisch heruntergeladen und installiert
- Nur aus dem
Debian-Security-Repository (keine Breaking Changes) - Logs liegen unter
/var/log/unattended-upgrades/
Automatischen Reboot konfigurieren
Manche Updates – Kernel, Glibc, systemd – werden erst nach einem Reboot wirksam. Ohne automatischen Reboot bleibt dein Server bis zum manuellen Neustart mit der alten, möglicherweise verwundbaren Version laufen.
Die Standard-Datei /etc/apt/apt.conf.d/50unattended-upgrades ist lang, komplett auf Englisch und alle relevanten Zeilen sind auskommentiert. Statt darin die richtigen Stellen zu suchen und zu entkommentieren, legen wir eine eigene kleine Datei an, die nur unsere Einstellungen enthält:
sudo nano /etc/apt/apt.conf.d/52unattended-upgrades-local
Folgenden Inhalt einfügen:
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";
Speichern mit Ctrl+X, dann Y.
Warum eine eigene Datei?
Die höhere Nummer (
52) gewinnt gegen die Defaults in der50-Datei – APT liest die Dateien in/etc/apt/apt.conf.d/der Reihe nach, spätere überschreiben frühere. Du musst nichts suchen, nichts entkommentieren. Und bei einem Paket-Update der50-Datei bleibt deine Einstellung erhalten, weil sie in einer separaten Datei steht.
Wann lieber
falsesetzen?Wenn du echte Mission-Critical-Dienste betreibst, bei denen ungeplante Reboots problematisch wären. Für klassische Self-Hosting-Setups (Vaultwarden, Immich, Blog) ist
truemit nächtlichem Zeitfenster die ehrlichere Wahl: Sicherheitslücken werden zeitnah geschlossen, Downtime fällt mitten in die Nacht.needrestart(haben wir oben mit installiert) zeigt dir auf jedem Login an, ob ein Reboot nötig ist – Überraschungen ausgeschlossen.
Check
Zuerst prüfen, ob der Service läuft und beim Boot startet:
sudo systemctl status unattended-upgrades
sudo systemctl is-enabled unattended-upgrades
Erwartung: active (running) und enabled.
Hinweis: Der Service heißt vollständig „Unattended Upgrades Shutdown" – das ist normal und hat nichts mit Herunterfahren zu tun. Es ist ein Wächter-Prozess, der im Hintergrund läuft und beim Systemstart-/Shutdown noch ausstehende Updates sauber abschließt. Die täglichen Updates selbst laufen über einen separaten Timer.
Dann prüfen, ob deine Reboot-Einstellungen wirklich greifen:
apt-config dump | grep -i "Unattended-Upgrade::Automatic-Reboot"
Erwartung:
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";
Tauchen beide Zeilen auf, wird deine 52unattended-upgrades-local korrekt eingelesen und überschreibt die Defaults.
Warum das wichtig ist
Sicherheitsupdates manuell zu machen heißt: vergessen. Automatisierung ist 2026 Standard für VPS-Betrieb.
7. sudo statt Root: das Least-Privilege-Prinzip
In den Schritten oben hast du schon mehrmals sudo benutzt. Hier nochmal kurz, warum das so läuft – und wie du sicherstellst, dass es bei dir auch wirklich funktioniert.
Warum nicht einfach als root arbeiten?
Der Root-Benutzer hat uneingeschränkte Systemrechte. Ein Tippfehler – etwa ein rm -rf an der falschen Stelle – kann das ganze System unbrauchbar machen. Deshalb gilt im Server-Alltag das Prinzip Least Privilege: Du arbeitest standardmäßig als normaler Benutzer und holst dir Administratorrechte nur dann, wenn du sie wirklich brauchst – über sudo.
Das ist nicht nur Sicherheits-Hygiene, sondern auch ein Konzept, das später überall wieder auftaucht: Container laufen mit eingeschränkten Rechten, Apps haben eigene Service-User, Backups schreiben mit minimalen Berechtigungen. Wer das Prinzip einmal verinnerlicht hat, versteht später vieles automatisch mit.
Prüfen, ob dein Benutzer in der sudo-Gruppe ist
groups martin
Erscheint sudo in der Ausgabe, ist alles korrekt.
Falls sudo fehlt (einmalig als Root oder über die Provider-Konsole):
sudo usermod -aG sudo martin
Danach einmal neu einloggen – Gruppenrechte werden erst beim Login geladen.
Hinweis
Auf manchen Providern (z. B. Netcup) wird der erste User direkt mit Sudo-Rechten angelegt – dann ist hier nichts zu tun. Auf anderen musst du nachhelfen. Der Check oben zeigt dir, was zutrifft.
Und jetzt?
Du hast jetzt eine saubere, gehärtete Linux-Basis: SSH per Schlüssel, Firewall aktiv, automatische Sicherheitsupdates, klare Benutzerrechte. Diese Basis bleibt von hier an möglichst unverändert – alles, was du an Diensten brauchst, läuft ab dem nächsten Artikel in Containern darüber – Docker und Docker Compose sind das nächste Fundament-Stück.
Was kommt als Nächstes für die Sicherheit?
Aktuell ist Port 22 öffentlich erreichbar – durch die Key-Authentifizierung sind Brute-Force-Versuche zwar chancenlos, aber er ist eben sichtbar. In Artikel 5 (Tailscale VPN) machen wir den letzten Schritt für SSH: Es ist dann nur noch über das verschlüsselte Tailscale-Netzwerk erreichbar, Port 22 verschwindet komplett aus dem öffentlichen Internet.
Für deine Web-Dienste, die später öffentlich erreichbar sein sollen (z. B. Vaultwarden, Immich, Ghost), kommt in Artikel 10 CrowdSec dazu – das übernimmt Angriffserkennung über alle Apps hinweg. Damit du dafür später nicht alles umbauen musst, wird Caddy in dieser Serie von Anfang an mit eingebautem CrowdSec-Plugin installiert (inaktiv – aktiviert wird es erst in Artikel 10).
👉 Nächster Artikel: 03. Docker & Docker Compose Grundlagen
Discussion