Deployment-Rules for second server
This commit is contained in:
@@ -0,0 +1,410 @@
|
||||
# mareike auf endeavour ausrollen (KeyHelp-Server mit Docker)
|
||||
|
||||
Diese Anleitung beschreibt, wie mareike **zusätzlich** zum bestehenden Server auf *endeavour* läuft. Endeavour ist ein
|
||||
Server, der mit KeyHelp verwaltet wird. Die Parent-Domain ist `meine-anmeldung.digital`.
|
||||
|
||||
KeyHelp selbst nutzt kein Docker. mareike läuft deshalb in zwei Containern **neben** KeyHelp. KeyHelp übernimmt
|
||||
weiter, was es gut kann: Domains, Let's-Encrypt-Zertifikate, MariaDB, Postfächer und Backups.
|
||||
|
||||
> **Der bestehende Server bleibt unberührt.** Für endeavour gibt es eigene Dateien unter `docker/endeavour/`.
|
||||
> `docker-compose.prod`, `docker/prod.Dockerfile`, `docker/run-mareike.sh`, `docker/nginx/default.conf` und
|
||||
> `.dockerignore` werden nicht verändert und von endeavour nicht benutzt.
|
||||
|
||||
---
|
||||
|
||||
## 1. Überblick
|
||||
|
||||
```
|
||||
Browser ──HTTPS──▶ KeyHelp-Webserver (Apache/nginx, Let's Encrypt, eine Domain je Tenant)
|
||||
│ Proxy, Host-Header bleibt erhalten
|
||||
▼
|
||||
127.0.0.1:8080 ──▶ Container "web" (nginx, liefert public/ aus)
|
||||
│ FastCGI
|
||||
▼
|
||||
Container "app" (PHP 8.5-FPM, mareike)
|
||||
│ Unix-Socket /run/mysqld/mysqld.sock
|
||||
▼
|
||||
KeyHelp-MariaDB (Host)
|
||||
```
|
||||
|
||||
- **Tenant-Erkennung:** `IdentifyTenant` sucht den Tenant über `tenants.url` = Host der Anfrage. Deshalb muss der
|
||||
Proxy den Host-Header unverändert weitergeben, und jede Tenant-Domain braucht exakt diesen Eintrag in `tenants.url`.
|
||||
- **HTTPS:** TLS endet bei KeyHelp. Die nginx im Container meldet PHP trotzdem `HTTPS=on`, damit Laravel `https://`-Links
|
||||
erzeugt. Eine Code-Änderung ist dafür nicht nötig.
|
||||
- **Port 8080** ist nur an `127.0.0.1` gebunden und von außen nicht erreichbar.
|
||||
|
||||
### Dateien im Repo
|
||||
|
||||
| Datei | Zweck |
|
||||
|---|---|
|
||||
| `docker/endeavour/Dockerfile` | Multi-Stage-Build. Target `app` enthält PHP-FPM, Code, vendor/ und Assets, Target `web` enthält nginx und public/. |
|
||||
| `docker/endeavour/Dockerfile.dockerignore` | Eigener Build-Kontext. Unter anderem kommt **keine `.env` ins Image**. |
|
||||
| `docker/endeavour/compose.yaml` | Stack mit den Services `app` und `web`. |
|
||||
| `docker/endeavour/nginx.conf` | nginx im Container, inkl. `HTTPS on` und echter Client-IP. |
|
||||
| `docker/endeavour/php.ini` | Upload-Größen, Speicher, OPcache. |
|
||||
| `docker/endeavour/env.production.example` | Vorlage für die `.env` auf dem Server. |
|
||||
| `docker/endeavour/deploy.sh` | Deploy-Skript: pullen, bauen, migrieren, starten. |
|
||||
|
||||
### Verzeichnisse auf endeavour
|
||||
|
||||
```
|
||||
/opt/mareike/
|
||||
├── bin/deploy.sh Kopie von docker/endeavour/deploy.sh
|
||||
├── src/ Git-Checkout (wird bei jedem Deploy aktualisiert)
|
||||
├── shared/.env Konfiguration mit Zugangsdaten (nur root lesbar)
|
||||
└── storage/ Laravel-storage/ (Logs, Sessions, erzeugte PDFs/ZIPs) – persistent
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Docker installieren
|
||||
|
||||
Als root auf endeavour. Das Beispiel gilt für Debian, bei Ubuntu steht in den Pfaden `ubuntu` statt `debian`.
|
||||
|
||||
```bash
|
||||
apt-get update
|
||||
apt-get install -y ca-certificates curl
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
|
||||
chmod a+r /etc/apt/keyrings/docker.asc
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
|
||||
https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
|
||||
> /etc/apt/sources.list.d/docker.list
|
||||
apt-get update
|
||||
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
```
|
||||
|
||||
Log-Rotation für alle Container (`/etc/docker/daemon.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
"log-driver": "json-file",
|
||||
"log-opts": { "max-size": "10m", "max-file": "5" }
|
||||
}
|
||||
```
|
||||
|
||||
Docker soll erst **nach** MariaDB starten, weil der Container das Socket-Verzeichnis `/run/mysqld` einbindet:
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/systemd/system/docker.service.d
|
||||
cat > /etc/systemd/system/docker.service.d/after-mariadb.conf <<'EOF'
|
||||
[Unit]
|
||||
After=mariadb.service
|
||||
Wants=mariadb.service
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
systemctl restart docker
|
||||
docker run --rm hello-world
|
||||
```
|
||||
|
||||
> **KeyHelp-Firewall:** Docker legt eigene iptables-Regeln an. Lädt KeyHelp seine Firewall neu, können diese Regeln
|
||||
> verloren gehen. Die Container sind dann nicht mehr erreichbar (502 im Browser). Abhilfe ist
|
||||
> `systemctl restart docker`. Weil mareike nur an `127.0.0.1` lauscht, öffnet Docker nach außen keine Ports.
|
||||
|
||||
---
|
||||
|
||||
## 3. Systemnutzer, Verzeichnisse, Repository
|
||||
|
||||
Die Container laufen mit der UID eines eigenen Host-Nutzers, damit `storage/` sauber beschreibbar ist. Der Name weicht
|
||||
bewusst von den KeyHelp-Konten ab, weil KeyHelp für seine Konten selbst Systemnutzer anlegt.
|
||||
|
||||
```bash
|
||||
getent passwd 2000 || useradd --system --uid 2000 --user-group --no-create-home --shell /usr/sbin/nologin mareike-docker
|
||||
# Ist UID 2000 belegt, eine andere freie UID wählen. deploy.sh liest sie automatisch aus.
|
||||
|
||||
install -d -m 0755 /opt/mareike /opt/mareike/bin
|
||||
install -d -m 0700 /opt/mareike/shared
|
||||
install -d -m 0775 -o mareike-docker -g mareike-docker /opt/mareike/storage
|
||||
```
|
||||
|
||||
Deploy-Key für das Repository anlegen (nur Lesezugriff):
|
||||
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -f /root/.ssh/mareike_deploy -N "" -C "endeavour-deploy"
|
||||
cat /root/.ssh/mareike_deploy.pub # in Gitea beim Repo th.guenther/mareike als Deploy-Key (read-only) eintragen
|
||||
|
||||
cat >> /root/.ssh/config <<'EOF'
|
||||
Host git.zahlenlabyrinth.de
|
||||
Port 9922
|
||||
User git
|
||||
IdentityFile /root/.ssh/mareike_deploy
|
||||
IdentitiesOnly yes
|
||||
EOF
|
||||
|
||||
git clone ssh://git@git.zahlenlabyrinth.de:9922/th.guenther/mareike.git /opt/mareike/src
|
||||
install -m 0750 /opt/mareike/src/docker/endeavour/deploy.sh /opt/mareike/bin/deploy.sh
|
||||
```
|
||||
|
||||
> Ändert sich `docker/endeavour/deploy.sh` im Repo, danach den `install`-Befehl erneut ausführen.
|
||||
|
||||
---
|
||||
|
||||
## 4. Datenbank in KeyHelp anlegen
|
||||
|
||||
1. Im KeyHelp-Panel ein Konto für mareike anlegen oder ein vorhandenes wählen, z. B. `anmeldung`. Unter diesem Konto
|
||||
liegen später auch die Domains.
|
||||
2. Unter **Datenbanken** eine neue MariaDB-Datenbank mit Benutzer anlegen. Der Benutzer ist `@localhost`. Den
|
||||
Remote-Zugriff **nicht** aktivieren, denn der Container verbindet sich über den Unix-Socket und gilt damit als
|
||||
`localhost`.
|
||||
3. Den Socket-Pfad prüfen:
|
||||
|
||||
```bash
|
||||
mysql -e "SELECT @@socket;" # erwartet: /run/mysqld/mysqld.sock
|
||||
```
|
||||
|
||||
Liegt der Socket woanders, in `/opt/mareike/bin/deploy.sh` die Variable `MAREIKE_DB_SOCKET_DIR` auf das Verzeichnis
|
||||
setzen und `DB_SOCKET` in der `.env` anpassen.
|
||||
|
||||
Weil die Datenbank von KeyHelp verwaltet wird, ist sie automatisch im **KeyHelp-Backup** enthalten.
|
||||
|
||||
---
|
||||
|
||||
## 5. `.env` anlegen
|
||||
|
||||
```bash
|
||||
install -m 0600 /opt/mareike/src/docker/endeavour/env.production.example /opt/mareike/shared/.env
|
||||
echo "base64:$(openssl rand -base64 32)" # Ergebnis als APP_KEY eintragen
|
||||
nano /opt/mareike/shared/.env
|
||||
```
|
||||
|
||||
Die wichtigsten Punkte:
|
||||
|
||||
- `APP_KEY` wird einmal erzeugt und dann **nie wieder geändert**, weil Laravel damit verschlüsselte Daten sonst nicht
|
||||
mehr lesen kann.
|
||||
- `APP_URL` ist die Domain des Dach-Tenants `lv` (siehe Abschnitt 6).
|
||||
- `DB_*` sind die Daten aus Schritt 4. `DB_HOST=localhost` und `DB_SOCKET` bleiben wie in der Vorlage.
|
||||
- `MAIL_*`: Am einfachsten ist ein Postfach in KeyHelp, etwa `noreply@meine-anmeldung.digital`. Als Host den
|
||||
Mail-Hostnamen des Servers eintragen, mit Port 587.
|
||||
- `WEBDAV_*`: **eigener Zielordner**, damit sich endeavour und der bestehende Server nicht gegenseitig Dateien
|
||||
überschreiben.
|
||||
- `PROVIDER` **muss leer bleiben.** Mit `bdp-lv-sachsen` würde `db:seed` die Sachsen-Tenants samt Personendaten
|
||||
einspielen.
|
||||
- `ASSET_URL` nicht setzen, dann kommen die Assets von der jeweiligen Tenant-Domain.
|
||||
- In der Datei keine `${VAR}`-Verweise verwenden, sondern alle Werte ausschreiben.
|
||||
|
||||
---
|
||||
|
||||
## 6. Erstinstallation
|
||||
|
||||
### 6.1 Erstes Deployment
|
||||
|
||||
```bash
|
||||
/opt/mareike/bin/deploy.sh main # oder einen Release-Tag, z. B. 4.9.1
|
||||
```
|
||||
|
||||
Das Skript holt den Code, baut beide Images, legt die Tabellen per `migrate` an und startet die Container. Der erste
|
||||
Build dauert ein paar Minuten.
|
||||
|
||||
Kurztest:
|
||||
|
||||
```bash
|
||||
/opt/mareike/bin/deploy.sh compose ps
|
||||
curl -sI http://127.0.0.1:8080/ | head -1 # 404 ist hier richtig: es gibt noch keinen Tenant
|
||||
```
|
||||
|
||||
### 6.2 Stammdaten einspielen
|
||||
|
||||
```bash
|
||||
/opt/mareike/bin/deploy.sh artisan db:seed --force
|
||||
```
|
||||
|
||||
Mit leerem `PROVIDER` läuft nur der `ProductionDataSeeder`. Er legt Rollen, Status, Zahlungsarten, Kostenstellentypen
|
||||
und die Befreiungsgründe an. Den Befehl **nur einmal** ausführen, ein zweiter Lauf würde Duplikate anlegen.
|
||||
|
||||
### 6.3 Dach-Tenant `lv` und Cron-Aufgaben anlegen
|
||||
|
||||
Der Slug `lv` ist im Code fest als Dach-Ebene verdrahtet (`LvOnlyMiddleware`, Registrierung, Tenant-Verwaltung).
|
||||
endeavour braucht deshalb genau einen Tenant mit diesem Slug. Seine `url` ist die Domain, die in Abschnitt 7 in KeyHelp
|
||||
angelegt wird.
|
||||
|
||||
```bash
|
||||
/opt/mareike/bin/deploy.sh artisan tinker
|
||||
```
|
||||
|
||||
```php
|
||||
App\Models\Tenant::create([
|
||||
'slug' => 'lv',
|
||||
'name' => '<Name des Verbands>',
|
||||
'url' => '<lv-subdomain>.meine-anmeldung.digital',
|
||||
'email' => '<kontakt@…>',
|
||||
'email_finance' => '<finanzen@…>',
|
||||
'account_name' => '<Kontoinhaber>',
|
||||
'account_iban' => '<IBAN>',
|
||||
'account_bic' => '<BIC>',
|
||||
'city' => '<Ort>',
|
||||
'postcode' => '<PLZ>',
|
||||
'is_active_local_group' => true,
|
||||
'has_active_instance' => true,
|
||||
]);
|
||||
|
||||
use App\Enumerations\CronTaskType;
|
||||
App\Models\CronTask::create(['name' => 'UploadInvoices', 'execution_type' => CronTaskType::CRON_TASK_TYPE_REALTIME]);
|
||||
App\Models\CronTask::create(['name' => 'CloseCostUnit', 'execution_type' => CronTaskType::CRON_TASK_TYPE_DAILY, 'schedule_time' => '00:05']);
|
||||
App\Models\CronTask::create(['name' => 'CloseEvent', 'execution_type' => CronTaskType::CRON_TASK_TYPE_DAILY, 'schedule_time' => '00:10']);
|
||||
App\Models\CronTask::create(['name' => 'NotifyTeam', 'execution_type' => CronTaskType::CRON_TASK_TYPE_DAILY, 'schedule_time' => '20:00']);
|
||||
```
|
||||
|
||||
Die Cron-Aufgaben entsprechen denen des bestehenden Servers (`BdpLvSachsenDataSeeder::installCronTasks()`). Sie gelten
|
||||
global und werden für jeden aktiven Tenant ausgeführt.
|
||||
|
||||
### 6.4 Erste Administratorin / ersten Administrator
|
||||
|
||||
1. Auf `https://<lv-subdomain>.meine-anmeldung.digital` ganz normal registrieren. Voraussetzung: Abschnitt 7 ist
|
||||
erledigt und der Mailversand funktioniert.
|
||||
2. Das Konto zur Administration hochstufen:
|
||||
|
||||
```bash
|
||||
mysql <datenbankname> -e "UPDATE users SET user_role_main='ROLE_ADMINISTRATOR', user_role_local_group='ROLE_GROUP_LEADER', active=1 WHERE email='<adresse>';"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Tenant-Domain in KeyHelp einrichten (je Tenant)
|
||||
|
||||
Diesen Ablauf für den Dach-Tenant `lv` und später für jede weitere Gruppe wiederholen.
|
||||
|
||||
### 7.1 Einmalig: Proxy-Module aktivieren (Apache)
|
||||
|
||||
```bash
|
||||
a2enmod proxy proxy_http headers
|
||||
systemctl reload apache2
|
||||
```
|
||||
|
||||
### 7.2 Domain anlegen
|
||||
|
||||
1. KeyHelp → **Domains** → neue Domain für das mareike-Konto, z. B. `wilde-moehre.meine-anmeldung.digital`.
|
||||
Den DNS-Eintrag (A/AAAA) auf endeavour setzen, falls KeyHelp den DNS nicht selbst verwaltet.
|
||||
2. **SSL/TLS:** Let's Encrypt aktivieren und **HTTPS erzwingen** einschalten.
|
||||
3. PHP wird für diese Domain nicht gebraucht.
|
||||
4. In den Domain-Einstellungen im Feld für **individuelle Webserver-Direktiven** (nur als Admin sichtbar, die
|
||||
Bezeichnung variiert je nach KeyHelp-Version) Folgendes eintragen:
|
||||
|
||||
**Apache** (KeyHelp-Standard):
|
||||
|
||||
```apache
|
||||
ProxyPreserveHost On
|
||||
ProxyRequests Off
|
||||
RequestHeader set X-Forwarded-Proto "https"
|
||||
ProxyPass /.well-known/acme-challenge/ !
|
||||
ProxyPass / http://127.0.0.1:8080/
|
||||
ProxyPassReverse / http://127.0.0.1:8080/
|
||||
```
|
||||
|
||||
**nginx** (falls KeyHelp auf nginx läuft):
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
client_max_body_size 70m;
|
||||
}
|
||||
```
|
||||
|
||||
Die Ausnahme für `/.well-known/acme-challenge/` sorgt dafür, dass KeyHelp die Zertifikate weiter selbst verlängern
|
||||
kann.
|
||||
5. Speichern und die Domain aufrufen. Solange der Tenant noch nicht existiert, zeigt mareike **404 „Tenant not
|
||||
found“**. Das ist ein gutes Zeichen, denn der Proxy funktioniert.
|
||||
|
||||
### 7.3 Tenant in mareike anlegen
|
||||
|
||||
Im Dach-Tenant `lv` unter der Tenant-Verwaltung den neuen Tenant anlegen. Als URL **exakt** die Domain ohne `https://`
|
||||
und ohne Schrägstrich eintragen, z. B. `wilde-moehre.meine-anmeldung.digital`.
|
||||
|
||||
Für den Tenant `lv` selbst ist das bereits in Schritt 6.3 passiert.
|
||||
|
||||
---
|
||||
|
||||
## 8. Cron einrichten
|
||||
|
||||
mareike hat keinen Laravel-Scheduler. Die Aufgaben laufen über die URL `/execute-crons`, die für **alle** aktiven
|
||||
Tenants arbeitet. Sie muss einmal pro Minute aufgerufen werden, dabei genügt der Host-Header eines beliebigen aktiven
|
||||
Tenants:
|
||||
|
||||
```bash
|
||||
cat > /etc/cron.d/mareike <<'EOF'
|
||||
* * * * * root curl -fsS -o /dev/null -H "Host: <lv-subdomain>.meine-anmeldung.digital" http://127.0.0.1:8080/execute-crons
|
||||
EOF
|
||||
```
|
||||
|
||||
Kontrolle: `/opt/mareike/storage/logs/` enthält die Task-Logs.
|
||||
|
||||
---
|
||||
|
||||
## 9. Updates ausrollen
|
||||
|
||||
```bash
|
||||
/opt/mareike/bin/deploy.sh 4.9.2 # Release-Tag
|
||||
/opt/mareike/bin/deploy.sh main # oder Branch
|
||||
```
|
||||
|
||||
Das Skript läuft in dieser Reihenfolge:
|
||||
|
||||
1. `git fetch` und Checkout des Standes.
|
||||
2. `storage/`-Ordner prüfen.
|
||||
3. Images neu bauen, dabei werden Basis-Images mit Sicherheitsupdates frisch gezogen.
|
||||
4. `migrate --force` ausführen.
|
||||
5. Container neu starten.
|
||||
6. Kompilierte Views leeren.
|
||||
7. Alte Images aufräumen.
|
||||
|
||||
Nützliche Kurzbefehle:
|
||||
|
||||
```bash
|
||||
/opt/mareike/bin/deploy.sh compose ps
|
||||
/opt/mareike/bin/deploy.sh compose logs -f web
|
||||
/opt/mareike/bin/deploy.sh artisan migrate:status
|
||||
/opt/mareike/bin/deploy.sh artisan tinker
|
||||
```
|
||||
|
||||
**Rollback:** Den vorherigen Tag ausrollen (`deploy.sh 4.9.1`). Migrationen werden dabei **nicht** zurückgedreht.
|
||||
Enthält ein Release Datenbankänderungen, deshalb vorher ein DB-Backup ziehen.
|
||||
|
||||
---
|
||||
|
||||
## 10. Backup
|
||||
|
||||
| Was | Wie |
|
||||
|---|---|
|
||||
| Datenbank | Über das KeyHelp-Backup, weil die DB von KeyHelp verwaltet wird. |
|
||||
| `/opt/mareike/storage` | **Nicht** im KeyHelp-Backup. Zusätzlich sichern, z. B. per borg/restic oder rsync auf das Backup-Ziel. |
|
||||
| `/opt/mareike/shared/.env` | Ebenfalls zusätzlich sichern, besonders wegen des `APP_KEY`. |
|
||||
| Code und Images | Nicht nötig, lassen sich jederzeit aus dem Repo neu bauen. |
|
||||
|
||||
---
|
||||
|
||||
## 11. Fehlersuche
|
||||
|
||||
| Symptom | Ursache / Lösung |
|
||||
|---|---|
|
||||
| 404 „Tenant not found“ | Der Host passt nicht zu `tenants.url` (Tippfehler, `https://` oder `/` im Eintrag), der Tenant ist nicht `has_active_instance`, oder der Proxy reicht den Host nicht durch (`ProxyPreserveHost On` fehlt). |
|
||||
| 502/503 vom KeyHelp-Webserver | Container laufen nicht (`deploy.sh compose ps`) oder die Firewall wurde neu geladen, dann hilft `systemctl restart docker`. |
|
||||
| Seite lädt ohne CSS, Mixed Content | `ASSET_URL` ist gesetzt oder die nginx-Config im Container wurde nicht übernommen (`fastcgi_param HTTPS on`). |
|
||||
| `SQLSTATE[HY000] [2002]` | Socket nicht erreichbar. `SELECT @@socket` prüfen, `MAREIKE_DB_SOCKET_DIR` bzw. `DB_SOCKET` anpassen, nach einem MariaDB-Neustart den Stack mit `deploy.sh compose restart app` neu starten. |
|
||||
| `Permission denied` in storage/ | Besitzer von `/opt/mareike/storage` muss `mareike-docker` sein, der nächste `deploy.sh`-Lauf korrigiert das. |
|
||||
| Upload bricht ab | Grenzen prüfen: `MAX_INVOICE_FILE_SIZE` in der `.env`, 64 MB in `php.ini`, 70 MB in `nginx.conf`. |
|
||||
| Mails kommen nicht an | `MAIL_*` prüfen, Log in `storage/logs/`. Absenderadresse muss zum Postfach passen (SPF/DKIM der Domain in KeyHelp). |
|
||||
|
||||
Logs:
|
||||
|
||||
```bash
|
||||
/opt/mareike/bin/deploy.sh compose logs --tail=200 app web
|
||||
ls -lt /opt/mareike/storage/logs/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Abgrenzung zum bestehenden Server
|
||||
|
||||
| | bestehender Server | endeavour |
|
||||
|---|---|---|
|
||||
| Dockerfile | `docker/prod.Dockerfile` | `docker/endeavour/Dockerfile` |
|
||||
| Compose | `docker-compose.prod` | `docker/endeavour/compose.yaml` |
|
||||
| nginx | `docker/nginx/default.conf` | `docker/endeavour/nginx.conf` |
|
||||
| Build-Kontext | `.dockerignore` | `docker/endeavour/Dockerfile.dockerignore` |
|
||||
| Datenbank | eigene | KeyHelp-MariaDB auf endeavour |
|
||||
| Tenants | `*.mareike.sachsen.pfadfinden.de` | `*.meine-anmeldung.digital` |
|
||||
|
||||
Beide Server nutzen denselben Code-Stand aus demselben Repository, haben aber **getrennte** Datenbanken, `.env`,
|
||||
WebDAV-Ordner und Tenants. Änderungen an `docker/endeavour/` wirken nur auf endeavour.
|
||||
Reference in New Issue
Block a user