🎉 Initial Commit

This commit is contained in:
Markus Benjamin Tabler 2026-06-27 20:35:49 +02:00
commit 8f23ba583d
20 changed files with 1153 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
.env
.env.local
*.env

30
dyndns/docker-compose.yml Normal file
View file

@ -0,0 +1,30 @@
name: dyndns
services:
ddns-updater:
container_name: ddns-updater
environment:
- PERIOD=${DDNS_PERIOD}
- UPDATE_COOLDOWN=${DDNS_COOLDOWN}
- TZ=${TZ}
image: qmcgaw/ddns-updater:latest
restart: ${RESTART_POLICY}
volumes:
- type: bind
source: ${DDNS_DATA_PATH}
target: /updater/data
bind:
create_host_path: true
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
ports:
- "8000:8000"
network_mode: host
hostname: ddns-updater
cpu_shares: 90
deploy:
resources:
limits:
memory: 15841M

View file

@ -0,0 +1,38 @@
name: ${CONTAINER_NAME}
services:
excalidraw:
cpu_shares: 90
command: []
container_name: ${CONTAINER_NAME}
deploy:
resources:
limits:
memory: 15841M
hostname: ${CONTAINER_NAME}
image: excalidraw/excalidraw:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.${ROUTER_NAME}.entrypoints=websecure"
- "traefik.http.routers.${ROUTER_NAME}.rule=Host(`${SUBDOMAIN}.${DOMAIN}`)"
- "traefik.http.routers.${ROUTER_NAME}.tls=true"
- "traefik.http.routers.${ROUTER_NAME}.tls.certresolver=${CERTIFICATE_RESOLVER}"
- "traefik.http.services.${ROUTER_NAME}.loadbalancer.server.port=${CONTAINER_PORT}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
restart: ${RESTART_POLICY}
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
ports: []
volumes: []
devices: []
cap_add: []
environment: []
networks:
- traefik
privileged: false
networks:
traefik:
name: traefik
external: true

View file

@ -0,0 +1,45 @@
name: ${CONTAINER_NAME}
networks:
forgejo:
external: false
traefik:
external: true
services:
server:
image: codeberg.org/forgejo/forgejo:9.0
container_name: ${CONTAINER_NAME}
restart: ${RESTART_POLICY}
environment:
- USER_UID=1000
- USER_GID=1000
networks:
- forgejo
- traefik
volumes:
- type: bind
source: ${DATA_PATH}
target: /data
bind:
create_host_path: true
- type: bind
source: /etc/localtime
target: /etc/localtime
read_only: true
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
ports:
- '222:22'
labels:
- "traefik.enable=true"
- "traefik.http.routers.${ROUTER_NAME}.entrypoints=websecure"
- "traefik.http.routers.${ROUTER_NAME}.rule=Host(`${SUBDOMAIN}.${DOMAIN}`)"
- "traefik.http.routers.${ROUTER_NAME}.tls=true"
- "traefik.http.routers.${ROUTER_NAME}.tls.certresolver=${CERTIFICATE_RESOLVER}"
- "traefik.http.services.${ROUTER_NAME}.loadbalancer.server.port=${CONTAINER_PORT}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"

View file

@ -0,0 +1,36 @@
name: gantt
services:
gantt-app:
image: nginx:stable-alpine
container_name: ${CONTAINER_NAME}
volumes:
- type: bind
source: ${DIST_PATH}
target: /usr/share/nginx/html:ro
bind:
create_host_path: true
- type: bind
source: ${CONFIG_PATH}
target: /etc/nginx/conf.d/default.conf:ro
bind:
create_host_path: true
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
restart: ${RESTART_POLICY}
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.${ROUTER_NAME}.entrypoints=websecure"
- "traefik.http.routers.${ROUTER_NAME}.rule=Host(`${SUBDOMAIN}.${DOMAIN}`)"
- "traefik.http.routers.${ROUTER_NAME}.tls=true"
- "traefik.http.routers.${ROUTER_NAME}.tls.certresolver=${CERTIFICATE_RESOLVER}"
- "traefik.http.services.${ROUTER_NAME}.loadbalancer.server.port=${CONTAINER_PORT}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
networks:
traefik:
external: true

View file

@ -0,0 +1,41 @@
services:
jellyfin:
image: jellyfin/jellyfin
container_name: ${CONTAINER_NAME}
restart: ${RESTART_POLICY}
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.${ROUTER_NAME}.entrypoints=websecure"
- "traefik.http.routers.${ROUTER_NAME}.rule=Host(`${SUBDOMAIN}.${DOMAIN}`)"
- "traefik.http.routers.${ROUTER_NAME}.tls=true"
- "traefik.http.routers.${ROUTER_NAME}.tls.certresolver=${CERTIFICATE_RESOLVER}"
- "traefik.http.services.${ROUTER_NAME}.loadbalancer.server.port=${CONTAINER_PORT}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
environment:
- JELLYFIN_PublishedServerUrl=https://${SUBDOMAIN}.${DOMAIN}
volumes:
- type: bind
source: ${CACHE_PATH}
target: /cache
bind:
create_host_path: true
- type: bind
source: ${CONFIG_PATH}
target: /config
bind:
create_host_path: true
- type: bind
source: ${MEDIA_PATH}
target: /media
bind:
create_host_path: true
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
traefik:
external: true

