This procedure collects third-party configuration files from your NVIDIA Minerva installation and packages them into a timestamped archive. The following configuration files are included:
-
Docker Swarm: Prometheus, syslog-ng, Vector, tsservice, OTel/DMT gateway, Nginx, and Fluentd
-
Kubernetes
MinervaInfraandMinervaAppcustom resources
All commands run in bash. Run them on a Linux host with bash, rsync, docker, and tar available for a Docker Swarm installation, or kubectl for a Kubernetes installation.
Run the entire procedure in a single shell session; later steps depend on environment variables exported by earlier ones. If your login shell is not bash (csh, tcsh, zsh, or dash, for example), run bash first and use it for the entire procedure.
Scope
This procedure checks for configuration files on disk; services do not need to be running. If no files are found for a given service, that step records skip: <reason> in probe.out and continues.
The archive may capture OS-package default files. For example, /etc/prometheus/prometheus.yml and /etc/syslog-ng/syslog-ng.conf exist on any Ubuntu host with those packages installed, even if those services were never part of your NVIDIA Minerva deployment. Before restoring, verify that the captured configs reflect your actual deployment.
For each service, only a specific set of file types is captured: Prometheus is limited to *.yml, *.yaml, and *.rules files under its config directory; Nginx and Fluentd are limited to *.conf files. Non-.conf includes and Prometheus rule files at absolute paths outside the resolved config directory are not captured.
Before You Begin: Determine Your Deployment Type
If you already know whether your installation is Docker Swarm or Kubernetes, skip to the relevant section. Otherwise, SSH to the NVIDIA Minerva host:
ssh <minerva-user>@<minerva-host>
If your login shell is not bash (csh, tcsh, zsh, dash, etc.), switch to bash and stay in it for the rest of the procedure. Then run the following probe to detect the deployment type:
if command -v docker >/dev/null 2>&1 && docker info 2>/dev/null | grep -q '^ *Swarm: active' && docker service ls --format '{{.Name}}' 2>/dev/null | grep -qE '^(backend_|infra_|tsservice_|ui_)'; then echo "Deployment: Docker Swarm -- follow the Docker Swarm section."; elif command -v kubectl >/dev/null 2>&1 && kubectl get crd minervainfras.minerva.minerva.nvidia.com >/dev/null 2>&1; then echo "Deployment: Kubernetes -- follow the Kubernetes section (context=$(kubectl config current-context))."; else echo "No active Minerva deployment detected from this shell. Confirm you're on the Minerva install host."; fi
Docker Swarm Deployment
The following table maps each service to its source and destination under ${BACKUP_ROOT}. Missing optional sources are recorded as skip: <reason> in ${BACKUP_ROOT}/probe.out and are not failures.
|
Item |
Source |
Destination |
|---|---|---|
|
Prometheus client |
Resolved Prometheus config directory from running |
|
|
syslog-ng |
Resolved syslog-ng config directory from running |
|
|
Vector |
|
|
|
Druid / tsservice overrides |
|
|
|
OTel / DMT gateway |
|
|
|
Nginx runtime edits |
|
|
|
Fluentd drop-ins |
|
|
-
SSH to the manager host:
ssh <minerva-user>@<swarm-manager-host> -
Prepare the backup directory.
MINERVA_ROOTdefaults to${HOME%/}/opt/minerva(the standard non-root installation root). If your installation uses a different root path, exportMINERVA_ROOT=/path/to/installbefore running this step.export MINERVA_ROOT="${MINERVA_ROOT:-${HOME%/}/opt/minerva}" export TS=$(date -u +%Y%m%dT%H%M%SZ) export BACKUP_ROOT="${HOME%/}/manual-backups/third-party-config/${TS}" RSYNC_EXCLUDES=(--exclude='*.key' --exclude='*.pem' --exclude='*.crt' --exclude='*.pfx' --exclude='*secret*' --exclude='*.log' --exclude='data/' --exclude='segments/') mkdir -p "${BACKUP_ROOT}"/{context,prometheus-client,syslog-ng,vector,tsservice,otel-dmt-gateway,nginx,fluentd} : > "${BACKUP_ROOT}/probe.out" echo "MINERVA_ROOT=${MINERVA_ROOT}" echo "BACKUP_ROOT=${BACKUP_ROOT}" -
Record the environment context:
{ echo "=== date ==="; date -u echo "=== hostname ==="; hostname -f echo "=== docker nodes ==="; docker node ls echo "=== docker stacks ==="; docker stack ls echo "=== docker services ==="; docker service ls } | tee "${BACKUP_ROOT}/context/swarm-context.txt" -
Copy the configuration files. Run each of the following blocks in sequence.
Prometheus client
ITEM=prometheus-client DEST="${BACKUP_ROOT}/${ITEM}" PROM_PID=$(pgrep -x prometheus | head -1) PROM_CFG= if [ -n "$PROM_PID" ]; then PROM_CFG=$(tr '\0' '\n' < /proc/$PROM_PID/cmdline | awk ' /^--config\.file=/ { sub(/^--config\.file=/, ""); print; exit } /^--config\.file$/ { f=1; next } f { print; exit }') fi [ -z "$PROM_CFG" ] && PROM_CFG=/etc/prometheus/prometheus.yml RUN_NOTE="${PROM_PID:+ (running pid=${PROM_PID})}"; RUN_NOTE="${RUN_NOTE:- (not running)}" if [ ! -f "$PROM_CFG" ]; then echo "skip: prometheus cfg not found at $PROM_CFG" | tee -a "${BACKUP_ROOT}/probe.out" else PROM_DIR=$(dirname "$PROM_CFG") rsync -a "${RSYNC_EXCLUDES[@]}" \ --include='*/' --include='*.yml' --include='*.yaml' --include='*.rules' --exclude='*' \ "${PROM_DIR}/" "${DEST}/" cp "$PROM_CFG" "${DEST}/" echo "copied: prometheus-client from ${PROM_DIR} (cfg=${PROM_CFG})${RUN_NOTE}" | tee -a "${BACKUP_ROOT}/probe.out" fisyslog-ng
ITEM=syslog-ng DEST="${BACKUP_ROOT}/${ITEM}" SLNG_PID=$(pgrep -x syslog-ng | head -1) SLNG_CFG= if [ -n "$SLNG_PID" ]; then SLNG_CFG=$(tr '\0' '\n' < /proc/$SLNG_PID/cmdline | awk ' /^--cfgfile=/ { sub(/^--cfgfile=/, ""); print; exit } /^-f$/ { f=1; next } f { print; exit }') fi [ -z "$SLNG_CFG" ] && SLNG_CFG=/etc/syslog-ng/syslog-ng.conf RUN_NOTE="${SLNG_PID:+ (running pid=${SLNG_PID})}"; RUN_NOTE="${RUN_NOTE:- (not running)}" if [ ! -f "$SLNG_CFG" ]; then echo "skip: syslog-ng cfg not found at $SLNG_CFG" | tee -a "${BACKUP_ROOT}/probe.out" else SLNG_DIR=$(dirname "$SLNG_CFG") rsync -a "${RSYNC_EXCLUDES[@]}" "${SLNG_DIR}/" "${DEST}/" echo "copied: syslog-ng from ${SLNG_DIR} (cfg=${SLNG_CFG})${RUN_NOTE}" | tee -a "${BACKUP_ROOT}/probe.out" fiVector
ITEM=vector DEST="${BACKUP_ROOT}/${ITEM}" SVC=$(docker service ls --format '{{.Replicas}}|{{.Name}}' 2>/dev/null | awk -F'|' '$1 !~ /(^0\/|\/0$)/ {print $2}' | grep -E '(^|_)vector$' | head -1) SVC_NOTE="${SVC:+ (service=${SVC})}"; SVC_NOTE="${SVC_NOTE:- (service not running)}" if [ ! -d "${MINERVA_ROOT}/var/vector" ]; then echo "skip: ${MINERVA_ROOT}/var/vector not present" | tee -a "${BACKUP_ROOT}/probe.out" elif [ -z "$(find "${MINERVA_ROOT}/var/vector" -type f -name '*.toml*' -print -quit)" ]; then echo "skip: no vector .toml files present" | tee -a "${BACKUP_ROOT}/probe.out" else rsync -a "${RSYNC_EXCLUDES[@]}" "${MINERVA_ROOT}/var/vector/" "${DEST}/" echo "copied: vector from ${MINERVA_ROOT}/var/vector${SVC_NOTE}" | tee -a "${BACKUP_ROOT}/probe.out" fiDruid/tsservice runtime overrides
ITEM=tsservice DEST="${BACKUP_ROOT}/${ITEM}" SVC=$(docker service ls --format '{{.Replicas}}|{{.Name}}' 2>/dev/null | awk -F'|' '$1 !~ /(^0\/|\/0$)/ {print $2}' | grep -E '(^|_)tsservice_' | head -1) SVC_NOTE="${SVC:+ (service=${SVC})}"; SVC_NOTE="${SVC_NOTE:- (service not running)}" if [ ! -d "${MINERVA_ROOT}/var/tsservice/config" ]; then echo "skip: ${MINERVA_ROOT}/var/tsservice/config not present" | tee -a "${BACKUP_ROOT}/probe.out" elif [ -z "$(find "${MINERVA_ROOT}/var/tsservice/config" -type f -print -quit)" ]; then echo "skip: tsservice config dir is empty" | tee -a "${BACKUP_ROOT}/probe.out" else rsync -a "${RSYNC_EXCLUDES[@]}" "${MINERVA_ROOT}/var/tsservice/config/" "${DEST}/" echo "copied: tsservice from ${MINERVA_ROOT}/var/tsservice/config${SVC_NOTE}" | tee -a "${BACKUP_ROOT}/probe.out" fiOTel/DMT gateway — prefers
~/minerva/dmt-gateway.yaml; falls back to${MINERVA_ROOT}/var/otel-collector/gateway.yaml, recordingfallback:when used.ITEM=otel-dmt-gateway DEST="${BACKUP_ROOT}/${ITEM}" SVC=$(docker service ls --format '{{.Replicas}}|{{.Name}}' 2>/dev/null | awk -F'|' '$1 !~ /(^0\/|\/0$)/ {print $2}' | grep -E 'otel-collector-gateway$' | head -1) SVC_NOTE="${SVC:+ (service=${SVC})}"; SVC_NOTE="${SVC_NOTE:- (service not running)}" if [ -f ~/minerva/dmt-gateway.yaml ]; then rsync -a "${RSYNC_EXCLUDES[@]}" ~/minerva/dmt-gateway.yaml "${DEST}/" echo "copied: otel-dmt-gateway canonical ~/minerva/dmt-gateway.yaml${SVC_NOTE}" | tee -a "${BACKUP_ROOT}/probe.out" elif [ -f "${MINERVA_ROOT}/var/otel-collector/gateway.yaml" ]; then rsync -a "${RSYNC_EXCLUDES[@]}" "${MINERVA_ROOT}/var/otel-collector/gateway.yaml" "${DEST}/gateway.yaml" echo "fallback: otel-dmt-gateway effective ${MINERVA_ROOT}/var/otel-collector/gateway.yaml (canonical missing)${SVC_NOTE}" | tee -a "${BACKUP_ROOT}/probe.out" else echo "skip: no dmt-gateway.yaml or effective gateway.yaml found" | tee -a "${BACKUP_ROOT}/probe.out" fiNginx runtime edits — covers the proxy (
nginx), data-plane LB (nginx-data), and on-premises variant (nginx-data-onprem). You do not need to know which services the installation runs.ITEM=nginx DEST="${BACKUP_ROOT}/${ITEM}" SVC=$(docker service ls --format '{{.Replicas}}|{{.Name}}' 2>/dev/null | awk -F'|' '$1 !~ /(^0\/|\/0$)/ {print $2}' | grep -E '(^|_)nginx(-[a-z0-9-]+)?$' | head -1) SVC_NOTE="${SVC:+ (service=${SVC})}"; SVC_NOTE="${SVC_NOTE:- (service not running)}" if [ ! -d "${MINERVA_ROOT}/var/nginx" ]; then echo "skip: ${MINERVA_ROOT}/var/nginx not present" | tee -a "${BACKUP_ROOT}/probe.out" elif [ -z "$(find "${MINERVA_ROOT}/var/nginx" -maxdepth 1 -type f -name '*.conf' -print -quit)" ]; then echo "skip: no nginx *.conf files present" | tee -a "${BACKUP_ROOT}/probe.out" else rsync -a "${RSYNC_EXCLUDES[@]}" --include='*.conf' --include='*/' --exclude='*' \ "${MINERVA_ROOT}/var/nginx/" "${DEST}/" echo "copied: nginx from ${MINERVA_ROOT}/var/nginx${SVC_NOTE}" | tee -a "${BACKUP_ROOT}/probe.out" fiFluentd drop-ins
ITEM=fluentd DEST="${BACKUP_ROOT}/${ITEM}" SVC=$(docker service ls --format '{{.Replicas}}|{{.Name}}' 2>/dev/null | awk -F'|' '$1 !~ /(^0\/|\/0$)/ {print $2}' | grep -E '(^|_)fluentd$' | head -1) SVC_NOTE="${SVC:+ (service=${SVC})}"; SVC_NOTE="${SVC_NOTE:- (service not running)}" if [ -z "$(find "${MINERVA_ROOT}/var/fluentd" -maxdepth 1 -type f -name '*.conf' -print -quit 2>/dev/null)" ]; then echo "skip: no fluentd drop-in *.conf files present" | tee -a "${BACKUP_ROOT}/probe.out" else rsync -a "${RSYNC_EXCLUDES[@]}" --include='*.conf' --exclude='*' \ "${MINERVA_ROOT}/var/fluentd/" "${DEST}/" echo "copied: fluentd from ${MINERVA_ROOT}/var/fluentd${SVC_NOTE}" | tee -a "${BACKUP_ROOT}/probe.out" fi -
Create the manifest:
find "${BACKUP_ROOT}" -type f -print | sort > "${BACKUP_ROOT}/manifest.files" if command -v sha256sum >/dev/null; then find "${BACKUP_ROOT}" -type f -not -name manifest.sha256 -print0 \ | xargs -0 sha256sum > "${BACKUP_ROOT}/manifest.sha256" else find "${BACKUP_ROOT}" -type f -not -name manifest.sha256 -print0 \ | xargs -0 shasum -a 256 > "${BACKUP_ROOT}/manifest.sha256" fi -
Package the archive:
cd "$(dirname "${BACKUP_ROOT}")" tar czf "third-party-config-${TS}.tar.gz" "$(basename "${BACKUP_ROOT}")" ls -lh "third-party-config-${TS}.tar.gz"Store the archive in your backup set.
-
Print the summary:
ARCHIVE="$(dirname ${BACKUP_ROOT})/third-party-config-${TS}.tar.gz" echo echo "================ Third-Party Config Backup Summary ================" echo "Timestamp: ${TS}" echo "Host: $(hostname -f)" echo "Backup dir: ${BACKUP_ROOT}" echo "Archive: ${ARCHIVE}" echo "Archive size: $(du -h "${ARCHIVE}" | cut -f1)" echo "File count: $(wc -l < "${BACKUP_ROOT}/manifest.files")" echo echo "Per-item result:" sed 's/^/ /' "${BACKUP_ROOT}/probe.out" echo "==================================================================="The summary lists every item as
copied,skip: <reason>, orfallback. Anything else indicates an error during the procedure.
Kubernetes Deployment
You need a host with kubectl configured against your cluster and read access to the minerva.minerva.nvidia.com CRDs.
-
Confirm your cluster context:
kubectl config current-context kubectl auth can-i get minervainfras.minerva.minerva.nvidia.com -A kubectl auth can-i get minervaapps.minerva.minerva.nvidia.com -A -
Create the backup directory:
export TS=$(date -u +%Y%m%dT%H%M%SZ) export BACKUP_ROOT="${HOME%/}/manual-backups/third-party-config-k8s/${TS}" mkdir -p "${BACKUP_ROOT}/custom-resources" "${BACKUP_ROOT}/context" : > "${BACKUP_ROOT}/probe.out" echo "BACKUP_ROOT=${BACKUP_ROOT}" -
Record the cluster context:
{ echo "=== date ==="; date -u echo "=== kubectl version ==="; kubectl version echo "=== cluster-info ==="; kubectl cluster-info echo "=== nodes ==="; kubectl get nodes -o wide echo "=== minervainfras ==="; kubectl get minervainfras -A echo "=== minervaapps ==="; kubectl get minervaapps -A } > "${BACKUP_ROOT}/context/k8s-context.txt" -
Export custom resources:
for KIND in minervainfras minervaapps; do if ! kubectl get crd "${KIND}.minerva.minerva.nvidia.com" >/dev/null 2>&1; then echo "skip: CRD ${KIND} not installed" | tee -a "${BACKUP_ROOT}/probe.out" continue fi if [ -z "$(kubectl get "${KIND}" -A -o name 2>/dev/null | head -1)" ]; then echo "skip: no ${KIND} resources found" | tee -a "${BACKUP_ROOT}/probe.out" continue fi kubectl get "${KIND}" -A -o yaml > "${BACKUP_ROOT}/custom-resources/${KIND}.yaml" echo "copied: ${KIND}" | tee -a "${BACKUP_ROOT}/probe.out" done -
Create the manifest and archive:
find "${BACKUP_ROOT}" -type f -print | sort > "${BACKUP_ROOT}/manifest.files" if command -v sha256sum >/dev/null; then find "${BACKUP_ROOT}" -type f -not -name manifest.sha256 -print0 \ | xargs -0 sha256sum > "${BACKUP_ROOT}/manifest.sha256" else find "${BACKUP_ROOT}" -type f -not -name manifest.sha256 -print0 \ | xargs -0 shasum -a 256 > "${BACKUP_ROOT}/manifest.sha256" fi cd "$(dirname "${BACKUP_ROOT}")" tar czf "third-party-config-k8s-${TS}.tar.gz" "$(basename "${BACKUP_ROOT}")" ls -lh "third-party-config-k8s-${TS}.tar.gz" -
Print the summary:
ARCHIVE="$(dirname ${BACKUP_ROOT})/third-party-config-k8s-${TS}.tar.gz" echo echo "============ Third-Party Config Backup (Kubernetes) Summary ============" echo "Timestamp: ${TS}" echo "Context: $(kubectl config current-context)" echo "Backup dir: ${BACKUP_ROOT}" echo "Archive: ${ARCHIVE}" echo "Archive size: $(du -h "${ARCHIVE}" | cut -f1)" echo "File count: $(wc -l < "${BACKUP_ROOT}/manifest.files")" echo echo "Per-item result:" sed 's/^/ /' "${BACKUP_ROOT}/probe.out" echo "========================================================================"
Verify the Archive
Run the following in the same shell session after completing the deployment steps. It resolves the archive path automatically, runs five checks, and prints OK or FAIL per check. A typical deployment archive is under 50 MB; if the archive is significantly larger, investigate before including it in your backup set.
if [ -z "${BACKUP_ROOT:-}" ] || [ -z "${TS:-}" ]; then
echo "FAIL: BACKUP_ROOT or TS is not set in this shell."
echo " Verify must run in the same shell that ran the prepare step."
elif [ ! -d "${BACKUP_ROOT}" ]; then
echo "FAIL: ${BACKUP_ROOT} does not exist."
else
ARCHIVE=$(ls "$(dirname "${BACKUP_ROOT}")"/*${TS}*.tar.gz 2>/dev/null | head -1)
if [ -z "${ARCHIVE}" ] || [ ! -f "${ARCHIVE}" ]; then
echo "FAIL: no archive matching TS=${TS} under $(dirname "${BACKUP_ROOT}")"
else
export ARCHIVE
echo "ARCHIVE=${ARCHIVE}"
ls -lh "${ARCHIVE}"
echo "--- probe.out ---"
cat "${BACKUP_ROOT}/probe.out"
echo "--- bulk data (must be empty) ---"
tar tzf "${ARCHIVE}" | grep -E '(/data/|/segments/|\.log$)' && echo FAIL || echo OK
echo "--- manifest integrity (every file must report OK) ---"
( cd "${BACKUP_ROOT}" && { sha256sum -c manifest.sha256 2>/dev/null || shasum -a 256 -c manifest.sha256; } ) | grep -v ': OK$' || echo all-OK
fi
fi
Full End-to-End Commands
The following section contains all Docker Swarm and Kubernetes commands concatenated for copy-paste in a single session.
Docker Swarm
# 1. Prepare
export MINERVA_ROOT="${MINERVA_ROOT:-${HOME%/}/opt/minerva}"
export TS=$(date -u +%Y%m%dT%H%M%SZ)
export BACKUP_ROOT="${HOME%/}/manual-backups/third-party-config/${TS}"
RSYNC_EXCLUDES=(--exclude='*.key' --exclude='*.pem' --exclude='*.crt' --exclude='*.pfx' --exclude='*secret*' --exclude='*.log' --exclude='data/' --exclude='segments/')
mkdir -p "${BACKUP_ROOT}"/{context,prometheus-client,syslog-ng,vector,tsservice,otel-dmt-gateway,nginx,fluentd}
: > "${BACKUP_ROOT}/probe.out"
# 2. Context
{
echo "=== date ==="; date -u
echo "=== hostname ==="; hostname -f
echo "=== docker nodes ==="; docker node ls
echo "=== docker stacks ==="; docker stack ls
echo "=== docker services ==="; docker service ls
} | tee "${BACKUP_ROOT}/context/swarm-context.txt"
# 3a. Prometheus client
DEST="${BACKUP_ROOT}/prometheus-client"
PROM_PID=$(pgrep -x prometheus | head -1)
PROM_CFG=
if [ -n "$PROM_PID" ]; then
PROM_CFG=$(tr '\0' '\n' < /proc/$PROM_PID/cmdline | awk '
/^--config\.file=/ { sub(/^--config\.file=/, ""); print; exit }
/^--config\.file$/ { f=1; next }
f { print; exit }')
fi
[ -z "$PROM_CFG" ] && PROM_CFG=/etc/prometheus/prometheus.yml
RUN_NOTE="${PROM_PID:+ (running pid=${PROM_PID})}"; RUN_NOTE="${RUN_NOTE:- (not running)}"
if [ ! -f "$PROM_CFG" ]; then
echo "skip: prometheus cfg not found at $PROM_CFG" | tee -a "${BACKUP_ROOT}/probe.out"
else
PROM_DIR=$(dirname "$PROM_CFG")
rsync -a "${RSYNC_EXCLUDES[@]}" \
--include='*/' --include='*.yml' --include='*.yaml' --include='*.rules' --exclude='*' \
"${PROM_DIR}/" "${DEST}/"
cp "$PROM_CFG" "${DEST}/"
echo "copied: prometheus-client from ${PROM_DIR} (cfg=${PROM_CFG})${RUN_NOTE}" | tee -a "${BACKUP_ROOT}/probe.out"
fi
# 3b. syslog-ng
DEST="${BACKUP_ROOT}/syslog-ng"
SLNG_PID=$(pgrep -x syslog-ng | head -1)
SLNG_CFG=
if [ -n "$SLNG_PID" ]; then
SLNG_CFG=$(tr '\0' '\n' < /proc/$SLNG_PID/cmdline | awk '
/^--cfgfile=/ { sub(/^--cfgfile=/, ""); print; exit }
/^-f$/ { f=1; next }
f { print; exit }')
fi
[ -z "$SLNG_CFG" ] && SLNG_CFG=/etc/syslog-ng/syslog-ng.conf
RUN_NOTE="${SLNG_PID:+ (running pid=${SLNG_PID})}"; RUN_NOTE="${RUN_NOTE:- (not running)}"
if [ ! -f "$SLNG_CFG" ]; then
echo "skip: syslog-ng cfg not found at $SLNG_CFG" | tee -a "${BACKUP_ROOT}/probe.out"
else
SLNG_DIR=$(dirname "$SLNG_CFG")
rsync -a "${RSYNC_EXCLUDES[@]}" "${SLNG_DIR}/" "${DEST}/"
echo "copied: syslog-ng from ${SLNG_DIR} (cfg=${SLNG_CFG})${RUN_NOTE}" | tee -a "${BACKUP_ROOT}/probe.out"
fi
# 3c. Vector
DEST="${BACKUP_ROOT}/vector"
SVC=$(docker service ls --format '{{.Replicas}}|{{.Name}}' 2>/dev/null | awk -F'|' '$1 !~ /(^0\/|\/0$)/ {print $2}' | grep -E '(^|_)vector$' | head -1)
SVC_NOTE="${SVC:+ (service=${SVC})}"; SVC_NOTE="${SVC_NOTE:- (service not running)}"
if [ ! -d "${MINERVA_ROOT}/var/vector" ]; then
echo "skip: ${MINERVA_ROOT}/var/vector not present" | tee -a "${BACKUP_ROOT}/probe.out"
elif [ -z "$(find "${MINERVA_ROOT}/var/vector" -type f -name '*.toml*' -print -quit)" ]; then
echo "skip: no vector .toml files present" | tee -a "${BACKUP_ROOT}/probe.out"
else
rsync -a "${RSYNC_EXCLUDES[@]}" "${MINERVA_ROOT}/var/vector/" "${DEST}/"
echo "copied: vector from ${MINERVA_ROOT}/var/vector${SVC_NOTE}" | tee -a "${BACKUP_ROOT}/probe.out"
fi
# 3d. tsservice
DEST="${BACKUP_ROOT}/tsservice"
SVC=$(docker service ls --format '{{.Replicas}}|{{.Name}}' 2>/dev/null | awk -F'|' '$1 !~ /(^0\/|\/0$)/ {print $2}' | grep -E '(^|_)tsservice_' | head -1)
SVC_NOTE="${SVC:+ (service=${SVC})}"; SVC_NOTE="${SVC_NOTE:- (service not running)}"
if [ ! -d "${MINERVA_ROOT}/var/tsservice/config" ]; then
echo "skip: ${MINERVA_ROOT}/var/tsservice/config not present" | tee -a "${BACKUP_ROOT}/probe.out"
elif [ -z "$(find "${MINERVA_ROOT}/var/tsservice/config" -type f -print -quit)" ]; then
echo "skip: tsservice config dir is empty" | tee -a "${BACKUP_ROOT}/probe.out"
else
rsync -a "${RSYNC_EXCLUDES[@]}" "${MINERVA_ROOT}/var/tsservice/config/" "${DEST}/"
echo "copied: tsservice from ${MINERVA_ROOT}/var/tsservice/config${SVC_NOTE}" | tee -a "${BACKUP_ROOT}/probe.out"
fi
# 3e. OTel / DMT gateway
DEST="${BACKUP_ROOT}/otel-dmt-gateway"
SVC=$(docker service ls --format '{{.Replicas}}|{{.Name}}' 2>/dev/null | awk -F'|' '$1 !~ /(^0\/|\/0$)/ {print $2}' | grep -E 'otel-collector-gateway$' | head -1)
SVC_NOTE="${SVC:+ (service=${SVC})}"; SVC_NOTE="${SVC_NOTE:- (service not running)}"
if [ -f ~/minerva/dmt-gateway.yaml ]; then
rsync -a "${RSYNC_EXCLUDES[@]}" ~/minerva/dmt-gateway.yaml "${DEST}/"
echo "copied: otel-dmt-gateway canonical ~/minerva/dmt-gateway.yaml${SVC_NOTE}" | tee -a "${BACKUP_ROOT}/probe.out"
elif [ -f "${MINERVA_ROOT}/var/otel-collector/gateway.yaml" ]; then
rsync -a "${RSYNC_EXCLUDES[@]}" "${MINERVA_ROOT}/var/otel-collector/gateway.yaml" "${DEST}/gateway.yaml"
echo "fallback: otel-dmt-gateway effective ${MINERVA_ROOT}/var/otel-collector/gateway.yaml (canonical missing)${SVC_NOTE}" | tee -a "${BACKUP_ROOT}/probe.out"
else
echo "skip: no dmt-gateway.yaml or effective gateway.yaml found" | tee -a "${BACKUP_ROOT}/probe.out"
fi
# 3f. Nginx
DEST="${BACKUP_ROOT}/nginx"
SVC=$(docker service ls --format '{{.Replicas}}|{{.Name}}' 2>/dev/null | awk -F'|' '$1 !~ /(^0\/|\/0$)/ {print $2}' | grep -E '(^|_)nginx(-[a-z0-9-]+)?$' | head -1)
SVC_NOTE="${SVC:+ (service=${SVC})}"; SVC_NOTE="${SVC_NOTE:- (service not running)}"
if [ ! -d "${MINERVA_ROOT}/var/nginx" ]; then
echo "skip: ${MINERVA_ROOT}/var/nginx not present" | tee -a "${BACKUP_ROOT}/probe.out"
elif [ -z "$(find "${MINERVA_ROOT}/var/nginx" -maxdepth 1 -type f -name '*.conf' -print -quit)" ]; then
echo "skip: no nginx *.conf files present" | tee -a "${BACKUP_ROOT}/probe.out"
else
rsync -a "${RSYNC_EXCLUDES[@]}" --include='*.conf' --include='*/' --exclude='*' \
"${MINERVA_ROOT}/var/nginx/" "${DEST}/"
echo "copied: nginx from ${MINERVA_ROOT}/var/nginx${SVC_NOTE}" | tee -a "${BACKUP_ROOT}/probe.out"
fi
# 3g. Fluentd
DEST="${BACKUP_ROOT}/fluentd"
SVC=$(docker service ls --format '{{.Replicas}}|{{.Name}}' 2>/dev/null | awk -F'|' '$1 !~ /(^0\/|\/0$)/ {print $2}' | grep -E '(^|_)fluentd$' | head -1)
SVC_NOTE="${SVC:+ (service=${SVC})}"; SVC_NOTE="${SVC_NOTE:- (service not running)}"
if [ -z "$(find "${MINERVA_ROOT}/var/fluentd" -maxdepth 1 -type f -name '*.conf' -print -quit 2>/dev/null)" ]; then
echo "skip: no fluentd drop-in *.conf files present" | tee -a "${BACKUP_ROOT}/probe.out"
else
rsync -a "${RSYNC_EXCLUDES[@]}" --include='*.conf' --exclude='*' \
"${MINERVA_ROOT}/var/fluentd/" "${DEST}/"
echo "copied: fluentd from ${MINERVA_ROOT}/var/fluentd${SVC_NOTE}" | tee -a "${BACKUP_ROOT}/probe.out"
fi
# 4. Manifest
find "${BACKUP_ROOT}" -type f -print | sort > "${BACKUP_ROOT}/manifest.files"
if command -v sha256sum >/dev/null; then
find "${BACKUP_ROOT}" -type f -not -name manifest.sha256 -print0 \
| xargs -0 sha256sum > "${BACKUP_ROOT}/manifest.sha256"
else
find "${BACKUP_ROOT}" -type f -not -name manifest.sha256 -print0 \
| xargs -0 shasum -a 256 > "${BACKUP_ROOT}/manifest.sha256"
fi
# 5. Archive
cd "$(dirname "${BACKUP_ROOT}")"
tar czf "third-party-config-${TS}.tar.gz" "$(basename "${BACKUP_ROOT}")"
ls -lh "third-party-config-${TS}.tar.gz"
# 6. Summary
ARCHIVE="$(dirname ${BACKUP_ROOT})/third-party-config-${TS}.tar.gz"
echo
echo "================ Third-Party Config Backup Summary ================"
echo "Timestamp: ${TS}"
echo "Host: $(hostname -f)"
echo "Backup dir: ${BACKUP_ROOT}"
echo "Archive: ${ARCHIVE}"
echo "Archive size: $(du -h "${ARCHIVE}" | cut -f1)"
echo "File count: $(wc -l < "${BACKUP_ROOT}/manifest.files")"
echo
echo "Per-item result:"
sed 's/^/ /' "${BACKUP_ROOT}/probe.out"
echo "==================================================================="
Kubernetes
# 1. Confirm context
kubectl config current-context
kubectl auth can-i get minervainfras.minerva.minerva.nvidia.com -A
kubectl auth can-i get minervaapps.minerva.minerva.nvidia.com -A
# 2. Prepare
export TS=$(date -u +%Y%m%dT%H%M%SZ)
export BACKUP_ROOT="${HOME%/}/manual-backups/third-party-config-k8s/${TS}"
mkdir -p "${BACKUP_ROOT}/custom-resources" "${BACKUP_ROOT}/context"
: > "${BACKUP_ROOT}/probe.out"
# 3. Context
{
echo "=== date ==="; date -u
echo "=== kubectl version ==="; kubectl version
echo "=== cluster-info ==="; kubectl cluster-info
echo "=== nodes ==="; kubectl get nodes -o wide
echo "=== minervainfras ==="; kubectl get minervainfras -A
echo "=== minervaapps ==="; kubectl get minervaapps -A
} > "${BACKUP_ROOT}/context/k8s-context.txt"
# 4. Export CRs
for KIND in minervainfras minervaapps; do
if ! kubectl get crd "${KIND}.minerva.minerva.nvidia.com" >/dev/null 2>&1; then
echo "skip: CRD ${KIND} not installed" | tee -a "${BACKUP_ROOT}/probe.out"
continue
fi
if [ -z "$(kubectl get "${KIND}" -A -o name 2>/dev/null | head -1)" ]; then
echo "skip: no ${KIND} resources found" | tee -a "${BACKUP_ROOT}/probe.out"
continue
fi
kubectl get "${KIND}" -A -o yaml > "${BACKUP_ROOT}/custom-resources/${KIND}.yaml"
echo "copied: ${KIND}" | tee -a "${BACKUP_ROOT}/probe.out"
done
# 5. Manifest + archive
find "${BACKUP_ROOT}" -type f -print | sort > "${BACKUP_ROOT}/manifest.files"
if command -v sha256sum >/dev/null; then
find "${BACKUP_ROOT}" -type f -not -name manifest.sha256 -print0 \
| xargs -0 sha256sum > "${BACKUP_ROOT}/manifest.sha256"
else
find "${BACKUP_ROOT}" -type f -not -name manifest.sha256 -print0 \
| xargs -0 shasum -a 256 > "${BACKUP_ROOT}/manifest.sha256"
fi
cd "$(dirname "${BACKUP_ROOT}")"
tar czf "third-party-config-k8s-${TS}.tar.gz" "$(basename "${BACKUP_ROOT}")"
ls -lh "third-party-config-k8s-${TS}.tar.gz"
# 6. Summary
ARCHIVE="$(dirname ${BACKUP_ROOT})/third-party-config-k8s-${TS}.tar.gz"
echo
echo "============ Third-Party Config Backup (Kubernetes) Summary ============"
echo "Timestamp: ${TS}"
echo "Context: $(kubectl config current-context)"
echo "Backup dir: ${BACKUP_ROOT}"
echo "Archive: ${ARCHIVE}"
echo "Archive size: $(du -h "${ARCHIVE}" | cut -f1)"
echo "File count: $(wc -l < "${BACKUP_ROOT}/manifest.files")"
echo
echo "Per-item result:"
sed 's/^/ /' "${BACKUP_ROOT}/probe.out"
echo "========================================================================"
Last updated: