From c100db57fe68f1226a1bbca8a93446a5c4078765 Mon Sep 17 00:00:00 2001 From: Vladimir Eremin Date: Tue, 7 Jul 2026 21:15:15 +0300 Subject: [PATCH] fix: mount /srv in resticontainer so absolute tierN symlinks resolve The tierN bind mounts are absolute symlinks into /srv (compositions/

/tierN -> /srv/com-*-tierN/

). Under /:/hostfs those targets are dangling inside the container (restic reported "does not exist, skipping" for every path). Mount /srv:/srv:ro at the same path with RESTIC_HOSTFS=/ so restic follows the symlinks and records the real host paths. --- PARITY.md | 7 ++++--- compositions/apex/docker-compose.yml | 15 ++++++++++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/PARITY.md b/PARITY.md index 7860e19..c21584e 100644 --- a/PARITY.md +++ b/PARITY.md @@ -65,9 +65,10 @@ action to its bash source and the behaviors that must match. `restic.*` compose labels: `restic.enable`, `restic.backup.paths` (comma-separated container mount destinations — matched exactly, whole mount dir backed up), `restic.backup.stop`, and `restic.hooks.pre-backup`/`post-backup` (run via docker exec). -- It mounts the docker socket (list/inspect/exec/stop/start) and the host root - `/:/hostfs:ro` (`RESTIC_HOSTFS=/hostfs`); it resolves each labelled mount to - `/hostfs` + the mount's host source and snapshots the union in one restic run. +- It mounts the docker socket (list/inspect/exec/stop/start) and `/srv:/srv:ro` + (`RESTIC_HOSTFS=/`); apex data + checkouts live under /srv and the `@tierN` mounts + are absolute symlinks into /srv, so mounting /srv at the same path lets restic + follow them and record the real host paths, and snapshots the union in one run. - The whole-tier-root RO mounts and the explicit `/data/@tier1,2` backup args are gone; a service is backed up only if it carries `restic.enable=true`. DB consistency is per-service: a pre-backup hook dumps into a dedicated backed-up mount (e.g. postgres diff --git a/compositions/apex/docker-compose.yml b/compositions/apex/docker-compose.yml index d35e9ff..3094ac6 100644 --- a/compositions/apex/docker-compose.yml +++ b/compositions/apex/docker-compose.yml @@ -32,11 +32,11 @@ services: # (restic.enable / restic.backup.paths / restic.backup.stop / restic.hooks.*). # On `backup` it queries the docker daemon, runs each service's pre-hook (e.g. # pg_dump), stops any container flagged restic.backup.stop, resolves the labeled - # container mounts to host paths (via /hostfs + mount source), snapshots the union + # container mounts to host paths (via the /srv mount), snapshots the union # in a single restic run, then restarts + runs post-hooks. Every other subcommand # (init/snapshots/forget/…) passes straight through to restic. Needs the docker - # socket (list/inspect/exec/stop/start) and the host root read-only (to read the - # data) — a privileged manual one-shot, same trust surface as the old restic core. + # socket (list/inspect/exec/stop/start) and /srv read-only (the fleet's data root) + # — a privileged manual one-shot, same trust surface as the old restic core. resticontainer: image: ghcr.io/deytenit/resticontainer:0.19.1 container_name: apex-resticontainer @@ -44,10 +44,15 @@ services: volumes: - ${APEX_TIER3}/restic/cache:/var/restic-cache - /var/run/docker.sock:/var/run/docker.sock # query + exec/stop/start - - /:/hostfs:ro # read the data off the host + # apex tier data + checkouts live under /srv, and the @tierN bind mounts are + # ABSOLUTE symlinks into /srv (compositions/

/@tierN -> /srv/com-…-tierN/

). + # Mount /srv at the same path with RESTIC_HOSTFS=/ so restic follows those + # symlinks and records the real host paths — a bare /:/hostfs cannot, because + # the absolute symlink target /srv/… is dangling inside the container. + - /srv:/srv:ro # read the data (resolves @tierN symlinks) environment: <<: *shared-environment - RESTIC_HOSTFS: /hostfs # prefix for resolved mount sources + RESTIC_HOSTFS: "/" # mount sources are already absolute (/srv/…) RESTIC_REPOSITORY: "${APEX_RESTIC_REPOSITORY?error}" RESTIC_PASSWORD: "${APEX_RESTIC_PASSWORD?error}" AWS_ACCESS_KEY_ID: "${APEX_RESTIC_AWS_ACCESS_KEY_ID?error}"