View file

@ -0,0 +1,59 @@
services:
kitchenowl:
image: tombursch/kitchenowl:latest
container_name: ${CONTAINER_NAME}
restart: ${RESTART_POLICY}
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
environment:
- DATABASE_URL=${DB_PROVIDER}://${DB_USER}:${DB_PASSWORD}@db:${DB_PORT}/${DB_DATABASE}
- BACKEND_PROVIDER=${DB_BACKEND_PROVIDER}
networks:
- traefik
- internal_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.${ROUTER_NAME}.entrypoints=websecure"
- "traefik.http.routers.${ROUTER_NAME}.rule=Host(`${SUBDOMAIN}.${DOMAIN}`)"
- "traefik.http.routers.${ROUTER_NAME}.tls=true"
- "traefik.http.routers.${ROUTER_NAME}.tls.certresolver=${CERTIFICATE_RESOLVER}"
- "traefik.http.services.${ROUTER_NAME}.loadbalancer.server.port=${CONTAINER_PORT}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
- "traefik.http.middlewares.${ROUTER_NAME}-stripprefix.headers.customrequestheaders.X-Forwarded-Proto=http"
- "traefik.http.routers.${ROUTER_NAME}.middlewares=kitchenowl-compress"
- "traefik.http.middlewares.${ROUTER_NAME}-compress.compress=true"
depends_on:
- db
db:
image: postgres:15-alpine
container_name: ${CONTAINER_NAME}_db
restart: ${RESTART_POLICY}
environment:
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=${DB_DATABASE}
volumes:
- type: bind
source: ${DB_PATH}
target: /var/lib/postgresql/data
bind:
create_host_path: true
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
- internal_net
networks:
traefik:
external: true
internal_net:
driver: bridge

60
matrix/docker-compose.yml Executable file
View file

@ -0,0 +1,60 @@
name: matrix
services:
synapse:
container_name: ${CONTAINER_NAME}
hostname: ${CONTAINER_NAME}
image: matrixdotorg/synapse:latest
restart: ${RESTART_POLICY}
cpu_shares: 90
deploy:
resources:
limits:
memory: 15841M
entrypoint:
- /bin/sh
- -c
- >
set -e
if [ ! -f /data/homeserver.yaml ]; then
echo "Generating initial configuration..."
/start.py generate
echo "" >> /data/homeserver.yaml
echo "enable_registration: true" >> /data/homeserver.yaml
echo "enable_registration_without_verification: true" >> /data/homeserver.yaml
fi
echo "Starting Synapse..."
exec /start.py
environment:
- SYNAPSE_REPORT_STATS=${SYNAPSE_REPORT_STATS}
- SYNAPSE_SERVER_NAME=${SUBDOMAIN}.${DOMAIN}
networks:
- traefik
volumes:
- type: bind
source: ${DATA_PATH}
target: /data
bind:
create_host_path: true
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
labels:
- "traefik.enable=true"
- "traefik.http.routers.${ROUTER_NAME}.entrypoints=websecure"
- "traefik.http.routers.${ROUTER_NAME}.rule=Host(`${SUBDOMAIN}.${DOMAIN}`)"
- "traefik.http.routers.${ROUTER_NAME}.tls=true"
- "traefik.http.routers.${ROUTER_NAME}.tls.certresolver=${CERTIFICATE_RESOLVER}"
- "traefik.http.services.${ROUTER_NAME}.loadbalancer.server.port=${CONTAINER_PORT}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
networks:
traefik:
name: traefik
external: true

49
n8n/docker-compose.yml Normal file
View file

@ -0,0 +1,49 @@
name: n8n
services:
n8n:
image: docker.n8n.io/n8nio/n8n:latest
container_name: ${CONTAINER_NAME}
restart: ${RESTART_POLICY}
networks:
- traefik
environment:
- NODE_ENV=production
- N8N_PORT=${CONTAINER_PORT}
- N8N_PROTOCOL=https
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
- N8N_HOST=${SUBDOMAIN}.${DOMAIN}
- WEBHOOK_URL=https://${SUBDOMAIN}.${DOMAIN}/
- GENERIC_TIMEZONE=${TZ}
- TZ=${TZ}
volumes:
- type: bind
source: ${DATA_PATH}
target: /home/node/.n8n
bind:
create_host_path: true
- type: bind
source: ${FILES_PATH}
target: /files
bind:
create_host_path: true
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
labels:
- "traefik.enable=true"
- "traefik.http.routers.${ROUTER_NAME}.entrypoints=websecure"
- "traefik.http.routers.${ROUTER_NAME}.rule=Host(`${SUBDOMAIN}.${DOMAIN}`)"
- "traefik.http.routers.${ROUTER_NAME}.tls=true"
- "traefik.http.routers.${ROUTER_NAME}.tls.certresolver=${CERTIFICATE_RESOLVER}"
- "traefik.http.services.${ROUTER_NAME}.loadbalancer.server.port=${CONTAINER_PORT}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
networks:
traefik:
name: traefik
external: true

