Aufbaukurs · 10 Stufen

Vom eigenen VPS zum gemeinsamen Kalender

FL-Pro-Selbstlern-Kurs · Stand 02.10.2026

Du richtest eine eigene Nextcloud ein, legst getrennte Benutzerkonten an und organisierst gemeinsam Termine und Aufgaben. Zum Schluss prüfst Du Freigaben, Gerätesynchronisierung und eine Wiederherstellung. Die Grundlage ist der vorhandene Kurs Self-Hosting & VPS. Wenn Du noch keinen Server hast, beginnst Du dort.

Praxisstand: Nextcloud 35.0.1, Calendar 6.6.2 und Tasks 0.18.1. Serverbetrieb, Freigaben, CalDAV, Aufgaben, Buchungen und Wiederherstellung wurden praktisch geprüft. Die Geräteanleitungen folgen den verlinkten Herstellerdokumentationen; Android, iOS und Thunderbird wurden hier nicht auf eigenen Geräten eingerichtet. Das Kursbeispiel verwendet ausschließlich fiktive Personen. Prüfprotokoll und Beispieldateien findest Du am Kursende.

Dein Ergebnis

Die fiktive Familie Mira, Alex und Sam organisiert Robins Betreuung. Sie benötigt eine Übersicht über Aufenthalte, einen Schulkalender und eine Aufgabenliste. Mira und Sam führen daneben Beratungsgespräche. Alex soll die Familiendaten sehen, aber keine Kundentermine. Jede Person hat außerdem einen privaten Kalender.

Bereich Mira Alex Sam Außenstehende
Robin – Betreuung & Übergaben Bearbeiten Bearbeiten Bearbeiten Kein Zugriff
Robin – Schule & Termine Bearbeiten Bearbeiten Bearbeiten Kein Zugriff
Robin – Elternaufgaben Bearbeiten Bearbeiten Bearbeiten Kein Zugriff
Beratung – Termine Bearbeiten Kein Zugriff Bearbeiten Kein interner Zugriff
Persönlicher Kalender Nur eigene Daten Nur eigene Daten Nur eigene Daten Kein Zugriff

Eine Buchungsseite ist eine eigene Freigabeentscheidung. Sie kommt erst nach dem privaten Kalender. Aufgaben und Termine haben in Nextcloud jeweils eine eigene Ansicht.

Dein Einstieg: Du hast noch keinen Server? Beginne im VPS-Kurs. Ab einem vorhandenen VPS führt Dich dieser Aufbaukurs durch die gesamte Kalender-Einrichtung.

Beispieldateien herunterladen (ZIP) · Prüfprotokoll

Stufe 1

Aus dem VPS-Kurs hierher wechseln

Ausgangszustand: Du brauchst einen Linux-VPS mit Docker Compose, SSH-Zugang und administrativen Rechten. Nginx Proxy Manager (NPM) übernimmt bereits HTTPS. Du kannst DNS-Einträge Deiner Domain bearbeiten. Ein eigener Testbereich enthält ausschließlich Beispieldaten.

Im VPS-Kurs führen Dich Stufen 1–4 durch Server, SSH und Absicherung, Stufe 6 durch DNS und Stufe 7 durch den Reverse Proxy. Der Docker-Kurs erklärt Volumes und Compose. Bestehende Dienste werden in ihren bisherigen Ordnern betrieben; Nextcloud erhält einen eigenen Stack.

Öffne Dein lokales Terminal, verbinde Dich mit Deinem bereits eingerichteten SSH-Alias und arbeite anschließend in der Linux-Shell des Servers. Beispiel: ssh meinserver. Der Alias stammt aus Deiner SSH-Einrichtung und ist kein Nextcloud-Befehl.

Prüfe auf dem VPS:

bash
docker version
docker compose version
free -h
df -h /
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Ports}}'
docker network ls

Ergebnis: Docker und Compose antworten. Du findest Deinen NPM-Container und siehst freien Arbeitsspeicher und Speicherplatz. Der Beispielstack begrenzt Web, Cron, Datenbank und Redis zusammen auf gut 2 GiB; Betriebssystem, andere Dienste und Sicherungen benötigen zusätzliche Reserven. Diese Limits sind ein Startwert für eine kleine Kalenderinstanz.

Hilfe: Ist der VPS schon knapp ausgelastet, zuerst den Bedarf klären. Keine fremden Container stoppen. Ist /opt/nextcloud-kalender bereits vorhanden, dort nichts überschreiben; bestehende Einrichtung prüfen und einen neuen Übungsordner wählen.

Stufe 2

Domain und Verbindung zum Proxy vorbereiten

Ausgangszustand: NPM läuft; Du kennst seinen tatsächlichen Containernamen. Für das Beispiel heißt die neue Adresse kalender.example.org. Ersetze sie durch Deine eigene Subdomain. example.org und 203.0.113.10 sind Dokumentationsbeispiele.

Lege beim DNS-Anbieter einen A-Eintrag an: Name kalender, Wert die IPv4-Adresse Deines VPS. Ein AAAA-Eintrag gehört nur dazu, wenn der Dienst auch über die eingetragene IPv6-Adresse erreichbar ist. Prüfe auf Deinem Rechner, beispielsweise mit PowerShell:

powershell
Resolve-DnsName kalender.example.org -Type A

Auf dem VPS liest Du Domain und NPM-Container ein. Die Variablen bleiben für die nächsten Stufen in derselben Servershell erhalten:

