Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Cloud / Hosting 12.11.2026 · 13 min Lesezeit Geprüft 12.11.2026

Uptime Kuma, Vaultwarden, ntfy und AdGuard auf 1 GB RAM: Was der Mini-Server schafft

Vier kleine Dienste kosten auf 1 GB RAM rund 240 MiB, mit WordPress bleiben 218 MiB verfügbar. Unter Last bremst die eine vCPU: AdGuard schafft 4.000 DNS-Anfragen pro Sekunde, Vaultwarden knapp 2 Anmeldungen, ntfy mit Token rund 70 Nachrichten, mit Passwort bricht es bei 10 ein.

Mit KI erstellt – redaktionelle Prüfung ausstehend

Werbung Mit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Symbolbild: kompakter Mini-Server mit Speicheranzeige, Titel Vier Dienste auf 1 GB

Uptime Kuma für das Monitoring, Vaultwarden für Passwörter, ntfy für Push-Nachrichten und AdGuard Home als DNS-Filter: Passen diese vier kleinen Dienste zusammen auf einen Server mit 1 GB RAM und einer vCPU? Diese Anleitung baut die vier hinter Caddy auf einem netcup VPS piko G11s auf, misst den Speicher und schickt von einem zweiten Server aus Last auf jeden Dienst.

Dies ist Teil 4 der Reihe „Mini-Server“. Am Ende kommt WordPress aus Teil 3 noch einmal dazu.

Was Sie brauchen

  • Einen Server mit Docker und 1 GB RAM, hier der Mini-Server aus Teil 1. Lasttests nur auf eigenen Systemen und nur kurz.
  • Eine Domain mit A-Eintrag auf den Server, Ports 80, 443 und 8443 bis 8445 offen, Port 53 nur für Ihre eigenen Clients (Schritt 2).
  • Einen zweiten Rechner mit Docker als Lastgenerator, wie in Teil 2 beschrieben.
  • Grundwissen zu Docker, siehe Docker Compose unter Linux.

Schritt 1: Vier Dienste hinter einem Caddy

Alle vier Dienste laufen in einem Compose-Projekt in /root/mini/dienste. Caddy holt ein Zertifikat für einen einzigen Hostnamen und verteilt die Dienste auf Ports: Uptime Kuma auf 443, Vaultwarden auf 8443, ntfy auf 8444, die Oberfläche von AdGuard Home auf 8445. So braucht es nur einen DNS-Eintrag. Die Datei compose.yaml, statt 203.0.113.10 tragen Sie die öffentliche IPv4-Adresse Ihres Servers ein:

services:
  caddy:
    image: caddy:2.11.7-alpine
    restart: unless-stopped
    ports: ["80:80", "443:443", "8443:8443", "8444:8444", "8445:8445"]
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - ./caddy/data:/data
      - ./caddy/config:/config
  kuma:
    image: louislam/uptime-kuma:2.5.11
    restart: unless-stopped
    ports: ["127.0.0.1:3001:3001"]
    volumes: [./kuma:/app/data]
  vaultwarden:
    image: vaultwarden/server:1.37.5
    restart: unless-stopped
    env_file: vaultwarden.env
    volumes: [./vaultwarden:/data]
  ntfy:
    image: binwiederhier/ntfy:v2.37.2
    restart: unless-stopped
    command: serve
    env_file: ntfy.env
    volumes: [./ntfy:/var/lib/ntfy]
  adguard:
    image: adguard/adguardhome:v0.107.80
    restart: unless-stopped
    ports:
      - "203.0.113.10:53:53/udp"
      - "203.0.113.10:53:53/tcp"
      - "127.0.0.1:3000:3000"
    volumes:
      - ./adguard/work:/opt/adguardhome/work
      - ./adguard/conf:/opt/adguardhome/conf

Port 53 ist bewusst nur an die öffentliche IPv4-Adresse gebunden. Unter Ubuntu belegt systemd-resolved bereits 127.0.0.53:53 und 127.0.0.54:53, und über IPv6 ist der DNS-Port so gar nicht erst offen. Die Datei Caddyfile, der letzte Block ist nur intern erreichbar und dient Uptime Kuma als Messziel:

mini.example.com {
	reverse_proxy kuma:3001
}
mini.example.com:8443 {
	reverse_proxy vaultwarden:80
}
mini.example.com:8444 {
	reverse_proxy ntfy:80
}
mini.example.com:8445 {
	reverse_proxy adguard:3000
}
:8080 {
	respond "ok" 200
}

Die beiden Dateien mit Einstellungen. vaultwarden.env erlaubt die Registrierung nur, bis Ihr Konto angelegt ist, ntfy.env sperrt alle Themen für Unbekannte:

# vaultwarden.env
DOMAIN=https://mini.example.com:8443
SIGNUPS_ALLOWED=true
# ntfy.env
NTFY_BASE_URL=https://mini.example.com:8444
NTFY_BEHIND_PROXY=true
NTFY_CACHE_FILE=/var/lib/ntfy/cache.db
NTFY_AUTH_FILE=/var/lib/ntfy/user.db
NTFY_AUTH_DEFAULT_ACCESS=deny-all
NTFY_ATTACHMENT_CACHE_DIR=/var/lib/ntfy/attachments

Setzen Sie zuerst die beiden Firewall-Regeln aus Schritt 2, damit AdGuard Home nie als offener Resolver im Netz steht. Bevor Caddy die Dienste öffentlich macht, richten Sie AdGuard Home und Uptime Kuma über einen SSH-Tunnel ein. Sonst könnte ein Fremder die Einrichtung und damit das Admin-Konto übernehmen:

# cd /root/mini/dienste
# docker compose up -d kuma vaultwarden ntfy adguard

Auf Ihrem Rechner öffnet ssh -L 3000:127.0.0.1:3000 -L 3001:127.0.0.1:3001 root@mini.example.com beide Oberflächen unter http://localhost:3000 und http://localhost:3001. Im Assistenten von AdGuard Home lassen Sie die Oberfläche auf Port 3000 und DNS auf Port 53. Uptime Kuma 2 fragt beim ersten Start nach der Datenbank, für einen kleinen Server reicht SQLite. Danach den Admin für ntfy anlegen, das Passwort fragt der Befehl ab:

# docker compose exec ntfy ntfy user add --role=admin admin
# docker compose up -d caddy

Legen Sie sofort Ihr Konto in Vaultwarden unter https://mini.example.com:8443 an, setzen Sie dann SIGNUPS_ALLOWED=false, bis dahin könnte sich jeder registrieren und starten Sie den Container mit docker compose up -d vaultwarden neu.

Verifizieren: Alle fünf Container laufen, und die vier Adressen antworten.

# docker compose ps --format '{{.Name}} {{.Status}}'
# for p in 443 8443 8444 8445; do curl -s -o /dev/null -w "$p %{http_code}\n" https://mini.example.com:$p/; done
dienste-adguard-1 Up 5 minutes
dienste-caddy-1 Up 2 hours
dienste-kuma-1 Up 2 hours (healthy)
dienste-ntfy-1 Up 30 minutes
dienste-vaultwarden-1 Up 30 minutes (healthy)
443 302
8443 200
8444 200
8445 302

Schritt 2: DNS nur für eigene Clients

Ein offener DNS-Resolver wird schnell für Angriffe mit gefälschten Absendern missbraucht. Docker veröffentlicht Ports an der Firewall des Hosts vorbei, eine Regel in UFW greift dafür nicht. Zuständig ist die Kette DOCKER-USER. Die beiden Regeln lassen Port 53 nur von einer erlaubten Adresse zu, hier 198.51.100.20:

# iptables -I DOCKER-USER -i eth0 -p udp ! -s 198.51.100.20 -m conntrack --ctorigdst 203.0.113.10 --ctorigdstport 53 -j DROP
# iptables -I DOCKER-USER -i eth0 -p tcp ! -s 198.51.100.20 -m conntrack --ctorigdst 203.0.113.10 --ctorigdstport 53 -j DROP

Wichtig ist --ctorigdst mit der eigenen Adresse. Ohne sie verwirft die Regel auch die Antworten der Upstream-Resolver auf die Anfragen von AdGuard Home selbst, siehe Typische Fehler. Den Namen Ihrer Netzwerkschnittstelle statt eth0 zeigt ip -br a. Die Regeln gelten nur bis zum nächsten Neustart, tragen Sie sie deshalb in Ihr Firewall-Skript ein, das nach Docker startet. Als zweite Sicherung tragen Sie dieselbe Adresse in der Oberfläche von AdGuard Home unter Einstellungen, DNS-Einstellungen, Zugriffseinstellungen als erlaubten Client ein. Dann beantwortet AdGuard Home Fremden keine Anfragen, auch wenn die Firewall-Regeln einmal fehlen.

Verifizieren: Vom erlaubten Rechner kommt eine Antwort, Werbedomains liefern 0.0.0.0. Von jedem anderen Rechner läuft dieselbe Abfrage in eine Zeitüberschreitung.

