services: app: container_name: aether-app # Fertiges Abbild aus der eigenen Gitea-Registry. # # Ein Tag mit Datum **und** `latest`: Zum Aufbauen nimmt man `latest`, zum # Zurückrollen den datierten. Wer nur `latest` fährt, hat im Fehlerfall # nichts, worauf er zurück kann – der Name zeigt dann schon auf die # Fassung, die gerade Ärger macht. # # Anmelden vor dem ersten Zug: # docker login gitea.honeyy.duckdns.org # # Aktualisieren: # docker compose pull app && docker compose up -d app image: gitea.honeyy.duckdns.org/honey/aether:latest # Selbst bauen statt ziehen? Dann die Zeile darüber auskommentieren und # diese drei einkommentieren. Der Kontext ist das **Wurzelverzeichnis** des # Projekts, nicht dieser Ordner – das Abbild braucht backend/ und frontend/. # # docker compose build app # # build: # context: .. # dockerfile: deploy/Dockerfile restart: unless-stopped env_file: .env # Als der Nutzer laufen, dem das Datenverzeichnis auf dem Wirt gehört. # Ohne das legt der Container seine Dateien als uid 100 an, und du kämst # per SFTP nur als root daran. user: "${APP_UID:-1000}:${APP_GID:-1000}" # ocrmypdf und ghostscript legen Arbeitsdateien unter $HOME ab. Bei einem # abweichenden Nutzer ist das Heimverzeichnis aus dem Image nicht mehr # beschreibbar – /tmp im Container ist es immer. environment: HOME: /tmp depends_on: postgres: condition: service_healthy volumes: # Bind-Mount statt eines benannten Volumes: Deine Dateien liegen damit in # einem gewöhnlichen Verzeichnis auf dem Wirt, mit echten Ordnern und # echten Namen (users//home/…). Du kommst per SFTP, rsync oder # einfach im Dateimanager daran – auch dann noch, wenn diese Anwendung # nie wieder startet. Bei einem benannten Volume läge alles unter # /var/lib/docker/volumes/ und gehörte root. # # Nur lesen. Wer hier hineinschreibt, hat eine Datei auf der Platte, die # in der Datenbank nicht existiert – die Cloud sieht sie nicht, und der # GC räumt sie irgendwann weg. Zum Hineinlegen ist WebDAV der Weg. - ${DATA_PATH:-./data}:/data networks: - internal ports: # Expose on loopback only — your external reverse proxy connects here. # Host port via APP_PORT in .env (default 8080), container port stays 8080. - "127.0.0.1:${APP_PORT:-8080}:8080" postgres: container_name: aether-db image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_DB: ${DB_NAME} POSTGRES_USER: ${DB_USER} POSTGRES_PASSWORD: ${DB_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data networks: - internal healthcheck: test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"] interval: 5s timeout: 5s retries: 10 # ── Office (optional) ────────────────────────────────────────────────────── # Laeuft nur mit `--profile office`. Ohne das Profil startet der Container # nicht – wer kein Office will, soll nicht 1 GB Arbeitsspeicher dafuer # abgeben, nur weil die Zeilen hier stehen. # # docker compose --profile office up -d # # Der Office-Server holt die Datei **selbst** beim Backend ab. Er muss Aether # also unter PUBLIC_URL erreichen, nicht unter localhost. Im selben # Compose-Netz erreicht er es unter `app:8080` – dafuer steht `extra_hosts` # bzw. ein PUBLIC_URL, das auf den Wirt zeigt. collabora: container_name: aether-collabora image: collabora/code:latest profiles: ["office"] restart: unless-stopped environment: # Welche Hosts ihre Dateien hier oeffnen duerfen. Punkte maskiert, weil # Collabora den Wert als regulaeren Ausdruck liest. aliasgroup1: ${OFFICE_ALIASGROUP:-http://host.docker.internal:8090} # Zwei getrennte Schalter, und beide zaehlen: # # ssl.enable – spricht Collabora selbst HTTPS? # ssl.termination – endet HTTPS davor, an einem Reverse Proxy? # # Steht termination auf true, sagt Collabora dem Browser „verbinde dich # per wss://" – auch dann, wenn davor gar kein HTTPS liegt. Im reinen # HTTP-Testaufbau scheitert damit die WebSocket-Verbindung, und der # Editor bleibt leer stehen, obwohl alles andere laedt (gemessen). # # Produktiv hinter deinem Reverse Proxy: OFFICE_SSL_TERMINATION=true. # # mount_jail_tree=false: Collabora sperrt jedes Dokument sonst in einen # eigenen Einhaengepunkt und braucht dafuer CAP_SYS_ADMIN. Die Rechte # dem Container zu geben waere der teurere Weg – ohne den Schalter # scheitert der Start eines Dokuments mit „Failed to exec coolmount". extra_params: >- --o:ssl.enable=${OFFICE_SSL:-false} --o:ssl.termination=${OFFICE_SSL_TERMINATION:-false} --o:mount_jail_tree=false dictionaries: de_DE en_US cap_add: - MKNOD extra_hosts: # Damit der Container den Wirt erreicht – dort haengt der Reverse Proxy # bzw. der veroeffentlichte Port von Aether. - "host.docker.internal:host-gateway" networks: - internal ports: - "127.0.0.1:${OFFICE_PORT:-9980}:9980" volumes: postgres_data: # "data" ist bewusst weg – die Dateien liegen jetzt unter DATA_PATH auf dem # Wirt (siehe oben). Die Datenbank bleibt ein benanntes Volume: An ihre # Dateien geht man nicht von Hand, und Postgres mag fremde Besitzer nicht. networks: internal: driver: bridge