Kassenbücher werden nun zuerst tabellarisch angezeigt. Das Erstellen einer Barkasse erfolgt über ein Modal und einzelne Kassenbücher öffnen sich über eine Detailseite.
FEDEO Hosting Guide
Diese Anleitung beschreibt ein produktionsnahes Self-Hosting von FEDEO mit Docker Compose, Traefik, PostgreSQL und optionalem S3-kompatiblem Objektspeicher via MinIO.
Architektur
Der Stack besteht aus:
frontend: Nuxt-Frontend auf Port3000backend: Node/Fastify-API auf Port3100db: PostgreSQLtraefik: Reverse Proxy mit automatischen Let's-Encrypt-Zertifikaten- optional
minio: S3-kompatibler Objektspeicher fur Dateiuploads
Die Konfiguration erfolgt uber Umgebungsvariablen beziehungsweise eine .env-Datei im Deploy-Verzeichnis.
Voraussetzungen
Vor dem Deployment sollten folgende Punkte erfullt sein:
- Ein Linux-Server oder VPS mit offentlichen Ports
80und443 - Docker Engine inkl. Compose Plugin
- Eine Domain, die auf den Server zeigt, z. B.
app.example.com - Optional: SMTP-Zugang fur E-Mails
- Optional: S3-Bucket oder MinIO fur Dateispeicher
Empfohlen:
- mindestens 2 vCPU
- mindestens 4 GB RAM
- SSD-Speicher fur PostgreSQL und Dateiuploads
DNS und Netzwerk
Lege mindestens einen A- oder AAAA-Record an:
app.example.com -> <SERVER-IP>
Traefik terminiert TLS direkt im Compose-Stack. Es ist kein zusatzlicher Reverse Proxy davor erforderlich.
Benotigte Backend-Umgebungsvariablen
Das Backend erwartet mindestens diese Umgebungsvariablen:
COOKIE_SECRETJWT_SECRETPORTHOSTDATABASE_URLS3_BUCKETENCRYPTION_KEYMAILER_SMTP_HOSTMAILER_SMTP_PORTMAILER_SMTP_SSLMAILER_SMTP_USERMAILER_SMTP_PASSMAILER_FROMS3_ENDPOINTS3_REGIONS3_ACCESS_KEYS3_SECRET_KEYM2M_API_KEYAPI_BASE_URLGOCARDLESS_BASE_URLGOCARDLESS_SECRET_IDGOCARDLESS_SECRET_KEYDOKUBOX_IMAP_HOSTDOKUBOX_IMAP_PORTDOKUBOX_IMAP_SECUREDOKUBOX_IMAP_USERDOKUBOX_IMAP_PASSWORDOPENAI_API_KEYSTIRLING_API_KEY
Minimal wichtige Werte fur den ersten Start:
HOST=0.0.0.0PORT=3100DATABASE_URL=postgres://fedeo:<starkes-passwort>@db:5432/fedeoAPI_BASE_URL=https://app.example.com/backend
Wenn du MinIO verwendest, setze zusatzlich:
S3_ENDPOINT=http://minio:9000S3_REGION=eu-central-1S3_ACCESS_KEY=<MINIO_ROOT_USER>S3_SECRET_KEY=<MINIO_ROOT_PASSWORD>S3_BUCKET=fedeo
Deploy-Struktur
Deploye den Stack direkt aus einem geklonten Checkout dieses Repositories, weil die Compose-Datei die lokalen Build-Kontexte ./frontend und ./backend verwendet.
Beispiel:
git clone <DEIN-REPO-URL> /opt/fedeo
cd /opt/fedeo
Die Verzeichnisstruktur sollte dann mindestens so aussehen:
/opt/fedeo/
docker-compose.yml
.env
backend/
frontend/
traefik/
letsencrypt/
logs/
postgres/
minio/
Danach:
mkdir -p /opt/fedeo/traefik/letsencrypt
mkdir -p /opt/fedeo/traefik/logs
mkdir -p /opt/fedeo/postgres
mkdir -p /opt/fedeo/minio
touch /opt/fedeo/traefik/letsencrypt/acme.json
chmod 600 /opt/fedeo/traefik/letsencrypt/acme.json
Beispiel .env
Diese Datei liegt neben der docker-compose.yml:
DOMAIN=app.example.com
CONTACT_EMAIL=admin@example.com
DB_NAME=fedeo
DB_USER=fedeo
DB_PASSWORD=change-this-db-password
DATABASE_URL=postgres://fedeo:change-this-db-password@db:5432/fedeo
MINIO_ROOT_USER=fedeo-minio
MINIO_ROOT_PASSWORD=change-this-minio-password
MINIO_BUCKET=fedeo
HOST=0.0.0.0
PORT=3100
COOKIE_SECRET=change-this-cookie-secret
JWT_SECRET=change-this-jwt-secret
ENCRYPTION_KEY=change-this-encryption-key
MAILER_SMTP_HOST=smtp.example.com
MAILER_SMTP_PORT=587
MAILER_SMTP_SSL=false
MAILER_SMTP_USER=mailer@example.com
MAILER_SMTP_PASS=change-this-mail-password
MAILER_FROM=FEDEO <no-reply@example.com>
S3_ENDPOINT=http://minio:9000
S3_REGION=eu-central-1
S3_ACCESS_KEY=fedeo-minio
S3_SECRET_KEY=change-this-minio-password
S3_BUCKET=fedeo
M2M_API_KEY=change-this-m2m-key
API_BASE_URL=https://app.example.com/backend
GOCARDLESS_BASE_URL=https://api.gocardless.com
GOCARDLESS_SECRET_ID=replace-this
GOCARDLESS_SECRET_KEY=replace-this
DOKUBOX_IMAP_HOST=imap.example.com
DOKUBOX_IMAP_PORT=993
DOKUBOX_IMAP_SECURE=true
DOKUBOX_IMAP_USER=dokubox@example.com
DOKUBOX_IMAP_PASSWORD=change-this-imap-password
OPENAI_API_KEY=replace-this
STIRLING_API_KEY=replace-this
NUXT_PUBLIC_PDF_LICENSE=replace-with-your-pdf-license
Vollstandiges Docker Compose mit optionaler S3-MinIO-Option
Hinweis: Der Stack unten startet MinIO standardmassig mit. Wenn du stattdessen AWS S3, Hetzner Object Storage, Backblaze B2 S3 oder einen anderen externen S3-Dienst nutzen willst, kannst du die Services minio und createbuckets entfernen und nur die entsprechenden S3-Umgebungsvariablen auf den externen Anbieter zeigen lassen.
services:
traefik:
image: traefik:v2.11
container_name: fedeo-traefik
restart: unless-stopped
command:
- --api.insecure=false
- --api.dashboard=false
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --certificatesresolvers.letsencrypt.acme.tlschallenge=true
- --certificatesresolvers.letsencrypt.acme.email=${CONTACT_EMAIL}
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
- --accesslog=true
- --accesslog.filepath=/logs/access.log
ports:
- "80:80"
- "443:443"
volumes:
- ./traefik/letsencrypt:/letsencrypt
- ./traefik/logs:/logs
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- web
db:
image: postgres:16
container_name: fedeo-db
restart: unless-stopped
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- ./postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
interval: 10s
timeout: 5s
retries: 10
networks:
- internal
minio:
image: minio/minio:latest
container_name: fedeo-minio
restart: unless-stopped
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
volumes:
- ./minio:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 10s
timeout: 5s
retries: 10
networks:
- internal
createbuckets:
image: minio/mc:latest
container_name: fedeo-minio-init
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
mc alias set local http://minio:9000 ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD};
mc mb --ignore-existing local/${MINIO_BUCKET};
mc anonymous set private local/${MINIO_BUCKET};
exit 0;
"
restart: "no"
networks:
- internal
backend:
build:
context: ./backend
container_name: fedeo-backend
restart: unless-stopped
depends_on:
db:
condition: service_healthy
minio:
condition: service_healthy
createbuckets:
condition: service_completed_successfully
environment:
NODE_ENV: production
HOST: ${HOST}
PORT: ${PORT}
COOKIE_SECRET: ${COOKIE_SECRET}
JWT_SECRET: ${JWT_SECRET}
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
DATABASE_URL: ${DATABASE_URL}
MAILER_SMTP_HOST: ${MAILER_SMTP_HOST}
MAILER_SMTP_PORT: ${MAILER_SMTP_PORT}
MAILER_SMTP_SSL: ${MAILER_SMTP_SSL}
MAILER_SMTP_USER: ${MAILER_SMTP_USER}
MAILER_SMTP_PASS: ${MAILER_SMTP_PASS}
MAILER_FROM: ${MAILER_FROM}
S3_ENDPOINT: ${S3_ENDPOINT}
S3_REGION: ${S3_REGION}
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
S3_SECRET_KEY: ${S3_SECRET_KEY}
S3_BUCKET: ${S3_BUCKET}
M2M_API_KEY: ${M2M_API_KEY}
API_BASE_URL: ${API_BASE_URL}
GOCARDLESS_BASE_URL: ${GOCARDLESS_BASE_URL}
GOCARDLESS_SECRET_ID: ${GOCARDLESS_SECRET_ID}
GOCARDLESS_SECRET_KEY: ${GOCARDLESS_SECRET_KEY}
DOKUBOX_IMAP_HOST: ${DOKUBOX_IMAP_HOST}
DOKUBOX_IMAP_PORT: ${DOKUBOX_IMAP_PORT}
DOKUBOX_IMAP_SECURE: ${DOKUBOX_IMAP_SECURE}
DOKUBOX_IMAP_USER: ${DOKUBOX_IMAP_USER}
DOKUBOX_IMAP_PASSWORD: ${DOKUBOX_IMAP_PASSWORD}
OPENAI_API_KEY: ${OPENAI_API_KEY}
STIRLING_API_KEY: ${STIRLING_API_KEY}
labels:
- traefik.enable=true
- traefik.http.routers.fedeo-backend.rule=Host(`${DOMAIN}`) && PathPrefix(`/backend`)
- traefik.http.routers.fedeo-backend.entrypoints=websecure
- traefik.http.routers.fedeo-backend.tls.certresolver=letsencrypt
- traefik.http.middlewares.fedeo-backend-strip.stripprefix.prefixes=/backend
- traefik.http.routers.fedeo-backend.middlewares=fedeo-backend-strip
- traefik.http.services.fedeo-backend.loadbalancer.server.port=3100
networks:
- web
- internal
frontend:
build:
context: ./frontend
container_name: fedeo-frontend
restart: unless-stopped
depends_on:
- backend
environment:
NODE_ENV: production
NUXT_PUBLIC_API_BASE: https://${DOMAIN}/backend
NUXT_PUBLIC_PDF_LICENSE: ${NUXT_PUBLIC_PDF_LICENSE}
labels:
- traefik.enable=true
- traefik.http.routers.fedeo-frontend.rule=Host(`${DOMAIN}`)
- traefik.http.routers.fedeo-frontend.entrypoints=websecure
- traefik.http.routers.fedeo-frontend.tls.certresolver=letsencrypt
- traefik.http.services.fedeo-frontend.loadbalancer.server.port=3000
networks:
- web
networks:
web:
driver: bridge
internal:
driver: bridge
Externe S3-Provider statt MinIO
Wenn du keinen lokalen MinIO-Container betreiben willst:
- Entferne die Services
minioundcreatebucketsaus der Compose-Datei. - Entferne im Backend
depends_onfurminioundcreatebuckets. - Trage in
.envdie Zugangsdaten des externen S3-Dienstes ein.
Beispiel fur die relevanten Werte:
S3_ENDPOINT=https://s3.eu-central-1.amazonaws.com
S3_REGION=eu-central-1
S3_ACCESS_KEY=...
S3_SECRET_KEY=...
S3_BUCKET=fedeo
Hinweis: Das Backend nutzt forcePathStyle: true. Das funktioniert sauber mit MinIO und vielen S3-kompatiblen Providern. Bei reinem AWS S3 kann je nach Endpoint-Setup ein abweichendes Verhalten sinnvoll sein. Falls du AWS S3 einsetzen willst, sollte die S3-Initialisierung im Backend gegen den konkreten Zielprovider getestet werden.
Start des Stacks
Im Deploy-Verzeichnis:
docker compose build
docker compose up -d
Danach Status prufen:
docker compose ps
docker compose logs -f traefik
docker compose logs -f backend
Funktionsprufung
Nach dem ersten Start sollten mindestens diese Checks erfolgreich sein:
curl -I https://app.example.com
curl https://app.example.com/backend/health
Erwartung:
- Frontend liefert
200oder302 - Backend liefert JSON wie
{"status":"ok"}
Updates
Bei neuen Versionen:
git pull
docker compose build
docker compose up -d
Falls du statt lokaler Builds vorgebaute Images verwenden willst, kannst du in der Compose-Datei build: durch passende image:-Eintrage ersetzen. Erst dann ist ein vorgelagertes docker compose pull sinnvoll.
Backup-Empfehlung
Regelmassig sichern:
./postgres./miniofalls MinIO lokal genutzt wird./traefik/letsencrypt/acme.json- deine
.env - deine dokumentierten Secret-Werte aus der
.envoder deinem Secret-Management
Bekannte Betriebsbesonderheiten
- Das Backend startet nur sauber, wenn alle Pflichtvariablen gesetzt sind.
- Ohne korrekt gesetzte S3-Secrets funktionieren Dateiuploads und dateibasierte Funktionen nicht.
- Fur die Frontend-PDF-Funktion wird eine gueltige
NUXT_PUBLIC_PDF_LICENSEbenotigt. - PostgreSQL ist im Projekt vorgesehen; andere SQL-Datenbanken sind in dieser Compose-Datei nicht berucksichtigt.
Optional: Nur mit bestehender externer Infrastruktur
Wenn bereits vorhanden:
- externer Reverse Proxy
- externer PostgreSQL-Server
- externer S3-Speicher
- externe Zertifikatsverwaltung
dann konnen traefik, db und minio aus dem Stack entfernt werden. In diesem Fall mussen die zugehorigen Hostnamen und Zugangsdaten in der .env beziehungsweise im Frontend-Environment auf die externe Infrastruktur zeigen.