# dig +short @203.0.113.10 heise.de
# dig +short @203.0.113.10 doubleclick.net
193.99.144.80
0.0.0.0

Schritt 3: Messen ohne Last

Gemessen wird wie in Teil 3 mit einer kleinen Schleife, diesmal mit dem Speicher jedes Containers. Sie liest ihn direkt aus der Kontrollgruppe und rechnet wie docker stats den Dateicache heraus, der sich jederzeit verwerfen lässt. Der Kern der Datei /root/mini/probe4.sh, die volle Fassung schreibt zusätzlich CPU-Zeit und Uhrzeit alle 2 Sekunden mit:

for c in $(docker ps -q --no-trunc); do
  d=/sys/fs/cgroup/system.slice/docker-$c.scope
  m=$(( ( $(cat $d/memory.current) - $(awk '/^inactive_file /{print $2}' $d/memory.stat) ) / 1048576 ))
  echo "$(docker inspect -f '{{.Name}}' $c | tr -d /) $m MiB"
done

Verifizieren: Die Schleife lieferte zur selben Zeit 15, 19, 20, 129 und 48 MiB, also dieselben Werte wie docker stats.

# docker stats --no-stream --format '{{.Name}} {{.MemUsage}}'
dienste-vaultwarden-1 15.01MiB / 957.5MiB
dienste-ntfy-1 19.68MiB / 957.5MiB
dienste-caddy-1 20.8MiB / 957.5MiB
dienste-kuma-1 129.3MiB / 957.5MiB
dienste-adguard-1 48.44MiB / 957.5MiB

Gemessen wurden der leere Server, jeder Dienst 10 Minuten allein und alle zusammen 30 Minuten, Uptime Kuma mit 20 Monitoren im Abstand von 60 Sekunden.

Balkendiagramm: Speicher je Container im Leerlauf, Uptime Kuma 124 MiB, AdGuard Home 43, ntfy 21, Vaultwarden 20, Caddy 10 MiB
Speicher je Container allein: Uptime Kuma braucht rund 120 MiB, die anderen drei Dienste und Caddy zusammen rund 95 MiB

Der leere Server mit Docker hatte im Mittel 613 MiB verfügbar, mit allen fünf Containern 376 MiB, also rund 240 MiB weniger. Uptime Kuma ist der größte Posten, auch auf der Platte: Sein Image belegt 2,54 GB, die anderen drei je 115 bis 416 MB. Die CPU war zu rund 12 % belegt, einschließlich der Messschleife.

Kommen WordPress und MariaDB aus Teil 3 dazu, mit den dortigen Grenzen und ohne Besucher, blieben im Mittel 218 MiB verfügbar, mindestens 189 MiB. Der WordPress-Container belegte 62 MiB, die Datenbank 85 MiB. Uptime Kuma lag dabei mit 142 MiB höher als im ersten Durchgang, in seiner Datenbank standen zu diesem Zeitpunkt zusätzlich 250 pausierte Monitore. Das passt, aber mit wenig Puffer.

AdGuard Home unter DNS-Last

Für die Last läuft auf dem zweiten Server das Werkzeug dnsperf mit einer Liste von 99 bekannten Domains. Die Antworten kommen aus dem Cache von AdGuard Home. Die Stufen steigen von 500 auf 16.000 Anfragen pro Sekunde, je 30 Sekunden:

# dnsperf -s 203.0.113.10 -d domains.txt -c 20 -T 2 -l 30 -Q 4000 -t 2

AdGuard Home bremst jeden Client in der Standardeinstellung auf 20 Anfragen pro Sekunde. Diese Bremse ist für den Test abgeschaltet, warum, steht unter Typische Fehler.

Säulendiagramm: beantwortete DNS-Anfragen je Laststufe, bis 2.000 pro Sekunde wie angefragt, ab 8.000 bei voller CPU 4.000 bis 4.450
Beantwortete DNS-Anfragen je Stufe: bis 2.000 pro Sekunde exakt wie angefragt, ab 8.000 bei voller CPU 4.000 bis 4.450

Bis 2.000 pro Sekunde kam jede Antwort im Mittel nach 1,3 bis 5,3 Millisekunden. Bei angefragten 4.000 schaffte AdGuard Home 3.600 bei 91 % CPU, ab 8.000 war die CPU voll und es beantwortete 4.000 bis 4.450 pro Sekunde mit 22 bis 25 Millisekunden. Verloren ging keine einzige Anfrage, dnsperf hält nur eine begrenzte Zahl Anfragen gleichzeitig offen und schickt neue erst, wenn Antworten kommen. Der Speicher von AdGuard Home stieg von 43 auf 109 MiB.

ntfy: Passwort oder Token

Für ntfy hielten 100 Abonnenten per WebSocket ein Thema offen, k6 schickte Nachrichten mit steigender Rate. In der Standardeinstellung erlaubt ntfy jedem Absender eine kurze Spitze und bremst dann. Bei 10 Nachrichten pro Sekunde und 20 Abonnenten kamen 50 Nachrichten durch, danach antwortete ntfy mit 429 Too Many Requests. Außerdem erlaubt ntfy je Adresse standardmäßig 30 Abos. Für die Messung waren die Grenzen für den Lastgenerator aufgehoben und die Zahl der Abos erhöht:

NTFY_VISITOR_REQUEST_LIMIT_EXEMPT_HOSTS=198.51.100.20
NTFY_VISITOR_SUBSCRIPTION_LIMIT=500

Mit Benutzername und Passwort brach ntfy schon bei 10 Nachrichten pro Sekunde ein, die Hälfte der Anfragen brauchte länger als 5,8 Sekunden. Bei 50 pro Sekunde lief jede Anfrage in die Zeitgrenze von 10 Sekunden. Nach dem Ende der höchsten Laststufe war ntfy siebeneinhalb Minuten lang nicht erreichbar, auch nicht für die Statusabfrage. In allen Stichproben lag es bei 87 bis 98 % CPU, wuchs auf 301 MiB, und mehr als 1.000 Verbindungen warteten unbeantwortet. Dann fing es sich. Ein Vergleich zeigt die Ursache:

Balkendiagramm: Dauer einer Anfrage an ntfy, ohne Anmeldung 56 ms, mit Token 62 ms, mit Passwort 155 ms
Dauer einer Anfrage an ntfy: mit Passwort rund 90 Millisekunden länger als mit Zugriffstoken

Mit Passwort dauert jede Anfrage rund 90 Millisekunden länger. Auf einer einzigen vCPU sind damit rechnerisch höchstens rund 11 angemeldete Anfragen pro Sekunde drin, und jede Verbindung eines Abonnenten zählt mit. Ein Zugriffstoken kostet praktisch nichts:

# docker compose exec ntfy ntfy token add admin

Skripte und Monitore senden ihn als Kopfzeile Authorization: Bearer mit dem ausgegebenen Token. Mit Token lief ntfy bei 50 Nachrichten pro Sekunde an 100 Abonnenten sauber, 95 % der Anfragen waren nach 0,9 Sekunden fertig. Darüber stellte es rund 67 bis 74 Nachrichten pro Sekunde zu, also rund 7.000 Zustellungen pro Sekunde, die Anfragen warteten dann 6 bis 7 Sekunden. Erst bei 800 angefragten Nachrichten pro Sekunde scheiterten 17 %. ntfy blieb dabei jederzeit erreichbar. Die CPU teilten sich ntfy mit rund 43 % und Caddy mit rund 33 % für TLS und WebSockets, ntfy wuchs auf bis zu 186 MiB.

Vaultwarden: Anmeldungen kosten Rechenzeit

Bei Vaultwarden ist die Anmeldung der teure Teil: Der Server prüft das Passwort mit vielen Runden einer Hashfunktion. Direkt am Container dauerte eine Anmeldung 0,44 bis 0,49 Sekunden. In der Standardeinstellung erlaubt Vaultwarden je Adresse 10 Anmeldungen am Stück, danach eine pro Minute. Bei 4 Versuchen pro Sekunde gingen genau 10 durch, die übrigen 171 bekamen 429.

Für die Messung standen LOGIN_RATELIMIT_SECONDS=1 und LOGIN_RATELIMIT_MAX_BURST=1000 in vaultwarden.env. Ohne die Bremse schaffte der Server 1 Anmeldung pro Sekunde mit rund 50 % CPU, 95 % waren nach 0,75 Sekunden fertig. Bei 2 pro Sekunde war die CPU voll, die Hälfte der Anmeldungen wartete fast 6 Sekunden, einzelne bis 13 Sekunden. Bei 4 pro Sekunde liefen 70 % in die Zeitgrenze von 15 Sekunden. Der Speicher blieb bei höchstens 17 MiB.

Uptime Kuma mit vielen Monitoren

Uptime Kuma bekam statt 20 Monitoren im Abstand von 60 Sekunden erst 100, dann 250 Monitore im Abstand von 20 Sekunden, jeweils 10 Minuten lang:

MonitoreAbstandSpeicher Uptime KumaCPU Uptime KumaCPU Server gesamt
2060 s118 MiB1,7 %12 %
10020 s151 MiB7,0 %19 %
25020 s181 MiB13,7 %26 %

250 Monitore mit 12,5 Prüfungen pro Sekunde sind für den kleinen Server kein Problem, die Datenbank war danach 2,5 MB groß, dazu 4,1 MB im Schreibprotokoll von SQLite. Eine Eigenheit: Nach dem Pausieren der 250 Monitore lief Uptime Kuma mit 20 Monitoren weiter, belegte aber noch 174 statt 118 MiB. Einen Teil des Speichers gab es erst mit einem Neustart des Containers wieder her, danach lag es bei 142 MiB.

Typische Fehler

AdGuard Home lädt keine Filterliste, im Protokoll steht dnsproxy: response received upstream_type=bootstrap … i/o timeout

Die Firewall-Regel für Port 53 schaut nur auf den Port und verwirft deshalb auch die Antworten der Upstream-Resolver. Ergänzen Sie -i eth0 und --ctorigdst mit der eigenen öffentlichen Adresse wie in Schritt 2.

AdGuard Home beantwortet nur 20 Anfragen pro Sekunde, der Rest geht verloren

Das ist die Ratenbremse, Standard 20 Anfragen pro Sekunde je Client-Netz. Im Test galt sie auch für eine Adresse, die in der Ausnahmeliste ratelimit_whitelist stand. Die Schnittstelle zeigte die Ausnahme an, das Abfrageprotokoll die richtige Client-Adresse, und auch nach einem Neustart blieb es bei 20 Antworten pro Sekunde. Erst der Wert 0 für die Bremse unter Einstellungen, DNS-Einstellungen hob die Grenze auf. Schalten Sie sie nur ab, wenn allein bekannte Clients auf Port 53 dürfen.

ntfy antwortet minutenlang nicht, Caddy meldet dial tcp …:80: i/o timeout

Zu viele Anfragen mit Passwort. Nutzen Sie Zugriffstoken. ntfy fing sich im Test nach siebeneinhalb Minuten von selbst, docker compose restart ntfy geht schneller.

Vaultwarden ignoriert eine geänderte Login-Grenze

Im Startprotokoll steht [ERROR] `LOGIN_RATELIMIT_MAX_BURST` contains an invalid value for its type and is ignored, the default is used instead. Vaultwarden lehnte den Wert 100000 ab und nutzte still die Standardgrenze, den Wert 1000 nahm es an.

Häufige Fragen

Reicht 1 GB RAM für alle vier Dienste?

Ja, im Leerlauf mit großem Abstand. Mit allen vier und Caddy blieben 376 MiB verfügbar. Unter Volllast sank der verfügbare Speicher auf bis zu 92 MiB, als ntfy mit Passwort überlastet war, in allen anderen Läufen blieben mindestens 130 MiB. Der Engpass ist die eine CPU: Ein Dienst unter Last bremst die anderen mit.

Sollte ich die Ratenbremsen abschalten?

Nein. Alle drei Bremsen schützen den kleinen Server: AdGuard Home gegen Missbrauch als Resolver, ntfy gegen Flut, Vaultwarden gegen das Raten von Passwörtern. Abgeschaltet oder gelockert waren sie nur für die Dauer der Messung, bei ntfy nur für den Lastgenerator. Bei AdGuard Home und Vaultwarden galten die Testwerte für alle Clients, danach waren wieder die Standardwerte aktiv.

Fazit

Uptime Kuma, Vaultwarden, ntfy und AdGuard Home passen zusammen auf einen Server mit 1 GB RAM, sogar mit WordPress daneben. Knapp wurde der Speicher nur, als ntfy mit Passwort überlastet war und auf rund 300 MiB anwuchs. Sonst stößt die eine vCPU zuerst an ihre Grenze, vor allem bei Anmeldungen: Vaultwarden schafft knapp 2 pro Sekunde, ntfy mit Passwort brach schon bei 10 Nachrichten pro Sekunde ein. Wer ntfy aus Skripten bedient, sollte deshalb Zugriffstoken nutzen. Für den eigenen Bedarf und ein kleines Team reicht der Mini-Server für alle vier mit Reserve. Die nächsten Teile der Reihe vergleichen den Mini-Server mit einem kostenlosen Server von Oracle und messen, ob Swap oder zram bei 1 GB etwas bringen.

Weiterführende Anleitungen und Quellen

Mini-ServerUptime KumaVaultwardenntfyAdGuard HomeLasttestnetcup