Skip to content

Instantly share code, notes, and snippets.

@dargmuesli
Forked from kwonghung-YIP/backup-docker-secrets.sh
Last active September 8, 2026 23:59
Show Gist options
  • Select an option

  • Save dargmuesli/3d5176e96b7174f9a5539c798155f9e7 to your computer and use it in GitHub Desktop.

Select an option

Save dargmuesli/3d5176e96b7174f9a5539c798155f9e7 to your computer and use it in GitHub Desktop.
Shell script for export all secrets defined in docker swarm
#!/bin/bash
# Dumps every swarm secret into a tar by mounting them all into a throwaway service.
# The tar holds plaintext secrets: encrypt it before moving it off this host.
set -euo pipefail
umask 077
service_name="backup-all-secrets"
out_dir="${1:-.}"
tar_file="backup_$(date +"%Y%m%d-%H%M%S").tar"
mapfile -t secret_list < <(docker secret ls --format '{{ .Name }}')
# Docker only creates /run/secrets once it mounts something there, so an empty list would break the tar.
(( ${#secret_list[@]} )) || { echo "no secrets to back up" >&2; exit 0; }
# Runs on every exit path so an interrupted run never leaves a container holding all secrets.
cleanup() { docker service rm "$service_name" >/dev/null 2>&1 || true; }
trap cleanup EXIT
# node.id is exact, unlike node.hostname, which matches whatever name the node used when it joined the swarm.
node_id=$(docker info --format '{{ .Swarm.NodeID }}')
args=(--name "$service_name" --constraint "node.id==$node_id" --restart-condition none)
for secret in "${secret_list[@]}"; do
args+=(--secret "$secret")
done
docker service create "${args[@]}" nginx >/dev/null
container_id=$(docker ps --filter "label=com.docker.swarm.service.name=$service_name" \
--filter status=running --format '{{ .ID }}' | head -n1)
[[ -n $container_id ]] || { echo "backup task never started" >&2; exit 1; }
mkdir -p "$out_dir"
docker exec -w /usr/local "$container_id" sh -c "tar -cf '$tar_file' -C /run/secrets ."
docker cp "$container_id:/usr/local/$tar_file" "$out_dir/"
# /run/secrets carries only name and value, so labels and driver settings are captured separately.
docker secret inspect "${secret_list[@]}" > "$out_dir/${tar_file%.tar}.metadata.json"
# docker cp preserves the container-side mode, so tighten it here rather than relying on umask.
chmod 600 "$out_dir/$tar_file" "$out_dir/${tar_file%.tar}.metadata.json"
echo "$out_dir/$tar_file"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment