seriffic Claude Opus 4.7 (1M context) commited on
Commit
c099105
·
1 Parent(s): 62af342

Add fallback: scripts/save_droplet_image.sh

Browse files

Insurance for the case where the public-base Dockerfile diverges
from the bootstrap droplet's ABI in a way that breaks specialists.
Commits the running container as an image, saves to a compressed
tarball (zstd if available, gzip fallback), and scp's it
off-droplet to a backup dir.

Not run by default — tarball is ~15 GB compressed and the scp takes
the better part of an hour. Use only if the Dockerfile build can't
reproduce the working env (e.g. AMD changes the public ROCm release
in a way that drops MI300X kernels we depend on). For 99% of
redeploys, scripts/deploy_droplet.sh + the Dockerfile are the right
path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files changed (1) hide show
  1. scripts/save_droplet_image.sh +70 -0
scripts/save_droplet_image.sh ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # Riprap droplet image fallback — save the bootstrap droplet's
3
+ # riprap-models image to a portable tarball.
4
+ #
5
+ # Use this when the public-base Dockerfile (services/riprap-models/Dockerfile)
6
+ # can't be reproduced — e.g. when AMD's public ROCm release diverges from
7
+ # the bootstrap droplet's ABI in a way that breaks our specialists. The
8
+ # saved tarball is byte-identical to the running container's image, so
9
+ # `docker load` on a fresh droplet gives you the exact env that worked.
10
+ #
11
+ # Caveats:
12
+ # - Tarballs are huge (~15 GB compressed, ~35 GB raw)
13
+ # - Needs the droplet to still be alive (run BEFORE you destroy it)
14
+ # - Can't be uploaded to a public registry without redacting any
15
+ # embedded auth tokens; recommend you scp it to backup storage
16
+ #
17
+ # Usage:
18
+ # scripts/save_droplet_image.sh <droplet-ip> [out-dir]
19
+ #
20
+ # Default out-dir: ~/riprap-backups/
21
+ set -euo pipefail
22
+
23
+ if [ "$#" -lt 1 ]; then
24
+ echo "Usage: $0 <droplet-ip> [out-dir]" >&2
25
+ exit 64
26
+ fi
27
+
28
+ DROPLET_IP="$1"
29
+ OUT_DIR="${2:-$HOME/riprap-backups}"
30
+ SSH_USER="${SSH_USER:-root}"
31
+ SSH_KEY_FLAG=""
32
+ if [ -n "${SSH_KEY:-}" ]; then SSH_KEY_FLAG="-i $SSH_KEY"; fi
33
+ SSH="ssh $SSH_KEY_FLAG -o StrictHostKeyChecking=accept-new ${SSH_USER}@${DROPLET_IP}"
34
+ SCP="scp $SSH_KEY_FLAG -o StrictHostKeyChecking=accept-new"
35
+
36
+ mkdir -p "$OUT_DIR"
37
+ STAMP=$(date -u +%Y%m%d-%H%M%S)
38
+ TAR="$OUT_DIR/riprap-droplet-base-${STAMP}.tar"
39
+
40
+ echo "==> 1. Commit running terramind container as image"
41
+ $SSH 'docker commit terramind riprap-droplet-base:latest'
42
+
43
+ echo "==> 2. Save image to droplet-local tarball"
44
+ $SSH "docker save riprap-droplet-base:latest -o /workspace/riprap-droplet-base.tar"
45
+ SIZE=$($SSH 'stat -c %s /workspace/riprap-droplet-base.tar')
46
+ echo " droplet-local tarball: $SIZE bytes"
47
+
48
+ echo "==> 3. Compress (zstd preferred; gzip fallback)"
49
+ if $SSH 'command -v zstd > /dev/null'; then
50
+ $SSH 'zstd -3 --rm /workspace/riprap-droplet-base.tar'
51
+ REMOTE="/workspace/riprap-droplet-base.tar.zst"
52
+ else
53
+ $SSH 'gzip /workspace/riprap-droplet-base.tar'
54
+ REMOTE="/workspace/riprap-droplet-base.tar.gz"
55
+ fi
56
+ CSIZE=$($SSH "stat -c %s $REMOTE")
57
+ echo " compressed: $CSIZE bytes ($(awk "BEGIN { printf \"%.1f\", $CSIZE/$SIZE*100 }")% of raw)"
58
+
59
+ echo "==> 4. scp to local: $TAR$(basename $REMOTE | sed 's|riprap-droplet-base.tar||')"
60
+ $SCP "${SSH_USER}@${DROPLET_IP}:${REMOTE}" "${TAR}$(echo $REMOTE | sed 's|.*\.tar||')"
61
+
62
+ echo "==> 5. Cleanup droplet tarball"
63
+ $SSH "rm -f $REMOTE"
64
+
65
+ ls -lh "${TAR}"*
66
+ echo
67
+ echo "Restore on a fresh droplet:"
68
+ echo " scp ${TAR}* root@<new-ip>:/workspace/"
69
+ echo " ssh root@<new-ip> 'zstd -d /workspace/riprap-droplet-base.tar.zst -o /tmp/img.tar && docker load -i /tmp/img.tar'"
70
+ echo " Then docker run with the original device flags (see CLAUDE.md)."