Jedes Projekt ist eine Gruppe im GitLab der Plattform unter gitlab.application-platform.com mit einem Repository pro Komponente, local-configuration für die gemeinsame Agenten-Konfiguration, gitops-configuration für das Deployment und gitlab-profile mit der README.
orgs/<organisation>/<projekt>/
├── backend/ # z. B. NestJS-API mit Dockerfile
├── <app>/ # z. B. Flutter-App, auch mehrere
├── <homepage>/ # z. B. Hugo-Site
├── gitops-configuration/ # Deployment-Konfiguration für Dev und Prod
├── local-configuration/ # AGENTS.md, CLAUDE.md, .cursor, .claude, .codex, .vscode
└── gitlab-profile/ # README der Gruppe
Im geklonten Projektordner zeigen Symlinks auf local-configuration, deshalb sehen KI-Agenten und Editoren für alle Repos dieselben Regeln.
Vom Push zum laufenden Container
flowchart LR push["Push auf main"] -->|"Pipeline"| image["Docker-Image<br/>mit Versions-Tag"] image -->|"Tag eintragen"| gitops["gitops-configuration<br/>versions.yaml"] gitops -->|"Ansible"| dev["Dev-Server"] gitops -->|"Ansible, manueller Job"| prod["Prod-Server"]
Die Pipeline berechnet aus den Commit-Nachrichten die nächste Version (feat: und fix: zählen hoch), baut das Docker-Image, lädt es in die Registry und trägt den Tag in die Deployment-Konfiguration für Dev ein; das Deployment-Repository rollt ihn per Ansible auf den Dev-Server aus. Produktion bekommt denselben Tag erst über den manuellen Produktions-Job, den Git-Workflow und Deployment beschreibt.
Das GitOps-Repository
gitops-configuration/
└── configurations/
├── dev/
│ ├── generated.yaml # Plattform: Hostnamen, Registry, Secrets (SOPS)
│ ├── custom.yaml # du: eigene Umgebungsvariablen
│ └── versions.yaml # Pipeline: deployte Image-Versionen
└── prod/ # gleiche Struktur
generated.yaml schreibt die Plattform bei jeder Projektänderung mit Hostnamen, Registry-Zugang und SOPS-verschlüsselten Secrets wie Datenbank-Passwörtern und JWT-Secrets. custom.yaml gehört dir und nimmt eigene Umgebungsvariablen unter additional_env_variables auf. versions.yaml schreibt die Pipeline mit dem deployten Image-Tag. Jeder Commit auf main, ob von der Pipeline oder von dir, löst das Ansible-Deployment der betroffenen Umgebung aus. Das Zusammenspiel mit den env/-Dateien steht unter Umgebungsvariablen.
Was auf dem Server läuft
Beim ersten Deployment richtet die Plattform den Server per Ansible ein: Docker, Traefik als Reverse Proxy, eine Firewall, HTTPS-Zertifikate und einen Deploy-Schlüssel in den authorized_keys des angegebenen Benutzers. Traefik verteilt Anfragen anhand der Domain an den richtigen Container, auch an Docker Apps. Für Let’s Encrypt muss der Server direkt erreichbar sein; bei Cloudflare setzt die Plattform die DNS-Einträge deshalb ohne Proxy.
Die Daten deines Backends liegen außerhalb der Container auf dem Server: ./backend-mysql für die Datenbank, ./backend-uploads für Uploads, die im Container unter /app/upload erscheinen, und ./backend-backups für die Archive von Easy Backup. Ein Release tauscht nur die Container aus.
Reine Homepage-Projekte auf einem Webspace lädt die Pipeline per FTP oder SFTP hoch, ohne Docker, GitOps und Ansible. Ein Kubernetes-Zugang unter Anbindungen wird bislang nur gespeichert; alle Anwendungen laufen per Docker auf dem Server.
Generierte Konfiguration und Sentry
Die Plattform erzeugt .gitlab-ci.yml, env/*.generated.env und generated.yaml und überschreibt sie bei jeder Projektänderung; eigene Werte gehören in env/*.custom.env und custom.yaml. Aktivierst du Sentry für eine Komponente unter Funktionen, legt die Plattform das Sentry-Projekt in deinem unter Anbindungen verbundenen Konto an und schreibt die DSN in diese Konfiguration, für Apps zum Beispiel in env/shared.generated.env. Den Umgang damit beschreibt Betrieb.