bash
read -r -p 'Deine Kalender-Domain, ohne https://: ' CALENDAR_DOMAIN
read -r -p 'Name des laufenden NPM-Containers: ' NPM_CONTAINER
test -n "$CALENDAR_DOMAIN" && test -n "$NPM_CONTAINER"
docker inspect "$NPM_CONTAINER" --format '{{.State.Running}}'
docker inspect "$NPM_CONTAINER" --format '{{json .NetworkSettings.Networks}}'

Die Verbindung zum Vorgängerkurs: Dort wird für die Dienste das Netz web verwendet. Ein vorhandener Server kann beispielsweise web-proxy verwenden. Entscheidend ist, dass NPM und der neue Webcontainer dasselbe Docker-Netz erreichen. Datenbank und Redis gehören nicht in dieses Proxy-Netz.

Für den Kurs legst Du ein eigenes Verbindungsnetz an und verbindest ausschließlich NPM damit. Seine bisherigen Netze bleiben erhalten. Prüfe vorher, dass dieser Name nicht bereits für einen anderen Zweck verwendet wird:

bash
docker network inspect nextcloud-proxy

Bei „network … not found“ anlegen und NPM verbinden:

bash
docker network create nextcloud-proxy
docker network connect nextcloud-proxy "$NPM_CONTAINER"

Bei einer vorhandenen eigenen Kursinstallation beide Schritte überspringen, wenn NPM schon angeschlossen ist. Trage die Verbindung zusätzlich in der vorhandenen NPM-Compose-Datei ein, damit sie bei einer Neuerstellung von NPM erhalten bleibt. Ergänze beim tatsächlichen NPM-Service nextcloud-proxy zu seinen bisherigen Netzwerken; die bisherigen Netzwerke, Ports und Volumes erhalten. Für einen NPM-Service, der bisher das Standardnetz nutzt, sieht die Ergänzung so aus:

yaml
services:
  npm:
    # image, ports, volumes und übrige vorhandene Einstellungen erhalten
    networks: [default, nextcloud-proxy]
networks:
  default:
  nextcloud-proxy:
    external: true

Dies ist eine Ergänzung, keine vollständige Ersatzdatei. Nutzt Dein Proxy bereits andere benannte Netze, behältst Du diese statt default bei. Führe docker compose config --quiet im NPM-Ordner aus. Nach dessen nächster Neuerstellung prüfst Du die Verbindung erneut. Die momentane Verbindung besteht bereits durch docker network connect.

Lies die Proxy-IP dieses Netzes aus:

bash
NPM_PROXY_IP=$(docker inspect "$NPM_CONTAINER" \
  --format '{{with index .NetworkSettings.Networks "nextcloud-proxy"}}{{.IPAddress}}{{end}}')
test -n "$NPM_PROXY_IP"

Ergebnis: Die Domain zeigt auf Deinen VPS; die Verbindung zum Proxy ist vorbereitet. Öffentlich werden später nur die bestehenden NPM-Ports 80 und 443 verwendet.

Stufe 3

Einen eigenen Compose-Stack anlegen

Ausgangszustand: Du arbeitest weiterhin auf dem VPS. Die Variablen aus Stufe 2 sind vorhanden. Du erstellst einen neuen Ordner mit geschützten Zugangsdaten.

bash
sudo -i

Achtung: Falls Du soeben von einem normalen Benutzer zu root gewechselt bist, wiederhole die Variableneingabe und IP-Ermittlung aus Stufe 2 in der neuen Shell. Die folgenden Befehle setzen root-Rechte voraus.

bash
test ! -e /opt/nextcloud-kalender || { echo 'Ordner vorhanden: zuerst prüfen'; exit 1; }
umask 077
mkdir /opt/nextcloud-kalender
cd /opt/nextcloud-kalender
test -n "$CALENDAR_DOMAIN" && test -n "$NPM_PROXY_IP"
DB_SECRET=$(openssl rand -hex 24)
ADMIN_SECRET=$(openssl rand -hex 24)
cat > .env <<EOF
POSTGRES_DB=nextcloud
POSTGRES_USER=nextcloud
POSTGRES_PASSWORD=$DB_SECRET
POSTGRES_HOST=db
REDIS_HOST=redis
NEXTCLOUD_ADMIN_USER=kalender-admin
NEXTCLOUD_ADMIN_PASSWORD=$ADMIN_SECRET
NEXTCLOUD_TRUSTED_DOMAINS=$CALENDAR_DOMAIN
TRUSTED_PROXIES=$NPM_PROXY_IP
OVERWRITEHOST=$CALENDAR_DOMAIN
OVERWRITEPROTOCOL=https
OVERWRITECLIURL=https://$CALENDAR_DOMAIN
PHP_MEMORY_LIMIT=512M
PHP_UPLOAD_LIMIT=64M
TZ=Europe/Berlin
EOF
unset DB_SECRET ADMIN_SECRET
chmod 600 .env

Die .env gehört in Deinen geschützten Einrichtungsbereich, nicht in ein Git-Repository oder eine Supportnachricht. Den Adminzugang über Deinen privaten SSH-Zugang in einen Passwortmanager übernehmen. Keine Zugangsdaten in Screenshots zeigen.

Erstelle docker-compose.yml im gerade angelegten Ordner, z. B. mit nano docker-compose.yml:

yaml
name: nextcloud-kalender
x-nextcloud: &nextcloud
  image: nextcloud:35.0.1-apache@sha256:b1ae671e9815401b0e837b19b9c778e89887721d67f0c8f9d34aa1d27a9a208f
  restart: unless-stopped
  env_file: .env
  volumes:
    - ./html:/var/www/html
  networks: [intern]
services:
  db:
    image: postgres:17-bookworm@sha256:639ab7ceb90e13123085b741fb31ef493fba25463002f6da665352e7b534b652
    restart: unless-stopped
    mem_limit: 512m
    env_file: .env
    volumes:
      - ./postgres:/var/lib/postgresql/data
    networks: [intern]
    healthcheck:
      test: [CMD-SHELL, 'pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB"']
      interval: 10s
      timeout: 5s
      retries: 12
  redis:
    image: redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499
    restart: unless-stopped
    mem_limit: 128m
    command: [redis-server, --maxmemory, 96mb, --maxmemory-policy, noeviction]
    networks: [intern]
  web:
    <<: *nextcloud
    container_name: nextcloud-kalender-web
    mem_limit: 1g
    networks: [intern, proxy]
    volumes:
      - ./html:/var/www/html
      - ./apache-workers.conf:/etc/apache2/conf-enabled/kalender-workers.conf:ro
    depends_on:
      db: {condition: service_healthy}
      redis: {condition: service_started}
  cron:
    <<: *nextcloud
    mem_limit: 512m
    entrypoint: /cron.sh
    depends_on: [web]
networks:
  intern:
  proxy:
    external: true
    name: nextcloud-proxy

Web und Cron teilen sich die Nextcloud-Dateien; PostgreSQL speichert die Datenbank. Redis unterstützt Sperren und Cache. Es gibt kein ports: für diese Dienste. Die vier Container ergänzen den bestehenden Proxy. Die Versionen und Digests sind ein geprüfter Stand vom 02.10.2026; sie ersetzen keine spätere Updateprüfung.

Erstelle außerdem apache-workers.conf:

apache
<IfModule mpm_prefork_module>
  StartServers 1
  MinSpareServers 1
  MaxSpareServers 1
  ServerLimit 2
  MaxRequestWorkers 2
  MaxConnectionsPerChild 500
</IfModule>

Und den CLI-Helfer occ.sh:

bash
#!/usr/bin/env bash
set -euo pipefail
cd -- "$(dirname -- "$0")"
exec docker compose exec -T -u www-data web php -d apc.enable_cli=1 occ "$@"
bash
chmod 700 occ.sh
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps
./occ.sh status --output=json

Ergebnis: Nach der ersten Installation meldet status installed: true, maintenance: false und needsDbUpgrade: false. db ist gesund. Die Anzeige enthält in diesem Entwurf keine tatsächliche Testausgabe.

Hilfe: Bei „database not ready“ kurz warten und docker compose ps prüfen. Bei Neustarts gezielt docker compose logs --tail=80 web db lesen. Keine vollständige Compose-Konfiguration mit Passwörtern in öffentliche Nachrichten kopieren. docker compose down -v ist kein Diagnosebefehl.

Grundlage: Docker-Image und Konfiguration.

Stufe 4

HTTPS und CalDAV-Discovery einschalten

Ausgangszustand: Der Stack läuft intern. Die Domain ist korrekt aufgelöst. Du öffnest Deine vorhandene NPM-Verwaltung über den bereits eingerichteten geschützten Zugang.

Lege einen Proxy Host an:

NPM-Feld Wert
Domain Names Deine Kalender-Subdomain
Scheme http – die interne Containerverbindung
Forward Hostname / IP nextcloud-kalender-web
Forward Port 80
Access List Keine zusätzliche HTTP-Basic-Auth vor CalDAV
SSL Neues Let's-Encrypt-Zertifikat anfordern, danach Force SSL

Unter Advanced / Custom Nginx Configuration ergänzen:

nginx
proxy_hide_header Upgrade;
location = /.well-known/caldav {
    return 301 https://$host/remote.php/dav/;
}
location = /.well-known/carddav {
    return 301 https://$host/remote.php/dav/;
}

Nextcloud braucht den korrekten HTTPS-Host, einen gezielt vertrauten Proxy und diese Discovery-Weiterleitungen. Reverse-Proxy-Dokumentation.

Auf dem VPS prüfen:

bash
curl --fail --silent --show-error "https://$CALENDAR_DOMAIN/status.php"
curl --head "http://$CALENDAR_DOMAIN/"
curl --head "https://$CALENDAR_DOMAIN/.well-known/caldav"

Ergebnis: status.php liefert JSON. HTTP führt zu HTTPS. Die CalDAV-Adresse führt zu /remote.php/dav/. Im Browser öffnet sich die Anmeldung mit gültigem Zertifikat.

Hilfe: 502 weist häufig auf ein falsches Ziel oder ein fehlendes gemeinsames Netz hin. „Untrusted domain“ verlangt einen Vergleich mit NEXTCLOUD_TRUSTED_DOMAINS. Eine Zertifikatswarnung zuerst beheben; TLS-Prüfungen nicht mit curl -k umgehen. Scheitert die Zertifikatsanforderung, DNS, AAAA-Eintrag und Erreichbarkeit von 80/443 kontrollieren.

Stufe 5

Calendar, Tasks und eigene Benutzer

Ausgangszustand: Du kannst Dich als kalender-admin über HTTPS anmelden. Dieses Konto dient der Verwaltung. Familienmitglieder bekommen eigene Alltagskonten.

Auf dem Server:

bash
cd /opt/nextcloud-kalender
./occ.sh app:install calendar
./occ.sh app:install tasks
./occ.sh background:cron
./occ.sh app:list

Wenn eine App schon installiert ist, nicht erneut installieren: Zustand lesen und gegebenenfalls ./occ.sh app:enable calendar bzw. tasks verwenden. Im Stand 02.10.2026 sind Calendar 6.6.2 und Tasks 0.18.1 mit Nextcloud 35 kompatibel. Beim späteren Durcharbeiten die angebotene kompatible Version prüfen.

Im Adminmenü öffnest Du Benutzer und legst mira, alex und sam an. Jede Person bekommt ein eigenes Passwort. Keine Mitgliedschaft in der Gruppe admin; diese bleibt beim Verwaltungskonto. E-Mail-Adressen nur eintragen, wenn Du sie kennst. Mit Übungskonten keine echten Einladungsempfänger verwenden.

Melde Dich ab und als Mira wieder an. Stelle unter den persönlichen Einstellungen Sprache, Zeitzone und gegebenenfalls E-Mail-Adresse ein. Prüfe vor dem ersten Termin Europe/Berlin. Richte für Geräte in den Sicherheitseinstellungen ein eigenes App-Passwort ein. Eine verlorene Geräteverbindung lässt sich so einzeln widerrufen. Gerätezugänge.

Ergebnis: Drei getrennte Alltagskonten, ein Verwaltungskonto und die beiden Apps sind vorhanden. Im Verwaltungsbereich ist Cron ausgewählt; der Cron-Container läuft. Hintergrundjobs.

Hilfe: Eine Mailadresse macht aus dem Konto keine Anmeldung über diesen Mailanbieter. Änderungen an .env setzen keine vorhandenen Benutzerpasswörter zurück.

Nextcloud · eigene Testdaten
Nextcloud-Anmeldung der geprüften Einrichtung; keine Zugangsdaten im Bild.
Nextcloud-Anmeldung der geprüften Einrichtung; keine Zugangsdaten im Bild.
Stufe 6

Kalender teilen und Rechte wirklich prüfen

Ausgangszustand: Mira öffnet die Calendar-App. Ein persönlicher Kalender ist vorhanden. Alex und Sam können sich mit ihren eigenen Konten anmelden.

Mira erstellt links über Neuer Kalender die vier Kalender aus der Tabelle am Anfang. Im Menü des jeweiligen Kalenders öffnet sie die Freigabe, sucht den gewünschten Benutzer und aktiviert die Bearbeitungsmöglichkeit. Betreuung, Schule und Elternaufgaben gehen an Alex und Sam. Beratung geht nur an Sam. Der persönliche Kalender bleibt privat.

Lege mit Mira im Schulkalender einen fiktiven Termin „TEST – Elternabend“ an: 09.10.2026, 18:00–19:00, Europe/Berlin, Beschreibung „Mira nimmt teil“. Keine Teilnehmeradressen eintragen: Freigaben zwischen Benutzerkonten brauchen keine Veranstaltungseinladung.

Öffne mit Alex ein separates Browserprofil oder privates Fenster. Alex ergänzt „Formular mitbringen“. Mira muss dieselbe Änderung sehen. Prüfe mit Alex außerdem, dass Beratung und Miras persönlicher Kalender fehlen. Ein Browserfenster mit einer alten Mira-Sitzung wäre kein Berechtigungstest.

Ergebnis: Die freigegebenen Termine sind gemeinsam bearbeitbar. Die gezielt privaten Bereiche sind im anderen Konto nicht sichtbar. Entferne den Testtermin nach der Prüfung.

Hilfe: Sichtbarkeit und Schreibrecht sind unterschiedliche Einstellungen. Ein öffentlicher Kalenderlink ist eine andere Funktion als das Teilen mit einem Benutzer. Für Familien- und Kundendaten verwende hier Benutzerfreigaben. Kalenderfreigaben.

Nextcloud · eigene Testdaten
Echte Kalenderansicht mit fiktiven Personen und Beispielterminen.
Echte Kalenderansicht mit fiktiven Personen und Beispielterminen.
Stufe 7

Betreuung, Serien und Elternaufgaben organisieren

Ausgangszustand: Die Freigaben funktionieren. Jetzt prüfst Du den Arbeitsablauf mit Beispielen.

Lege „TEST – Robin bei Alex“ im Betreuungskalender als wiederkehrenden Zeitraum an. Verschiebe anschließend eine einzelne Wiederholung, nicht die gesamte Serie. Kontrolliere auch eine Wiederholung nach einer Zeitumstellung: Ein Termin um 17 Uhr soll lokal um 17 Uhr bleiben.

Stelle den Betreuungskalender auf die Kalendertransparenz „Nie als beschäftigt anzeigen“, sofern die aktuelle Oberfläche diese Einstellung anbietet. Ein Aufenthaltsblock beschreibt, wo Robin ist. Er sagt noch nicht, welche erwachsene Person zu welcher Zeit gebunden ist. Sam trägt eine tatsächliche Abholpflicht deshalb zusätzlich als beschäftigten Termin in Sams persönlichen Kalender ein. Diese Trennung später bei Buchungen gezielt prüfen.

Wechsle zur Tasks-App. Lege dort die gemeinsame Liste „Robin – Elternaufgaben“ an, falls sie noch nicht existiert, und teile sie gezielt mit Alex und Sam. Listen verwenden CalDAV-Kalender; der gleichnamige Bereich kann bereits vorhanden sein. Bestehende Liste verwenden, nicht doppelt anlegen.

Erstelle die Aufgabe „TEST – Schulformular abgeben“ mit Fälligkeit 09.10.2026 und Beschreibung:

text
Zuständig: Sam
Ergebnis: Formular ausgefüllt und abgegeben.
Rückmeldung: Datum der Abgabe hier ergänzen.

Sam öffnet die Liste, ergänzt die Rückmeldung und markiert die Aufgabe als erledigt. Mira kontrolliert den Status. Die Beschreibung hält die Zuständigkeit fest; daraus folgt keine automatische Zuweisung oder garantierte Benachrichtigung. Tasks-Funktionen.

Ergebnis: Eine Serienausnahme verändert nur den gewählten Termin. Eine Aufgabe hat Fälligkeit, nachvollziehbare Zuständigkeit und gemeinsamen Erledigungsstatus. Entferne die Übungsserie und Aufgabe anschließend.

Hilfe: Zeigt ein Endgerät die Aufgaben nicht, kann trotzdem die Terminsynchronisierung funktionieren. VTODO-Aufgaben benötigen eine passende Aufgaben-App. Die Nextcloud-Datei-App allein ersetzt keinen CalDAV-Client.

Nextcloud · eigene Testdaten
Echte Tasks-Ansicht mit Fälligkeit und Erledigungsstatus. Die Zuständigkeit steht im Reiter Notizen.
Echte Tasks-Ansicht mit Fälligkeit und Erledigungsstatus. Die Zuständigkeit steht im Reiter Notizen.
Stufe 8

Mehrere Kalender auf dem Gerät anzeigen

Ausgangszustand: Die Nextcloud-Kalender funktionieren im Browser. Für Dein Gerät legst Du unter persönlichen Sicherheitseinstellungen ein eigenes App-Passwort an. Du verwendest das Alltagskonto, nicht das Admin-Konto.

Der Hauptweg für die Übung ist Thunderbird am Rechner:

  1. Öffne die Kalenderansicht und lege einen neuen Kalender im Netzwerk an.
  2. Verwende Deinen Nextcloud-Benutzernamen und https://DEINE-DOMAIN/remote.php/dav/ als Adresse. DEINE-DOMAIN hier durch die Domain aus Stufe 2 ersetzen.
  3. Melde Dich mit dem App-Passwort an und wähle die gewünschten gefundenen Kalender.
  4. Erstelle „TEST – Synchronisierung“ in einem freigegebenen Nextcloud-Kalender.
  5. Prüfe den Termin im Nextcloud-Browser. Ändere dort die Beschreibung und synchronisiere Thunderbird erneut.

Bei Problemen kopiere die konkrete CalDAV-Adresse aus den Nextcloud-Kalendereinstellungen statt eines selbst erratenen Kalenderpfads. Thunderbird-Anleitung.

Zusätzliche Gerätewege: Android verwendet beispielsweise DAVx⁵ zur CalDAV-Synchronisierung und eine Kalender-App zur Ansicht. Unter iOS legst Du ein CalDAV-Konto an; die genaue Menüposition hängt von der iOS-Version ab. Verwende auch hier pro Gerät ein eigenes App-Passwort. Android, iOS.

Deinen vorhandenen mailbox.org-Kalender dazunehmen

Lege in derselben Kalender-App ein zweites CalDAV-Konto für mailbox.org an. Server: https://dav.mailbox.org/caldav/, Benutzername: Deine vollständige mailbox.org-Adresse. Verwende einen nach Deinen mailbox.org-Sicherheitseinstellungen gültigen Zugang. Die Zugangsdaten gehören ausschließlich auf Deine eigenen Geräte. mailbox.org-Verbindungsdaten.

Ergebnis: Die App kann Nextcloud und mailbox.org zusammen anzeigen. Die Daten bleiben jeweils beim zugehörigen Anbieter. Beim Erstellen eines Termins entscheidet das Kalenderfeld, wo Du ihn speicherst: Ein neuer gemeinsamer Schultermin gehört in den Nextcloud-Schulkalender.

Die Nextcloud-Webansicht erhält durch das zweite Gerätekonto keinen Zugriff auf den mailbox.org-Kalender. Ebenso werden mailbox.org-Termine dadurch nicht automatisch zu Sperrzeiten einer Nextcloud-Buchungsseite. Eine spätere serverseitige Synchronisierung wäre eine eigene Einrichtung.

Stufe 9

Eine externe Buchung getrennt ausprobieren

Ausgangszustand: Deine privaten Kalender sind korrekt freigegeben. Du kennst die konkreten Regeln für ein einfaches Beratungsgespräch. Die folgenden Schritte sind eine Übung mit fiktiven Daten, kein fertiges Angebot für Sitzungen oder Gruppen.

Öffne in Calendar den Bereich Terminpläne / Appointments. Trage zuvor Deine E-Mail-Adresse in den persönlichen Einstellungen ein; ohne sie bietet die Oberfläche keine Einrichtung an. Erstelle ein Probeangebot: „TEST – Kennenlerngespräch“, 30 Minuten, ein Gastgeber, begrenzte Verfügbarkeiten. Prüfe, welche Vorlauf-, Puffer- und Konfliktkalenderoptionen die installierte Version tatsächlich anbietet. Wähle den persönlichen Kalender mit den echten Sperrzeiten des Gastgebers und den Beratungskalender gezielt aus. Betreuung und mailbox.org sind nicht automatisch passende Konfliktkalender.

