---
name: deployment
description: Production deployment setup for tibi-projects – Basispanel subdomain, .env, CI-Pipeline, Makefile. Use when deploying a new project to production or setting up a staging environment.
---

# Deployment

## Überblick

Ein tibi-Projekt wird per Gitea Actions CI gebaut und via rsync auf den Produktionsserver (dock4) deployed. Davor muss die Subdomain im Basispanel angelegt und der Kunde korrekt konfiguriert sein.

## 1. Basispanel – Subdomain anlegen

### Kunde prüfen

```bash
# Domain des Kunden suchen
mcp_call(server="basispanel", tool="bp_list_domains", args={"search": "<kunde>"})
# → liefert Customer-ID, Domain-ID, Company, Username
```

### Subdomain anlegen

```bash
# 1. Config holen (verfügbare Webserver + Storages sehen)
mcp_call(server="basispanel", tool="bp_get_config")

# 2. Subdomain erstellen (ohne Webserver)
mcp_call(server="basispanel", tool="bp_create_subdomain", args={
    "domainId": <domain-id>,
    "name": "<subdomain>",  # oder leer für bare domain
})

# 3. Löschen + neu mit Webserver (wenn Update nicht klappt)
mcp_call(server="basispanel", tool="bp_delete_subdomain", args={"id": <subdomain-id>})

mcp_call(server="basispanel", tool="bp_create_subdomain", args={
    "domainId": <domain-id>,
    "name": "<subdomain>",
    "webserverKey": "dock4_lamp2",
    "webserverStorage": "dock4_webroots2",
    "webserverSettings": {
        "redirectType": "docroot",
        "docroot": "/<subdomain>.<domain>/frontend",
        "gitbaseRepository": "<org>/<repo>",
        "deployRoot": "./..",
        "defaultAlias": "wwwAlias",
        "defaultSubdomain": "defaultSubdomain",
        "wwwRedirect": "wwwRedirect",
        "php": "phpDisabled",      # tibi-SPA kein PHP
        "https": "noHttps",        # erstmal aus, später aktivieren
        "certbot": "noCertbot",
    },
})
```

Wichtige Keys (aus `bp_get_config`):
| Server | Key | Storage |
|--------|-----|---------|
| dock4 | `dock4_lamp2` | `dock4_webroots2` |
| dock1 | `dock1_...` | `dock1_webroots...` |

### Status prüfen

```bash
mcp_call(server="basispanel", tool="bp_get_subdomain_status", args={"id": <subdomain-id>})
```

Achtung: Health-Check zeigt DNS-Warnungen (externe Nameserver) – das ist normal solange der Kunde sein DNS selbst verwaltet.

## 2. `.env` konfigurieren

```env
# Basis
PROJECT_NAME=<project>
TIBI_PREFIX=tibi
TIBI_NAMESPACE=<project>

# RSYNC für Deploy
RSYNC_HOST=ftp1.webmakers.de
RSYNC_PORT=22223
PRODUCTION_RSYNC_UID=100<customer-id>00    # z.B. 10051300
PRODUCTION_RSYNC_GID=33

# Production Server
PRODUCTION_SERVER=dock4.basehosts.de
PRODUCTION_TIBI_PREFIX=tibi
PRODUCTION_PATH=/webroots2/customers/<customer-id>/htdocs

# Staging
STAGING_PATH=/staging/<org>/<project>/dev

# URLs
LIVE_URL=http://<subdomain>.<domain>.dock4.basispanel.de   # Preview-URL
STAGING_URL=https://dev-<project>.staging.testversion.online
CODING_URL=https://<project>.code.testversion.online
```

## 3. CI-Pipeline (`.gitea/workflows/deploy.yml`)

```yaml
name: deploy to production
on: "push"
jobs:
    deploy:
        steps:
            - checkout + git fetch --tags
            - node 22 + yarn install
            - yarn validate
            - ./scripts/ci-modify-config.sh # injiziert LIVE_URL, release, preview
            - yarn build # frontend
            - yarn build:server # SSR
            - sourcemaps → sentry
            - if dev-branch: ./scripts/ci-staging.sh
            - if master-branch: ./scripts/ci-deploy.sh
```

**Wichtig:** Das aktuelle Workflow-File führt `yarn validate`, `yarn build` und `yarn build:server` im CI aus. Wenn `validate` dort scheitert, behebe den eigentlichen Typ- oder Pfadfehler statt den Schritt stillschweigend zu entfernen.

