Compare commits
13 Commits
7f1767345a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d2cfc1be5 | |||
| 35c02d6920 | |||
| a3d7803819 | |||
| 2fb046a880 | |||
| 70febee487 | |||
| 82ff0dc817 | |||
| 0f2ec2db3b | |||
| b782b4b8aa | |||
| d875c9c0cc | |||
| df498a8494 | |||
| 5803215702 | |||
| 2e78934b3a | |||
| ac80d261b8 |
+136
-5
@@ -147,8 +147,22 @@
|
|||||||
"d /data/backup 0775 root root -"
|
"d /data/backup 0775 root root -"
|
||||||
"d /data/backup/timemachine 0775 danlin users -"
|
"d /data/backup/timemachine 0775 danlin users -"
|
||||||
|
|
||||||
# FileBrowser config
|
# FileBrowser config — muss dem Container-User (uid 1000) gehören,
|
||||||
"d /data/filebrowser 0755 root root -"
|
# das Image läuft non-root als filebrowser:1000.
|
||||||
|
"d /data/filebrowser 0755 danlin users -"
|
||||||
|
|
||||||
|
# Das Image bringt eine config.yaml mit, die aber vom Bind-Mount auf
|
||||||
|
# /home/filebrowser/data verdeckt wird — ohne sie startet es nicht.
|
||||||
|
# "C" legt sie nur an, wenn sie fehlt; Änderungen bleiben erhalten.
|
||||||
|
"C /data/filebrowser/config.yaml 0644 danlin users - ${pkgs.writeText "filebrowser-config.yaml" ''
|
||||||
|
server:
|
||||||
|
port: 80
|
||||||
|
baseURL: "/"
|
||||||
|
logging:
|
||||||
|
- levels: "info|warning|error"
|
||||||
|
sources:
|
||||||
|
- path: "/srv"
|
||||||
|
''}"
|
||||||
|
|
||||||
# Secrets
|
# Secrets
|
||||||
"d /data/secrets 0700 root root -"
|
"d /data/secrets 0700 root root -"
|
||||||
@@ -167,13 +181,17 @@
|
|||||||
ports = [ "8080:80" ];
|
ports = [ "8080:80" ];
|
||||||
volumes = [
|
volumes = [
|
||||||
"/data:/srv"
|
"/data:/srv"
|
||||||
"/data/filebrowser:/config"
|
# Das Image legt DB + config.yaml unter /home/filebrowser/data ab
|
||||||
|
# (FILEBROWSER_DATABASE/-CONFIG). Ohne diesen Mount lebt die Datenbank
|
||||||
|
# nur im Container und ist nach jedem Neustart weg.
|
||||||
|
"/data/filebrowser:/home/filebrowser/data"
|
||||||
];
|
];
|
||||||
environment = {
|
environment = {
|
||||||
TZ = "Europe/Berlin";
|
TZ = "Europe/Berlin";
|
||||||
FB_DATABASE = "/config/filebrowser.db";
|
|
||||||
};
|
};
|
||||||
environmentFiles = [ "/data/secrets/filebrowser.env" ];
|
environmentFiles = [ "/data/secrets/filebrowser.env" ];
|
||||||
|
# Läuft als non-root (uid 1000) und dürfte Port 80 sonst nicht binden.
|
||||||
|
extraOptions = [ "--sysctl" "net.ipv4.ip_unprivileged_port_start=0" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
virtualisation.oci-containers.containers.gitea = {
|
virtualisation.oci-containers.containers.gitea = {
|
||||||
@@ -193,6 +211,87 @@
|
|||||||
GITEA__server__SSH_PORT = "2222";
|
GITEA__server__SSH_PORT = "2222";
|
||||||
GITEA__server__SSH_LISTEN_PORT = "2222";
|
GITEA__server__SSH_LISTEN_PORT = "2222";
|
||||||
GITEA__server__START_SSH_SERVER = "true";
|
GITEA__server__START_SSH_SERVER = "true";
|
||||||
|
# Actions (CI) aktivieren — Voraussetzung für den act_runner unten.
|
||||||
|
GITEA__actions__ENABLED = "true";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Gitea Actions Runner
|
||||||
|
########################################
|
||||||
|
# Job-Container laufen über das bereits vorhandene Podman (das Modul erkennt
|
||||||
|
# das automatisch: DOCKER_HOST -> /run/podman/podman.sock, Gruppe "podman").
|
||||||
|
services.gitea-actions-runner = {
|
||||||
|
package = pkgs.gitea-actions-runner;
|
||||||
|
instances.fileserver = {
|
||||||
|
enable = true;
|
||||||
|
name = "fileserver";
|
||||||
|
# Interne LAN-URL — vom Host UND aus den Docker-Job-Containern erreichbar
|
||||||
|
# (localhost/127.0.0.1 würde im Job-Container auf den Container selbst zeigen).
|
||||||
|
url = "http://10.202.82.6:3000";
|
||||||
|
# Registration-Token: in Gitea unter Site Admin → Actions → Runners →
|
||||||
|
# "Create new Runner" erzeugen und in diese Datei schreiben (0600 root).
|
||||||
|
tokenFile = "/data/secrets/gitea-runner-token";
|
||||||
|
labels = [
|
||||||
|
"ubuntu-latest:docker://node:20-bookworm"
|
||||||
|
"ubuntu-22.04:docker://node:20-bookworm"
|
||||||
|
"alpine:docker://alpine:3.19"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Reaper für verwaiste Job-Container. Wird der Runner mitten in einem Job
|
||||||
|
# gestoppt (Reboot, autoUpgrade, Gitea-Neustart), läuft dessen Container mit
|
||||||
|
# "sleep 10800" bis zu 3h weiter und wird nie abgeräumt. Dieser Timer entfernt
|
||||||
|
# solche Container — aber NUR, wenn Gitea den zugehörigen Task als beendet
|
||||||
|
# führt (Status 1/2/3/4). Laufende (6) oder unbekannte Tasks bleiben unberührt.
|
||||||
|
systemd.services.gitea-runner-reaper = {
|
||||||
|
description = "Reap orphaned Gitea Actions job containers";
|
||||||
|
after = [ "podman.service" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
User = "root";
|
||||||
|
ExecStart = pkgs.writeShellScript "gitea-runner-reaper" ''
|
||||||
|
set -uo pipefail
|
||||||
|
PODMAN=${pkgs.podman}/bin/podman
|
||||||
|
|
||||||
|
# Ohne laufenden Gitea-Container keine DB-Abfrage möglich -> nichts tun.
|
||||||
|
if ! $PODMAN container exists gitea; then
|
||||||
|
echo "gitea container not running — skipping reap"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
$PODMAN ps --filter name=GITEA-ACTIONS-TASK- --format '{{.Names}}' | while read -r c; do
|
||||||
|
[ -n "$c" ] || continue
|
||||||
|
tid=$(printf '%s' "$c" | sed -n 's/^GITEA-ACTIONS-TASK-\([0-9]\+\)_.*/\1/p')
|
||||||
|
[ -n "$tid" ] || continue
|
||||||
|
# immutable=1: lock-freier read-only Zugriff auf die (von Gitea offene) DB.
|
||||||
|
st=$($PODMAN exec gitea sqlite3 "file:/data/gitea/gitea.db?immutable=1" \
|
||||||
|
"SELECT status FROM action_task WHERE id=$tid;" 2>/dev/null)
|
||||||
|
# Status: 1=success 2=failure 3=cancelled 4=skipped 5=waiting 6=running
|
||||||
|
case "$st" in
|
||||||
|
1|2|3|4)
|
||||||
|
echo "reaping orphan $c (task $tid finished, status=$st)"
|
||||||
|
$PODMAN rm -f "$c" >/dev/null 2>&1 || true
|
||||||
|
;;
|
||||||
|
*) : ;; # 6/5/leer/unbekannt -> in Ruhe lassen
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Übrig gebliebene Actions-Netzwerke/Volumes und dangling Images aufräumen
|
||||||
|
# (prune entfernt nur, was an KEINEN laufenden Container gebunden ist).
|
||||||
|
$PODMAN network prune -f >/dev/null 2>&1 || true
|
||||||
|
$PODMAN volume prune -f >/dev/null 2>&1 || true
|
||||||
|
$PODMAN image prune -f >/dev/null 2>&1 || true
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.timers.gitea-runner-reaper = {
|
||||||
|
wantedBy = [ "timers.target" ];
|
||||||
|
timerConfig = {
|
||||||
|
OnCalendar = "*:0/15";
|
||||||
|
Persistent = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -203,10 +302,42 @@
|
|||||||
description = "Backup /data to Google Drive via rclone";
|
description = "Backup /data to Google Drive via rclone";
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
ExecStart = ''
|
ExecStart = pkgs.writeShellScript "rclone-backup" ''
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
# Laufende SQLite-DBs lassen sich nicht als Datei kopieren — sie ändern
|
||||||
|
# sich während des Transfers (md5-Fehler). Daher vorab ein konsistentes
|
||||||
|
# Hot-Backup (.backup) in einen Snapshot-Ordner ziehen, der mitgesichert
|
||||||
|
# wird; die Live-Dateien selbst schließen wir unten aus.
|
||||||
|
SNAP=/data/db-snapshots
|
||||||
|
mkdir -p "$SNAP"
|
||||||
|
for db in \
|
||||||
|
/data/gitea/gitea/gitea.db \
|
||||||
|
/data/filebrowser/database.db \
|
||||||
|
/data/daten/DJing/Data/MusicLibrary.db; do
|
||||||
|
[ -f "$db" ] || continue
|
||||||
|
out="$SNAP/$(echo "$db" | sed 's#^/data/##; s#/#_#g')"
|
||||||
|
# Echtes SQLite -> konsistentes .backup. Andernfalls (filebrowser
|
||||||
|
# nutzt z.B. BoltDB) oder bei Sperre auf eine Rohkopie zurückfallen.
|
||||||
|
if ${pkgs.sqlite}/bin/sqlite3 "$db" ".timeout 15000" ".backup '$out'" 2>/dev/null && [ -s "$out" ]; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
cp -f "$db" "$out" || echo "WARN: snapshot of $db failed" >&2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
${pkgs.rclone}/bin/rclone sync /data gdrive:backup-daten \
|
${pkgs.rclone}/bin/rclone sync /data gdrive:backup-daten \
|
||||||
--fast-list \
|
--fast-list \
|
||||||
--drive-stop-on-upload-limit \
|
--drive-stop-on-upload-limit \
|
||||||
|
--tpslimit 25 \
|
||||||
|
--drive-pacer-min-sleep 10ms \
|
||||||
|
--retries 3 \
|
||||||
|
--exclude "**/*.db-journal" \
|
||||||
|
--exclude "**/*.db-wal" \
|
||||||
|
--exclude "**/*.db-shm" \
|
||||||
|
--exclude "gitea/gitea/gitea.db" \
|
||||||
|
--exclude "filebrowser/database.db" \
|
||||||
|
--exclude "daten/DJing/Data/MusicLibrary.db" \
|
||||||
--log-file=/var/log/rclone-backup.log \
|
--log-file=/var/log/rclone-backup.log \
|
||||||
--log-level=INFO
|
--log-level=INFO
|
||||||
'';
|
'';
|
||||||
|
|||||||
Reference in New Issue
Block a user