Prüfe vor externen Tests den E-Mail-Versand. Als Admin: Verwaltungseinstellungen → Grundeinstellungen → E-Mail-Server. Für mailbox.org beispielsweise SMTP, SSL/TLS, smtp.mailbox.org, Port 465, Authentifizierung und vollständiger Benutzername. Den Absender nur auf eine Adresse setzen, für die Dein Konto senden darf. Passwörter privat eingeben; erst eine Testmail an Dich selbst senden. Hier den UI-Weg verwenden, keine zusätzlichen SMTP_*-Werte in .env, die ihn überschreiben. mailbox.org SMTP.

Teste die Buchungsseite ausschließlich selbst in einem privaten Browserfenster:

  • Welche Daten sieht ein Gast? Private Termintitel und Familienkalender dürfen dort nicht erscheinen.
  • Blockiert ein bewusst belegter persönlicher Termin den Slot?
  • Verschwindet ein per E-Mail bestätigter Slot für die nächste Buchung? Eine noch unbestätigte Anfrage kann weiterhin buchbar sein; den Bestätigungslink deshalb wirklich öffnen.
  • Kommen die nötigen Bestätigungen an, und ist der Zeitpunkt der verbindlichen Buchung klar?
  • Funktionieren Absage und Zeitzonenanzeige nach den Regeln des Angebots? Einen automatischen Absagelink nicht voraussetzen; der dokumentierte Kalenderablauf sieht eine Kontaktaufnahme zum Gastgeber vor.

Nur die Buchungsseite darf später extern verlinkt werden, kein interner Familienkalender. Eine erforderliche manuelle Annahme, mehrere Gastgeber oder Gruppenplätze sind eigene Anforderungen; erst prüfen, ob die gewählte Funktion sie abbildet. Appointment-Dokumentation.

Ergebnis: Du kannst einen einfachen eigenen Buchungsablauf erklären und seine Grenzen benennen. Erst nach erfolgreicher Prüfung und passenden Website-Informationen wird aus dem Probeangebot ein öffentliches Angebot.

Stufe 10

Sichern, wiederherstellen und Updates durchführen

Ausgangszustand: Deine Testinstanz funktioniert. Ein Container-Neustart ist kein Backup. Termine, Freigaben und Aufgaben benötigen die Datenbank; zusätzlich müssen Konfiguration, Apps und Dateien gesichert werden. Backup-Grundlagen.

Für die kleine Kursinstanz stoppen wir Web und Cron während der Sicherung kurz. Datenbank und Dateikopie stammen so aus demselben ruhenden Zustand. Das Skript startet die beiden Dienste auch bei einem Sicherungsfehler wieder.

Erzeuge auf dem VPS als root einen getrennten Schlüssel. Übernimm eine geschützte Kopie in Deinen privaten Backupbereich außerhalb des VPS. Ohne Schlüssel lässt sich die Sicherung nicht entschlüsseln:

bash
umask 077
test ! -e /root/.nextcloud-kalender-backup.key || { echo 'Schlüssel bereits vorhanden: nicht überschreiben'; exit 1; }
openssl rand -hex 32 > /root/.nextcloud-kalender-backup.key
mkdir -p /var/backups/nextcloud-kalender

Erstelle /opt/nextcloud-kalender/backup.sh:

bash
#!/usr/bin/env bash
set -euo pipefail
umask 077
cd /opt/nextcloud-kalender
key=/root/.nextcloud-kalender-backup.key
target=/var/backups/nextcloud-kalender
test -s "$key"
mkdir -p "$target"
exec 9>/run/lock/nextcloud-kalender-backup.lock
flock -n 9 || exit 0
tmp=$(mktemp -d /opt/nextcloud-kalender/.backup-XXXXXXXX)
stopped=0
cleanup() {
  result=$?
  trap - EXIT
  if [ "$stopped" = 1 ]; then
    docker compose start web cron || result=1
  fi
  case "$tmp" in /opt/nextcloud-kalender/.backup-*) rm -rf -- "$tmp" ;; esac
  exit "$result"
}
trap cleanup EXIT
stamp=$(date -u +%Y%m%dT%H%M%SZ)
archive="$target/$stamp.tar.gz.enc"
stopped=1
docker compose stop web cron
docker compose exec -T db sh -c \
  'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" --format=custom' > "$tmp/database.dump"
cp docker-compose.yml .env apache-workers.conf occ.sh backup.sh "$tmp/"
cp -a html "$tmp/html"
tar -czf - -C "$tmp" . | openssl enc -aes-256-cbc -salt -pbkdf2 \
  -iter 200000 -pass file:"$key" -out "$archive.partial"
openssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 -pass file:"$key" \
  -in "$archive.partial" | tar -tzf - >/dev/null
mv -- "$archive.partial" "$archive"
sha256sum "$archive" > "$archive.sha256"
echo "Sicherung erstellt: $stamp"
bash
cd /opt/nextcloud-kalender
chmod 700 backup.sh
bash -n backup.sh
./backup.sh
docker compose ps

Die Sicherungen sind nur für root lesbar. Dein normaler SSH-Benutzer aus dem VPS-Kurs braucht deshalb eine geschützte Exportkopie; Root-Login bleibt deaktiviert. Zuerst auf dem VPS als root:

bash
read -r -p 'Dein normaler SSH-Benutzer (nicht root): ' BACKUP_SSH_USER
test "$BACKUP_SSH_USER" != root
BACKUP_SSH_GROUP=$(id -gn "$BACKUP_SSH_USER")
BACKUP_SSH_HOME=$(getent passwd "$BACKUP_SSH_USER" | cut -d: -f6)
test -n "$BACKUP_SSH_HOME" && test -d "$BACKUP_SSH_HOME"
install -d -m 700 -o "$BACKUP_SSH_USER" -g "$BACKUP_SSH_GROUP" \
  "$BACKUP_SSH_HOME/nextcloud-backups" "$BACKUP_SSH_HOME/nextcloud-schluessel"
for BACKUP_EXPORT_FILE in /var/backups/nextcloud-kalender/*.tar.gz.enc /var/backups/nextcloud-kalender/*.sha256; do
  test -f "$BACKUP_EXPORT_FILE" || continue
  install -m 600 -o "$BACKUP_SSH_USER" -g "$BACKUP_SSH_GROUP" \
    "$BACKUP_EXPORT_FILE" "$BACKUP_SSH_HOME/nextcloud-backups/"
done
install -m 600 -o "$BACKUP_SSH_USER" -g "$BACKUP_SSH_GROUP" \
  /root/.nextcloud-kalender-backup.key "$BACKUP_SSH_HOME/nextcloud-schluessel/backup.key"

Dann im lokalen Windows-Terminal; den SSH-Alias ersetzen. Die Schlüsselkopie liegt in einem getrennten, auf Dein Windows-Konto begrenzten Ordner:

powershell
New-Item -ItemType Directory -Force ./nextcloud-backups | Out-Null
New-Item -ItemType Directory -Force ./nextcloud-schluessel | Out-Null
$BackupIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
icacls ./nextcloud-schluessel /inheritance:r /grant:r "${BackupIdentity}:(OI)(CI)F"
scp 'meinserver:nextcloud-backups/*.tar.gz.enc' ./nextcloud-backups/
scp 'meinserver:nextcloud-backups/*.sha256' ./nextcloud-backups/
scp 'meinserver:nextcloud-schluessel/backup.key' ./nextcloud-schluessel/
Get-FileHash ./nextcloud-backups/*.tar.gz.enc -Algorithm SHA256

Vergleiche den Hash mit dem entsprechenden Wert in der .sha256-Datei. Er prüft die Übertragung; er ist keine Signatur. Die gewählte OpenSSL-Verschlüsselung bietet Vertraulichkeit, aber keine integrierte Authentizitätsprüfung. Schlüssel und Sicherungen deshalb geschützt aufbewahren. Plane Rotation und freien Speicher; dieses einfache Kursskript löscht keine alten Sicherungen.

Wiederherstellung in einem eigenen Teststack

Erstelle einen markierten Übungstermin vor der Sicherung. Stelle diese Sicherung in einem neuen Ordner wieder her. Verwende ausschließlich Deine eigene Sicherung und Deinen Schlüssel. Nicht über den produktiven Ordner entpacken.

Auf dem VPS, als root:

bash
read -r -p 'Absoluter Pfad zu Deiner eigenen .tar.gz.enc-Sicherung: ' BACKUP_FILE
test -f "$BACKUP_FILE"
RESTORE_DIR=$(mktemp -d /opt/nextcloud-kalender-restore-XXXXXXXX)
openssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 \
  -pass file:/root/.nextcloud-kalender-backup.key -in "$BACKUP_FILE" \
  | tar -xzf - -C "$RESTORE_DIR"
cd "$RESTORE_DIR"

Die wiederhergestellte normale Compose-Datei nicht starten. Erstelle stattdessen compose.restore.yml:

yaml
services:
  db:
    image: postgres:17-bookworm@sha256:639ab7ceb90e13123085b741fb31ef493fba25463002f6da665352e7b534b652
    env_file: .env
    mem_limit: 512m
    volumes: [./restore-postgres:/var/lib/postgresql/data]
    networks: [isoliert]
    healthcheck:
      test: [CMD-SHELL, 'pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB"']
      interval: 5s
      timeout: 5s
      retries: 20
  redis:
    image: redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499
    mem_limit: 128m
    networks: [isoliert]
  web:
    image: nextcloud:35.0.1-apache@sha256:b1ae671e9815401b0e837b19b9c778e89887721d67f0c8f9d34aa1d27a9a208f
    env_file: .env
    mem_limit: 1g
    environment:
      OVERWRITEHOST: localhost:8780
      OVERWRITEPROTOCOL: http
      OVERWRITECLIURL: http://localhost:8780
      NEXTCLOUD_TRUSTED_DOMAINS: localhost
    volumes: [./html:/var/www/html]
    networks: [isoliert]
networks:
  isoliert:
    internal: true

Dieses Testnetz hat keinen externen Zugang. Es gibt keinen Cron, keinen öffentlichen Port und keine Verbindung zu NPM. Gleiche Servicenamen bedeuten hier eigene Dienste im getrennten Compose-Projekt.

bash
RESTORE_PROJECT=$(basename "$RESTORE_DIR" | tr '[:upper:]' '[:lower:]')
docker compose -p "$RESTORE_PROJECT" -f compose.restore.yml up -d --wait db redis
docker compose -p "$RESTORE_PROJECT" -f compose.restore.yml exec -T db \
  pg_restore -U nextcloud -d nextcloud --no-owner --no-privileges --exit-on-error < database.dump
cat > html/config/zzz-restore.config.php <<'PHPRESTORE'
<?php
$CONFIG = [
  'dbuser' => getenv('POSTGRES_USER'),
  'dbpassword' => getenv('POSTGRES_PASSWORD'),
  'trusted_domains' => ['localhost', '127.0.0.1'],
  'overwritehost' => 'localhost:8780',
  'overwriteprotocol' => 'http',
  'overwrite.cli.url' => 'http://localhost:8780',
  'mail_smtphost' => '127.0.0.1',
  'mail_smtpport' => 9,
];
PHPRESTORE
chmod 644 html/config/zzz-restore.config.php
docker compose -p "$RESTORE_PROJECT" -f compose.restore.yml up -d web

Nextcloud kann bei der Installation einen eigenen Datenbankbenutzer wie oc_admin anlegen. Dieser gehört zum alten Datenbankcluster. Der Test verwendet deshalb die Zugangsdaten seiner neu angelegten Datenbank und importiert keine alten Rollen oder Berechtigungen. zzz-restore.config.php ist ausschließlich für diesen Teststack. Nach dem Start dieselbe Test-Compose-Datei bei jedem Aufruf verwenden:

bash
docker compose -p "$RESTORE_PROJECT" -f compose.restore.yml exec -T -u www-data web \
  php -d apc.enable_cli=1 occ status --output=json
docker compose -p "$RESTORE_PROJECT" -f compose.restore.yml exec -T -u www-data web \
  php -d apc.enable_cli=1 occ user:list
RESTORE_IP=$(docker compose -p "$RESTORE_PROJECT" -f compose.restore.yml ps -q web \
  | xargs docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}')
printf 'Nur für Deinen SSH-Tunnel: %s\n' "$RESTORE_IP"

Öffne lokal einen SSH-Tunnel, ersetze TEST-CONTAINER-IP durch die soeben ermittelte Adresse und meinserver durch Deinen SSH-Alias:

text
ssh -N -L 127.0.0.1:8780:TEST-CONTAINER-IP:80 meinserver

Im Browser http://localhost:8780 öffnen. Dieses HTTP bleibt innerhalb des SSH-Tunnels. Prüfe den wiederhergestellten Testtermin, die Aufgabe und die Freigaben mit den bekannten Testkonten. Eine bloße Auflistung der Benutzer beweist noch keine vollständige Wiederherstellung. Wiederherstellungsgrundlagen.

Stoppe anschließend nur den Teststack, in dessen Verzeichnis:

bash
docker compose -p "$RESTORE_PROJECT" -f compose.restore.yml down

Der Testordner enthält geschützte wiederhergestellte Daten. Seine spätere Entfernung erfordert die Kontrolle des konkreten Pfads. Der Produktivstack und seine Daten bleiben erhalten.

Regelmäßiger Betrieb und Updates

Ergänze erst nach erfolgreicher Sicherung und Wiederherstellungsprobe mit crontab -e eine Zeile. Bestehende Zeilen erhalten:

cron
13 6 * * * /opt/nextcloud-kalender/backup.sh >> /var/log/nextcloud-kalender-backup.log 2>&1

Die Zeit gilt in der Zeitzone des Servers. Kontrolliere die nächste tatsächliche Ausführung und hole die Sicherung außerhalb des VPS ab. Die Schlüsselkopie gehört nicht in dasselbe ungeschützte Verzeichnis wie die Archive.

Vor einem Update: Kompatibilität von Server, Calendar und Tasks prüfen, frisch sichern und einen Rückweg bereithalten. Dann die Image-Version samt Digest gezielt aktualisieren, Web und Cron auf derselben Nextcloud-Version halten und docker compose pull / up -d ausführen. Danach occ status, Cron, Login, gemeinsame Bearbeitung und CalDAV prüfen. Datenbank-Major-Upgrades sind ein eigener Schritt. Ein älteres Image über eine bereits aktualisierte Datenbank zu starten ist kein verlässliches Rollback.

Abschlussaufgabe

Deine Einrichtung selbst prüfen

Bestätige nur Ergebnisse, die Du selbst geprüft hast. Deine Häkchen werden ausschließlich in diesem Browser gespeichert. Sie sind Deine Selbstauskunft, keine automatische Serverprüfung.

Beispieldateien und Betriebscheckliste · Unser Prüfprotokoll

Lernkontrolle

Entscheidungen und Fehlerfälle

Sechs Fragen; ab fünf richtigen Antworten ist die Lernkontrolle bestanden. Jede Auswertung erklärt Dir die richtige Entscheidung. Für den Teilnahmenachweis bestätigst Du zusätzlich die sieben Ergebnisse der Abschlussaufgabe.

1. Alex sieht Beratungstermine. Was prüfst Du zuerst?
2. Die Handy-App zeigt mailbox.org und Nextcloud zusammen. Sind mailbox.org-Termine deshalb in der Nextcloud-Buchungsseite berücksichtigt?
3. Robin ist eine Woche bei Alex. Was verhindert unnötig blockierte Beratungsslots?
4. Beim Backup schlägt der Datenbank-Dump fehl. Was muss danach passieren?
5. Warum verwendest Du beim Restore eine eigene Compose-Datei?
6. Calendar geht im Browser, Thunderbird findet nichts. Welcher nächste Schritt hilft?

Abschluss

Dein Teilnahmenachweis

Für den Nachweis brauchst Du eine bestandene Lernkontrolle und Deine sieben bestätigten Übungsergebnisse.