## 4. Deploy-Skripte

### `scripts/ci-deploy.sh` (Production)

- Liest `.env` und `api/config.yml.env`
- rsynct `frontend/`, `api/`, `media/` via SSH zu `RSYNC_HOST`
- deshalb muessen Collection-Dateiuploads auf den Repo-Root `media/` zeigen, typischerweise via `uploadPath: ../media/<collection>` in `api/collections/*.yml`
- `api/media` ist in diesem Setup nicht der persistente Deploy-Zielpfad fuer Uploads
- Nutzt `RSYNC_USER` + `RSYNC_PASS` (aus Gitea Secrets)
- Auf master: excludiert `src/` und `*.map`
- Reloadt den projektlokalen Proxy-Endpunkt via `LIVE_URL/api/_/admin/reload` mit `Authorization: Bearer ${ADMIN_TOKEN}`
- Cleared SSR cache via `LIVE_URL/api/ssr?clear=1`

### `scripts/ci-staging.sh` (Dev/Staging)

- rsynct `api/`, `frontend/dist`, und `frontend/assets` nach `/data/${{ github.repository }}/${{ github.ref_name }}`
- Startet `docker-compose-staging.yml`
- Reloadt den projektlokalen Proxy-Endpunkt via `STAGING_URL/api/_/admin/reload` mit `Authorization: Bearer ${ADMIN_TOKEN}`

### `scripts/ci-modify-config.sh`

- Injiziert `LIVE_URL` als `originURL` in `api/hooks/config-client.js`
- Injiziert `LIVE_URL` als `PREVIEW_URL` in `api/config.yml.env`
- Setzt `release` + `buildTime` für Sentry
- Kopiert `frontend/spa.html` → `api/templates/spa.html` (SSR-Template)
- Ersetzt `__TIMESTAMP__` in spa.html (Cache-Busting)

## 5. Makefile

Wichtige Targets:

```makefile
# Media von Production syncen
media-sync-master-to-local:
    rsync -v -e "ssh ... -l $(PRODUCTION_RSYNC_UID),$(PRODUCTION_RSYNC_GID),$(PRODUCTION_PATH)/media" \
        -az $(RSYNC_HOST):/ media/

# MongoDB von Production syncen (via Chisel-Tunnel)
mongo-sync-master-to-local:
    chisel client --auth coder:$$PASSWORD http://$(PRODUCTION_SERVER):10987 27017:mongo:27017 &
    mongodump ... | mongorestore ...
```

## 6. DNS

Der Kunde verwaltet sein DNS selbst (externe Nameserver). Für die Subdomain muss ein A-Record gesetzt werden:

```
<subdomain>.<domain>  IN  A  45.129.180.102   (IP von dock4)
```

Die Preview-URL `http://<subdomain>.<domain>.dock4.basispanel.de` funktioniert ohne DNS (wird von Basispanel intern aufgelöst).

## 7. HTTPS nachträglich aktivieren

Sobald das Projekt live geht:

1. Im Basispanel Subdomain updaten:
    - `https`: `"https"` (statt `"noHttps"`)
    - `certbot`: `"certbot"` (automatisches Letsencrypt)
    - `httpsRedirect`: `"httpsRedirect"` (HTTP→HTTPS)
2. `.env`: `LIVE_URL` auf `https://www.<domain>` ändern
3. `api/hooks/config-client.js`: `originURL` entsprechend setzen (wird von CI überschrieben)

## 8. Typische Fehler

| Problem                         | Ursache                                        | Fix                                                                                  |
| ------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------ |
| `invalid webserverKey: dock4`   | Falscher Key                                   | Mit `bp_get_config` prüfen → `dock4_lamp2`                                           |
| `subdomain exists`              | Doppelt angelegt                               | Mit `bp_delete_subdomain` löschen, neu anlegen                                       |
| `yarn validate` scheitert in CI | Typen/Submodule/Pfade nicht sauber eingecheckt | Checkout-, Submodule- und Include-Pfade korrigieren; `validate` im Workflow belassen |
| Rsync "Permission denied"       | Falscher RSYNC_USER                            | In Gitea Secrets prüfen                                                              |
| 404 auf Subdomain               | DNS nicht gesetzt                              | A-Record beim Kunden-DNS-Provider eintragen                                          |
