Skip to content

Audit before you touch anything

The plan assumed Traefik and Gitea were running on the VPS. They weren’t — and finding that out read-only, before changing anything, shaped the whole rebuild.

None of these need root or change anything:

Terminal window
hostname; uname -m # which machine, which CPU
docker ps -a; docker network ls # containers and networks
docker volume ls; docker image ls # what data and images remain
ss -ltn # what is listening, and where
systemctl list-units --type=service --state=running
ls /opt /srv /etc/*infra* 2>/dev/null # where configs might live
docker system df -v # volume sizes

What it showed here:

  • Zero containers, no custom networks — but 13 volumes (Traefik certs, a Gitea with registry and runner, several apps) and 23 images. A previous Docker Swarm stack had been removed by hand (docker service rm, prune, swarm leave), leaving its data.
  • Nothing listening publicly. sshd on 127.0.0.1 only (reached through a Cloudflare Tunnel), so every hostname returned Cloudflare’s 521 (“origin down”).
  • The CPU is x86_64, while the laptop building images was arm64 — an image built locally would not run there (Going live).
  • A newer infrastructure repo (/opt/vps-infra) had been cloned but never started: no .env, empty secrets/.

The old images, volumes and stack files are a working reference for how the previous setup was configured. So until the new system is live:

  • no docker system prune, image prune, volume prune — nor wrapper targets that call them;
  • old volumes are never mounted, even read-write by a new service;
  • new services get fresh volumes, and the names are checked not to collide.

They’re retired at the very end, by name, after backups work (Retiring).

The volumes were the only copy of that data. Each one goes into a tarball via a throwaway container that mounts it read-only:

Terminal window
sudo mkdir -p /var/backups/pre-rebuild && sudo chmod 700 /var/backups/pre-rebuild
for v in <volume-1> <volume-2> …; do
sudo docker run --rm -v "$v":/src:ro -v /var/backups/pre-rebuild:/out alpine:latest \
tar czf "/out/$v.tgz" -C /src . && echo "ok $v"
done

Then prove every archive is complete — compressed stream intact, archive readable, and the same number of entries as the source volume:

Terminal window
for f in /b/*.tgz; do gzip -t "$f" && tar tzf "$f" >/dev/null && echo "intact $f ($(tar tzf "$f" | wc -l) entries)"; done
docker run --rm -v <volume>:/s:ro alpine:latest sh -c 'find /s | wc -l'