View file

@ -0,0 +1,44 @@
name: op-xwiki-stack
networks:
frontend:
name: op_xwiki_frontend
backend:
name: op_xwiki_backend
traefik:
name: traefik
external: true
services:
openproject_web:
image: openproject/openproject:17
container_name: ${CONTAINER_NAME}
restart: ${RESTART_POLICY}
environment:
- OPENPROJECT_SECRET_KEY_BASE=${OP_SECRET_KEY_BASE}
- SECRET_KEY_BASE=${SECRET_KEY_BASE}
volumes:
- type: bind
source: ${DATA_PATH}
target: /var/openproject
bind:
create_host_path: true
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
- frontend
- backend
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.${ROUTER_NAME}.entrypoints=websecure"
- "traefik.http.routers.${ROUTER_NAME}.rule=Host(`${SUBDOMAIN}.${DOMAIN}`)"
- "traefik.http.routers.${ROUTER_NAME}.tls=true"
- "traefik.http.routers.${ROUTER_NAME}.tls.certresolver=${CERTIFICATE_RESOLVER}"
- "traefik.http.services.${ROUTER_NAME}.loadbalancer.server.port=${CONTAINER_PORT}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"

View file

@ -0,0 +1,72 @@
name: passbolt
services:
passbolt-db:
image: mariadb:10.11
container_name: ${CONTAINER_NAME}-db
restart: ${RESTART_POLICY}
environment:
- MYSQL_ROOT_PASSWORD=${DB_MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=${DB_DATABASE}
- MYSQL_USER=${DB_USER}
- MYSQL_PASSWORD=${DB_PASSWORD}
volumes:
- type: bind
source: ${DB_PATH}
target: /var/lib/mysql
bind:
create_host_path: true
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
- internal_net
passbolt:
image: passbolt/passbolt:latest-ce
container_name: ${CONTAINER_NAME}
restart: ${RESTART_POLICY}
depends_on:
- passbolt-db
environment:
- APP_FULL_BASE_URL=https://${SUBDOMAIN}.${DOMAIN}
- DB_HOST=passbolt-db
- DB_USER=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- DB_DATABASE=${DB_DATABASE}
- EMAIL_DEFAULT_TRANSPORT=${EMAIL_TRANSPORT}
volumes:
- type: bind
source: ${PGP_PATH}
target: /etc/passbolt/gpg
bind:
create_host_path: true
- type: bind
source: ${JWT_PATH}
target: /etc/passbolt/jwt
bind:
create_host_path: true
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
- traefik
- internal_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.${ROUTER_NAME}.entrypoints=websecure"
- "traefik.http.routers.${ROUTER_NAME}.rule=Host(`${SUBDOMAIN}.${DOMAIN}`)"
- "traefik.http.routers.${ROUTER_NAME}.tls=true"
- "traefik.http.routers.${ROUTER_NAME}.tls.certresolver=${CERTIFICATE_RESOLVER}"
- "traefik.http.services.${ROUTER_NAME}.loadbalancer.server.port=${CONTAINER_PORT}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
networks:
traefik:
name: traefik
external: true
internal_net:
driver: bridge

192
readme.md Normal file
View file

@ -0,0 +1,192 @@
# 🏠 Homeserver Setup
Zentrale Dokumentation der Infrastruktur, Docker-Container und persistenten Daten meines Debian-basierten Homeservers.
---
## 🗺️ Projektstruktur
Das Setup trennt strikt zwischen persistenten Anwendungsdaten (`data/`) und den Docker-Konfigurationsdateien (`homeserver/`).
```text
## Tree
├── data
│   ├── backup_emmc
│   │   └── sys_ssh
│   ├── dyndns
│   │   └── ddns-data
│   ├── excalidraw
│   ├── forgejo
│   │   └── forgejo
│   ├── gantt
│   │   ├── dist
│   │   ├── env.d.ts
│   │   ├── gantt_dockerfolder
│   │   ├── index.html
│   │   ├── node_modules
│   │   ├── package.json
│   │   ├── package-lock.json
│   │   ├── public
│   │   ├── README.md
│   │   ├── src
│   │   ├── tsconfig.app.json
│   │   ├── tsconfig.json
│   │   ├── tsconfig.node.json
│   │   └── vite.config.ts
│   ├── jellyfin
│   │   ├── cache
│   │   ├── config
│   │   └── media
│   ├── kitchenowl
│   │   └── db_data
│   ├── letsencrypt
│   ├── matrix
│   │   └── data
│   ├── n8n
│   │   └── local-files
│   ├── openProject
│   ├── passbolt
│   │   ├── db-data
│   │   ├── gpg-keys
│   │   └── jwt-keys
│   ├── sure
│   │   ├── db
│   │   ├── enable_banking.pem
│   │   └── storage
│   ├── test
│   │   └── dns-check.sh
│   ├── traefik
│   │   ├── acme.json
│   │   └── letsencrypt
│   ├── wartungsskripte
│   │   ├── update_befehl.sh
│   │   └── update_docker.sh
│   └── xWiki
│   ├── postgres_data
│   └── xwiki_data
└── homeserver
│ └── docker-compose.yml
├── excalidraw
│ └── docker-compose.yml
├── forgejo
│ └── docker-compose.yml
├── gantt_dockerfolder
│ └── docker-compose.yml
├── jellyfin
│ └── docker-compose.yml
├── kitchenowl
│ └── docker-compose.yml
├── letsencrypt
├── matrix
│ └── docker-compose.yml
├── n8n
│ └── docker-compose.yml
├── openProject
│ └── docker-compose.yml
├── passbolt
│ └── docker-compose.yml
├── readme.md
├── sure
│ └── docker-compose.yml
├── test
│ └── dns-check.sh
├── traefik
│ └── docker-compose.yml
├── wartungsskripte
│ ├── update_befehl.sh
│ └── update_docker.sh
└── xWiki
└── docker-compose.yml
```
## 🚀 Infrastruktur & Netzwerk
Als zentraler Reverse Proxy wird Traefik eingesetzt. Er fängt alle Anfragen auf Port 80/443 ab, verteilt sie an die jeweiligen Docker-Container und erstellt automatisch SSL-Zertifikate via Let's Encrypt.
## Wichtige URLs im Netzwerk:
🌐 Git-Server (Forgejo): `https://git.bybenji.de` (SSH-Port: 222)
🌐 Passwortmanager: `https://passbolt.bybenji.de`
🌐 Streaming: `https://jellyfin.bybenji.de`
## 🛠️ Betrieb & Wartung
Container starten / stoppen
Um einen bestimmten Dienst zu starten, in den entsprechenden Ordner unter homeserver/ wechseln und ausführen:
```Bash
cd homeserver/jellyfin
docker compose up -d
```
### Updates
Unter `homeserver/wartungsskripte/` befinden sich Skripte zur Serverpflege.
`update_docker.sh`: Aktualisiert alle laufenden Docker-Container auf das neueste Image und räumt alte Images auf (docker system prune).
Aktuell ist ein Manuelles Wartungsfenster jeden Samstag um 21 Uhr gesetzt.
> [!NOTE] Automatisierung
Einrichtung als Cronjob für automatische Updates jeden Sonntag um 03:00 Uhr:
>```Bash
>0 3 * * 0 /bin/bash /mnt/PB960/data/gantt/homeserver/wartungsskripte/update_docker.>sh
>```
## 📦 Backup-Strategie
Um maximale Datensicherheit zu gewährleisten, werden alle Produktivdaten (`data/`) nach dem 3-2-1-Prinzip gesichert.
### 1. Datenbasis & Lokales Backup (Kopie 1 & 2 - Medium 1)
* **Produktivdaten:** Befinden sich live auf dem Produktivsystem.
* **Lokales Backup:** Täglich inkrementelle und monatlich vollständige Backups werden auf einem dedizierten **NAS** gesichert, das sich physisch im selben Serverrack befindet.
### 2. Offsite & Standby-Server (Kopie 3 - Standort extern)
* **Backup-Server:** Eine vollständige Kopie wird an einen geografisch getrennten Standort gesichert.
* **Hot-Standby:** Da dieser Server denselben Techstack besitzt, kann er bei Wartungsarbeiten oder Ausfällen des Hauptservers direkt als temporäres Produktivsystem zugeschaltet werden.
> [!NOTE]
> **Offene Baustelle: Das 2. Speichermedium**
> Für die vollständige Erfüllung des 3-2-1-Prinzips fehlt noch das zweite, alternative Speichermedium (z. B. eine rotierende externe USB-Festplatte (Air-Gapped) oder ein verschlüsselter S3-Cloud-Speicher wie Hetzner Storage Box).
### 🖥️ System-Backup
* Unabhängig von den Anwendungsdaten werden Abbilder des eMMC-Speichers sowie die systemrelevanten SSH-Schlüssel unter `data/backup_emmc/sys_ssh/` vorgehalten.
### 💾 Speicher-Architektur & NAS
Sowohl das lokale Produktiv-NAS als auch das Offsite-NAS sind speicherseitig identisch aufgebaut, um maximale Redundanz und volle Kompatibilität im Failover-Fall zu gewährleisten.
### 📍 Phase 1: Start-Setup (Aktueller Stand)
* **Main-Server:** 1x 14 TB Seagate Exos (Single Drive 14 TB Netto-Nutzdaten)
* **Offsite-Server:** 1x 14 TB Seagate Exos (Single Drive 14 TB Backup-Ziel)
* **Sicherheit:** Die Datenkomponente wird per täglichem Skript/Replikation auf den Offsite-Server gesichert. Es besteht in dieser Phase noch keine lokale Festplatten-Redundanz (RAID) beim Ausfall einer Platte vor Ort.
### 🚀 Phase 2: RAID 5 Erweiterung (Zukunft)
Sobald zusätzlicher Speicher oder lokale Ausfallsicherheit benötigt wird, wird jedes System um 2 weitere 14 TB Platten ergänzt:
* **Konfiguration:** Migration auf ein **3-Platten-RAID-5** pro Server.
* **Neuer Speicher:** **28 TB Netto-Nutzdaten** pro Server bei einer Fehlertoleranz von 1 ausfallenden Festplatte pro System.
## 🖥️ Offsite-Server & Techstack
Der Offsite-Server dient primär als geografisch getrenntes Backup-Ziel, ist jedoch hardware- und softwareseitig als **Hot-Standby** konzipiert. Bei einem Totalausfall oder Wartungsarbeiten am Hauptstandort kann er die primären Dienste temporär übernehmen.
### 🌐 Virtualisierungs-Plattform (Proxmox VE)
Als Betriebssystem-Unterbau wird **Proxmox VE (Virtual Environment)** eingesetzt. Dies ermöglicht eine strikte Kapselung und einfache Verwaltung der Ressourcen:
* **Hypervisor:** Typ-1-Bare-Metal-Hypervisor auf Debian-Basis.
* **LXC (Linux Containers):** Für ressourcensparende Anwendungen (wie den Docker-Host).
* **VMs (Virtuelle Maschinen):** Für isolierte Systeme, die einen eigenen Kernel oder ein anderes OS benötigen.
### 📦 Container-Architektur (Der "Bauchtanz")
Um den exakt gleichen Techstack wie auf dem Main-Server zu spiegeln, läuft innerhalb von Proxmox ein dedizierter LXC-Container (oder eine VM), der als **Docker-Host** fungiert.
* **Docker & Compose:** Innerhalb dieser Instanz wird die identische `homeserver/`-Struktur per Git synchronisiert gehalten.
* **Dienste-Status:** Die Docker-Container (Nextcloud, Immich, Jellyfin etc.) sind im Normalbetrieb gestoppt oder laufen im reinen Standby-Modus, um Ressourcen zu schonen, stehen aber für ein sofortiges Failover bereit.
### 🔄 Replikation & Backup-Infrastruktur (Phase 1)
In der aktuellen Phase (Single-Drive) sieht das Zusammenspiel wie folgt aus:
1. **ZFS / Local Storage:** Proxmox verwaltet die einzelne 14 TB Seagate Exos Festplatte als lokalen Speicherpool.
2. **Pull-/Push-Backups:** Der Hauptserver schiebt seine Daten (z. B. via `rsync`, `rclone` oder BorgBackup) verschlüsselt über das Netzwerk auf den Offsite-Proxmox-Speicher.
---
# Visualisierung
├── für einen Ordner oder eine Datei, wenn danach noch weitere Elemente auf derselben Ebene folgen.
└── für das letzte Element auf einer Ebene (das „Eckstück“).
│ für Linien, die an Unterordnern vorbeiführen (wichtig für die richtige Einrückung).

View file

@ -0,0 +1,110 @@
services:
sparkyfitness-db:
image: postgres:18.3-alpine
container_name: ${CONTAINER_NAME}-db
restart: ${RESTART_POLICY}
environment:
POSTGRES_DB: ${SPARKY_FITNESS_DB_NAME}
POSTGRES_USER: ${SPARKY_FITNESS_DB_USER}
POSTGRES_PASSWORD: ${SPARKY_FITNESS_DB_PASSWORD}
PUID: 1000
GUID: 1000
volumes:
- type: bind
source: ${DB_PATH:-./postgresql}
target: /var/lib/postgresql
bind:
create_host_path: true
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
- sparkyfitness-network
sparkyfitness-server:
image: codewithcj/sparkyfitness_server:latest
environment:
SPARKY_FITNESS_LOG_LEVEL: ${SPARKY_FITNESS_LOG_LEVEL}
ALLOW_PRIVATE_NETWORK_CORS: ${ALLOW_PRIVATE_NETWORK_CORS:-false}
SPARKY_FITNESS_EXTRA_TRUSTED_ORIGINS: ${SPARKY_FITNESS_EXTRA_TRUSTED_ORIGINS}
SPARKY_FITNESS_PUBLIC_API_DOCS: ${SPARKY_FITNESS_PUBLIC_API_DOCS:-false}
SPARKY_FITNESS_DB_USER: ${SPARKY_FITNESS_DB_USER:-sparky}
SPARKY_FITNESS_DB_HOST: ${SPARKY_FITNESS_DB_HOST:-sparkyfitness-db}
SPARKY_FITNESS_DB_NAME: ${SPARKY_FITNESS_DB_NAME}
SPARKY_FITNESS_DB_PASSWORD: ${SPARKY_FITNESS_DB_PASSWORD}
SPARKY_FITNESS_APP_DB_USER: ${SPARKY_FITNESS_APP_DB_USER:-sparkyapp}
SPARKY_FITNESS_APP_DB_PASSWORD: ${SPARKY_FITNESS_APP_DB_PASSWORD}
SPARKY_FITNESS_DB_PORT: 5432
SPARKY_FITNESS_API_ENCRYPTION_KEY: ${SPARKY_FITNESS_API_ENCRYPTION_KEY}
BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET}
SPARKY_FITNESS_FRONTEND_URL: ${SPARKY_FITNESS_FRONTEND_URL:-http://0.0.0.0}
SPARKY_FITNESS_DISABLE_SIGNUP: ${SPARKY_FITNESS_DISABLE_SIGNUP}
SPARKY_FITNESS_ADMIN_EMAIL: ${SPARKY_FITNESS_ADMIN_EMAIL}
SPARKY_FITNESS_EMAIL_HOST: ${SPARKY_FITNESS_EMAIL_HOST}
SPARKY_FITNESS_EMAIL_PORT: ${SPARKY_FITNESS_EMAIL_PORT}
SPARKY_FITNESS_EMAIL_SECURE: ${SPARKY_FITNESS_EMAIL_SECURE}
SPARKY_FITNESS_EMAIL_USER: ${SPARKY_FITNESS_EMAIL_USER}
SPARKY_FITNESS_EMAIL_PASS: ${SPARKY_FITNESS_EMAIL_PASS}
SPARKY_FITNESS_EMAIL_FROM: ${SPARKY_FITNESS_EMAIL_FROM}
GARMIN_MICROSERVICE_URL: http://sparkyfitness-garmin:8000
HTTP_PROXY: ${HTTP_PROXY:-}
HTTPS_PROXY: ${HTTPS_PROXY:-}
NO_PROXY: ${NO_PROXY:-}
PUID: 1000
GUID: 1000
networks:
- sparkyfitness-network
restart: always
depends_on:
- sparkyfitness-db
volumes:
- type: bind
source: ${SERVER_BACKUP_PATH:-./backup}
target: /app/SparkyFitnessServer/backup
bind:
create_host_path: true
- type: bind
source: ${SERVER_UPLOADS_PATH:-./uploads}
target: /app/SparkyFitnessServer/uploads
bind:
create_host_path: true
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
sparkyfitness-frontend:
image: codewithcj/sparkyfitness:latest
environment:
SPARKY_FITNESS_FRONTEND_URL: ${SPARKY_FITNESS_FRONTEND_URL}
SPARKY_FITNESS_SERVER_HOST: ${CONTAINER_NAME}-server
SPARKY_FITNESS_SERVER_PORT: 3010
PUID: 1000
GUID: 1000
networks:
- sparkyfitness-network
- traefik
restart: ${RESTART_POLICY}
depends_on:
- sparkyfitness-server
labels:
- "traefik.enable=true"
- "traefik.http.routers.${ROUTER_NAME}.entrypoints=websecure"
- "traefik.http.routers.${ROUTER_NAME}.rule=Host(`${SUBDOMAIN}.${DOMAIN}`)"
- "traefik.http.routers.${ROUTER_NAME}.tls=true"
- "traefik.http.routers.${ROUTER_NAME}.tls.certresolver=${CERTIFICATE_RESOLVER}"
- "traefik.http.services.${ROUTER_NAME}.loadbalancer.server.port=${CONTAINER_PORT}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
- "traefik.http.routers.${ROUTER_NAME}.middlewares=sparkyfitness-headers"
- "traefik.http.middlewares.${ROUTER_NAME}-headers.headers.customrequestheaders.X-Forwarded-Proto=https"
- "traefik.http.middlewares.${ROUTER_NAME}-headers.headers.customrequestheaders.X-Forwarded-Host=fitness.bybenji.de"
networks:
sparkyfitness-network:
driver: bridge
traefik:
external: true

149
sure/docker-compose.yml Normal file
View file

@ -0,0 +1,149 @@
name: sure
networks:
traefik:
name: traefik
external: true
internal_net:
name: sure_internal_net
driver: bridge
services:
sure-db:
image: postgres:16-alpine
container_name: ${CONTAINER_NAME}-db
restart: ${RESTART_POLICY}
environment:
- POSTGRES_DB=${DB_DATABASE}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_USER=${DB_USER}
volumes:
- type: bind
source: ${DB_PATH}
target: /var/lib/postgresql/data
bind:
create_host_path: true
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
- internal_net
cpu_shares: 90
deploy:
resources:
limits:
memory: 15841M
sure-redis:
image: redis:7-alpine
container_name: ${CONTAINER_NAME}-redis
restart: ${RESTART_POLICY}
volumes:
- type: bind
source: ${REDIS_PATH}
target: /data
bind:
create_host_path: true
networks:
- internal_net
cpu_shares: 90
deploy:
resources:
limits:
memory: 15841M
web:
image: ghcr.io/we-promise/sure:stable
container_name: ${CONTAINER_NAME}-web
restart: ${RESTART_POLICY}
depends_on:
sure-db:
condition: service_started
sure-redis:
condition: service_started
environment:
- SELF_HOSTED=${SELF_HOSTED}
- ALLOW_REGISTRATION=${ALLOW_REGISTRATION}
- SECRET_KEY_BASE=${SECRET_KEY_BASE}
- RAILS_ASSUME_SSL=${RAILS_ASSUME_SSL}
- RAILS_FORCE_SSL=${RAILS_FORCE_SSL}
- DB_HOST=sure-db
- POSTGRES_DB=${DB_DATABASE}
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- REDIS_URL=${REDIS_URL}
volumes:
- type: bind
source: ${STORAGE_PATH}
target: /rails/storage
bind:
create_host_path: true
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
- traefik
- internal_net
cpu_shares: 90
deploy:
resources:
limits:
memory: 15841M
labels:
- "traefik.enable=true"
- "traefik.http.routers.${ROUTER_NAME}.entrypoints=websecure"
- "traefik.http.routers.${ROUTER_NAME}.rule=Host(`${SUBDOMAIN}.${DOMAIN}`)"
- "traefik.http.routers.${ROUTER_NAME}.tls=true"
- "traefik.http.routers.${ROUTER_NAME}.tls.certresolver=${CERTIFICATE_RESOLVER}"
- "traefik.http.services.${ROUTER_NAME}.loadbalancer.server.port=${CONTAINER_PORT}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
worker:
image: ghcr.io/we-promise/sure:stable
container_name: ${CONTAINER_NAME}-worker
restart: ${RESTART_POLICY}
command:
- /rails/bin/docker-entrypoint
- ./bin/bundle
- exec
- sidekiq
depends_on:
sure-db:
condition: service_started
sure-redis:
condition: service_started
environment:
- APP_URL=https://${SUBDOMAIN}.${DOMAIN}
- DB_HOST=sure-db
- POSTGRES_DB=${DB_DATABASE}
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- REDIS_URL=${REDIS_URL}
- SECRET_KEY_BASE=${SECRET_KEY_BASE}
volumes:
- type: bind
source: ${STORAGE_PATH}
target: /rails/storage
bind:
create_host_path: true
- type: bind
source: ${BANKING_PATH}
target: /rails/config/enable_banking.pem
read_only: true
bind:
create_host_path: true
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
- internal_net
cpu_shares: 90
deploy:
resources:
limits:
memory: 15841M

19
test/dns-check.sh Executable file
View file

@ -0,0 +1,19 @@
#!/bin/bash
while true; do
clear
echo "=== Nameserver-Überwachung für bybenji.de ==="
echo "Letzte Prüfung: $(date +%H:%M:%S)"
echo "----------------------------------------"
NS_INFO=$(nslookup -type=ns budget.bybenji.de 8.8.8.8 2>&1)
echo "$NS_INFO"
if echo "$NS_INFO" | grep -q "desec.io"; then
echo -e "\n\a🚀🚀🚀 ERFOLG! Die Nameserver wurden auf deSEC umgestellt! 🚀🚀🚀"
break
fi
echo -e "\n[Warten auf deSEC...] Nächste Prüfung in 30 Sekunden. (Abbrechen mit STRG+C)"
sleep 30
done

View file

@ -0,0 +1,63 @@
name: traefik
networks:
traefik:
name: traefik
# external: true -> Entfernt, damit dieser Stack das Netzwerk global auf dem Server erstellt!
services:
traefik:
image: traefik:latest
container_name: ${CONTAINER_NAME}
hostname: traefik
restart: ${RESTART_POLICY}
cpu_shares: 90
deploy:
resources:
limits:
memory: 15841M
command:
- --api.insecure=true
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entryPoint.to=websecure
- --entrypoints.web.http.redirections.entryPoint.scheme=https
- --entrypoints.websecure.address=:443
- --certificatesresolvers.myresolver.acme.dnschallenge=true
- --certificatesresolvers.myresolver.acme.dnschallenge.provider=desec
- --certificatesresolvers.myresolver.acme.dnschallenge.delaybeforecheck=10
- --certificatesresolvers.myresolver.acme.email=${CERT_RESOLVER_EMAIL}
- --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.myresolver.acme.dnschallenge.resolvers=${DNS_RESOLVERS}
environment:
- DESEC_TOKEN=${DESEC_TOKEN}
- DESEC_TTL=${DESEC_TTL}
- DESEC_POLLING_INTERVAL=${DESEC_POLLING_INTERVAL}
- DESEC_PROPAGATION_TIMEOUT=${DESEC_PROPAGATION_TIMEOUT}
ports:
- "${HTTP_PORT}:80"
- "${HTTPS_PORT}:443"
- "${DASHBOARD_PORT}:8080"
volumes:
- type: bind
source: /var/run/docker.sock
target: /var/run/docker.sock
read_only: true
- type: bind
source: ${LETSENCRYPT_PATH}
target: /letsencrypt
bind:
create_host_path: true
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
- traefik

View file

@ -0,0 +1 @@
sudo ./update_docker.sh

View file

@ -0,0 +1,72 @@
#!/bin/bash
PROJECTS=(
"/mnt/PB960/homeserver/dyndns"
"/mnt/PB960/homeserver/excalidraw"
"/mnt/PB960/homeserver/forgejo"
"/mnt/PB960/homeserver/gantt_dockerfolder"
"/mnt/PB960/homeserver/jellyfin"
"/mnt/PB960/homeserver/kitchenowl"
"/mnt/PB960/homeserver/matrix"
"/mnt/PB960/homeserver/n8n"
"/mnt/PB960/homeserver/openProject"
"/mnt/PB960/homeserver/passbolt"
"/mnt/PB960/homeserver/sparkyFitness"
"/mnt/PB960/homeserver/sure"
"/mnt/PB960/homeserver/traefik"
"/mnt/PB960/homeserver/xWiki"
)
echo "=========================================="
echo " STARRTE AUTOMATISCHES DOCKER-UPDATE "
echo " Datum: $(date '+%Y-%m-%d %H:%M:%S')"
echo "=========================================="
# Prüfen, ob das Skript als Root ausgeführt wird
if [ "$EUID" -ne 0 ]; then
echo "[FEHLER] Bitte starte das Skript mit 'sudo'!"
exit 1
fi
# Aktuellste Version des Repos holen
echo "[1/2] Aktualisiere Git-Repository (Homeserver)..."
cd /mnt/PB960/homeserver && git pull
echo "[2/2] Aktualisiere Git-Repository (Gantt)..."
cd /mnt/PB960/data/gantt && git pull
for TARGET_DIR in "${PROJECTS[@]}"; do
if [ -d "$TARGET_DIR" ]; then
echo ""
echo "------------------------------------------"
echo "[INFO] Verarbeite Projekt in: $TARGET_DIR"
echo "------------------------------------------"
cd "$TARGET_DIR" || continue
# 1. Container sauber stoppen
echo "[1/4] Stoppe laufende Container..."
docker compose down
# 2. Neueste Versionen/Images aus dem Netz ziehen
echo "[2/4] Ziehe neueste Docker-Images (Pull)..."
docker compose pull
# 3. Container im Hintergrund neu starten (erzwingt das Update)
echo "[3/4] Starte Container neu (erzwinge Update)..."
docker compose up -d --build --force-recreate
# 4. Alte, ungenutzte Image-Reste löschen (spart Speicherplatz)
echo "[4/4] Bereinige alte Image-Leichen..."
docker image prune -f
echo "[ERFOLG] Projekt $TARGET_DIR wurde aktualisiert!"
else
echo "[WARNUNG] Verzeichnis $TARGET_DIR existiert nicht. Überspringe..."
fi
done
echo ""
echo "=========================================="
echo " ALLE UPDATES ERFOLGREICH DURCHGEFÜHRT! "
echo "=========================================="

70
xWiki/docker-compose.yml Normal file
View file

@ -0,0 +1,70 @@
name: xwiki
networks:
frontend:
name: op_xwiki_frontend
backend:
name: op_xwiki_backend
traefik:
name: traefik
external: true
services:
xwiki_db:
image: postgres:15-alpine
container_name: ${CONTAINER_NAME}_postgres
restart: ${RESTART_POLICY}
environment:
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=${DB_DATABASE}
volumes:
- type: bind
source: ${DB_PATH}
target: /var/lib/postgresql/data
bind:
create_host_path: true
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
xwiki_web:
image: xwiki:lts-postgres-tomcat
container_name: ${CONTAINER_NAME}_web
restart: ${RESTART_POLICY}
environment:
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=${DB_DATABASE}
- DB_HOST=xwiki_db
- JAVA_OPTS=-Xmx1024m
volumes:
- type: bind
source: ${DATA_PATH}
target: /usr/local/xwiki
bind:
create_host_path: true
networks:
- frontend
- backend
- traefik
depends_on:
xwiki_db:
condition: service_healthy
labels:
- "traefik.enable=true"
- "traefik.http.routers.${ROUTER_NAME}.entrypoints=websecure"
- "traefik.http.routers.${ROUTER_NAME}.rule=Host(`${SUBDOMAIN}.${DOMAIN}`)"
- "traefik.http.routers.${ROUTER_NAME}.tls=true"
- "traefik.http.routers.${ROUTER_NAME}.tls.certresolver=${CERTIFICATE_RESOLVER}"
- "traefik.http.services.${ROUTER_NAME}.loadbalancer.server.port=${CONTAINER_PORT}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"