{"id":26085288,"url":"https://github.com/gonzalo123/docker_secrets","last_synced_at":"2026-04-20T09:34:38.532Z","repository":{"id":277702695,"uuid":"933234769","full_name":"gonzalo123/docker_secrets","owner":"gonzalo123","description":"Hot-reload for Docker secrets in Docker Swarm","archived":false,"fork":false,"pushed_at":"2025-02-15T14:20:27.000Z","size":13,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-08T22:13:19.459Z","etag":null,"topics":["bash","docker","secrets","shell-script","swarm"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gonzalo123.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-02-15T13:33:18.000Z","updated_at":"2025-03-01T13:57:33.000Z","dependencies_parsed_at":"2025-02-16T20:45:40.486Z","dependency_job_id":null,"html_url":"https://github.com/gonzalo123/docker_secrets","commit_stats":null,"previous_names":["gonzalo123/docker_secrets"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/gonzalo123/docker_secrets","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gonzalo123%2Fdocker_secrets","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gonzalo123%2Fdocker_secrets/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gonzalo123%2Fdocker_secrets/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gonzalo123%2Fdocker_secrets/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gonzalo123","download_url":"https://codeload.github.com/gonzalo123/docker_secrets/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gonzalo123%2Fdocker_secrets/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32041613,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T00:18:06.643Z","status":"online","status_checked_at":"2026-04-20T02:00:06.527Z","response_time":94,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bash","docker","secrets","shell-script","swarm"],"created_at":"2025-03-09T05:58:27.752Z","updated_at":"2026-04-20T09:34:38.513Z","avatar_url":"https://github.com/gonzalo123.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Hot-reload for Docker secrets in Docker Swarm\n\nThe best way to store passwords in a Docker Swarm cluster, apart from proprietary solutions from cloud providers, is the\nuse of Docker secrets. Docker secrets are a way to securely store sensitive information, such as passwords, API keys,\nand authentication tokens, in Docker Swarm. Docker secrets are encrypted, and can only be accessed at runtime by the\nservices that need them. Docker mounts the secrets as files in the container's filesystem, allowing applications to read\nthem as if they were regular files. The only problem of Docker secrets is that they cannot be updated once created, so\nif we need to change a password, for example, we must create a new secret and update the services that use it. There are\ntechniques to do this, but we need to update our docker-compose files. Today we will see how to do it without modifying\nthe docker-compose files and, although some service restarts are necessary, with minimal downtime.\n\nWe will start with an example project. It is a Flask API that displays the secret, in this case, the password of a\ndatabase, on the screen. The idea is not to expose our password, of course. We only do this to be able to access our\nservice and see that the password is updated correctly. The API code is as follows:\n\n```python\nfrom flask import Flask\n\nfrom settings import DB_PASSWORD\n\napp = Flask(__name__)\n\n\n@app.get(\"/\")\ndef home():\n    return dict(SECRET=DB_PASSWORD)\n```\n\nOur service is deployed as follows:\n\n```yaml\nversion: '3.9'\n\nservices:\n  api:\n    image: api_secret:latest\n    command: gunicorn -w 1 app:app -b 0.0.0.0:5000 --timeout 180\n    secrets:\n      - db_password\n    ports:\n      - \"5000:5000\"\n\nsecrets:\n  db_password:\n    external: true\n```\n\nAs we can see, the `db_password` secret is mounted in the `api` service container. To deploy our service, we first\ncreate the secret and then deploy the service:\n\n```shell\necho \"old password\" | docker secret create db_password -\ndocker build -t api_secret .\n\ndocker stack deploy -c docker-compose.yml service1\n```\n\nNow we want to update the secret, but without touching the docker-compose and without redeploying. The idea is to create\na temporary secret with the new password, update the service to use the new secret, and then delete the old secret. That\nmeans that our service will have a secret or a temporary secret, but never both at the same time. If there's a temporary\nsecret, the service will use it. If there's no temporary secret, the service will use the regular secret. That's the\npython code to do that:\n\n```python\nfrom get_docker_secret import get_docker_secret\n\n\ndef get_secret(key, default=None):\n    return get_docker_secret(\n        name=f\"{key}.tmp\",\n        default=get_docker_secret(name=key, default=default)\n    )\n```\n\nIn broad terms, what we do is the following:\n\n```shell\n# Create the new secret\necho \"new password\" | docker secret create db_password.tmp -\n\n# Update the service to use the new secret\ndocker service update --secret-add db_password.tmp --secret-rm db_password service1_ap1\n\n# Remove the old secret that is no longer used\ndocker secret rm db_password\n\n# Create the secret again with the new password\necho \"new password\" | docker secret create db_password -\n\n# Update the service to use the new secret\ndocker service update --secret-add db_password --secret-rm db_password.tmp service1_ap1\n\n# Remove the temporary secret\ndocker secret rm db_password.tmp\n```\n\nAnd that's it. It is clear that every time we update a secret, the service restarts, so there will be some downtime. But\nit is minimal downtime.\n\nI have created a small bash script that automates what we have discussed.\n\n```shell\n#!/bin/bash\n\nif [ \"$#\" -ne 2 ]; then\n  echo \"Usage: $0 \u003cSECRET_NAME\u003e \u003cNEW_PASSWORD\u003e\"\n  exit 1\nfi\n\nSECRET_NAME=$1\nNEW_PASSWORD=$2\n\nTEMP_SECRET_NAME=\"$SECRET_NAME.tmp\"\n\nif ! docker secret ls --format \"{{.Name}}\" | grep -qw \"$SECRET_NAME\"; then\n  echo \"[ERROR] Secret '$SECRET_NAME' does not exist.\"\n  echo \"[INFO] Available secrets in the cluster:\"\n  docker secret ls --format \"table {{.ID}}\\t{{.Name}}\\t{{.CreatedAt}}\"\n  exit 1\nfi\n\necho \"[INFO] Creating new temporal secret '$TEMP_SECRET_NAME'...\"\necho \"$NEW_PASSWORD\" | docker secret create \"$TEMP_SECRET_NAME\" -\n\necho \"[INFO] Looking for services using secret: '$SECRET_NAME'...\"\nSERVICES=$(docker service ls --format \"{{.Name}}\" | while read -r service; do\n  if docker service inspect \"$service\" | jq -e \".[].Spec.TaskTemplate.ContainerSpec.Secrets | map(select(.SecretName == \\\"$SECRET_NAME\\\")) | length \u003e 0\" \u003e/dev/null; then\n    echo \"$service\"\n  fi\ndone)\n\necho \"[INFO] Updating services to use the new secret and remove the old one...\"\nfor service in $SERVICES; do\n  docker service update --secret-add \"$TEMP_SECRET_NAME\" --secret-rm \"$SECRET_NAME\" \"$service\"\ndone\n\necho \"[INFO] Removing the old secret...\"\ndocker secret rm \"$SECRET_NAME\"\necho \"$NEW_PASSWORD\" | docker secret create \"$SECRET_NAME\" -\n\necho \"[INFO] Renaming the new secret...\"\nfor service in $SERVICES; do\n  docker service update --secret-add \"$SECRET_NAME\" --secret-rm \"$TEMP_SECRET_NAME\" \"$service\"\ndone\n\necho \"[INFO] Removing the temporary secret...\"\ndocker secret rm \"$TEMP_SECRET_NAME\"\n\necho \"[INFO] Secret '$SECRET_NAME' updated successfully.\"\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgonzalo123%2Fdocker_secrets","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgonzalo123%2Fdocker_secrets","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgonzalo123%2Fdocker_secrets/lists"}