{"id":13689746,"url":"https://github.com/shizunge/gantry","last_synced_at":"2026-04-16T03:02:02.507Z","repository":{"id":176558071,"uuid":"653814037","full_name":"shizunge/gantry","owner":"shizunge","description":"Docker service for automatically updating Docker swarm services whenever their image is updated.","archived":false,"fork":false,"pushed_at":"2026-04-13T01:14:24.000Z","size":774,"stargazers_count":88,"open_issues_count":2,"forks_count":4,"subscribers_count":4,"default_branch":"main","last_synced_at":"2026-04-13T03:08:47.180Z","etag":null,"topics":["automation","docker","docker-swarm","self-hosted","shell","updater"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shizunge.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-06-14T19:48:49.000Z","updated_at":"2026-04-07T18:10:27.000Z","dependencies_parsed_at":"2023-11-23T07:31:29.599Z","dependency_job_id":"e31fd31c-25dc-420d-96be-009e2ff3103f","html_url":"https://github.com/shizunge/gantry","commit_stats":null,"previous_names":["shizunge/gantry"],"tags_count":36,"template":false,"template_full_name":null,"purl":"pkg:github/shizunge/gantry","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shizunge%2Fgantry","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shizunge%2Fgantry/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shizunge%2Fgantry/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shizunge%2Fgantry/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shizunge","download_url":"https://codeload.github.com/shizunge/gantry/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shizunge%2Fgantry/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31869050,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T15:24:51.572Z","status":"online","status_checked_at":"2026-04-16T02:00:06.042Z","response_time":69,"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":["automation","docker","docker-swarm","self-hosted","shell","updater"],"created_at":"2024-08-02T16:00:24.046Z","updated_at":"2026-04-16T03:02:02.500Z","avatar_url":"https://github.com/shizunge.png","language":"Shell","funding_links":[],"categories":["Development with Docker","Developer Workflow","Community Tools","Shell"],"sub_categories":["CI/CD","Extra Functionality"],"readme":"# Gantry - Docker service updater\n\n[![Release](https://img.shields.io/github/release/shizunge/gantry.svg?label=Release)](https://github.com/shizunge/gantry/releases/latest)\n[![License](https://img.shields.io/badge/License-GPLv3-blue)](https://github.com/shizunge/gantry/blob/main/LICENSE)\n[![Image Size](https://img.shields.io/docker/image-size/shizunge/gantry/latest.svg?label=Image%20Size)](https://hub.docker.com/r/shizunge/gantry)\n[![Docker Pulls](https://img.shields.io/docker/pulls/shizunge/gantry.svg?label=Docker%20Pulls\u0026logo=Docker)](https://hub.docker.com/r/shizunge/gantry)\n[![Build](https://img.shields.io/github/actions/workflow/status/shizunge/gantry/on-push.yml?label=Build\u0026branch=main\u0026logo=GitHub)](https://github.com/shizunge/gantry/actions/workflows/on-push.yml)\n[![Coverage](https://img.shields.io/codecov/c/github/shizunge/gantry.svg?token=47MWUJOH4Q\u0026label=Coverage\u0026logo=Codecov)](https://codecov.io/gh/shizunge/gantry)\n[![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/shizunge/gantry?label=CodeFactor\u0026logo=CodeFactor)](https://www.codefactor.io/repository/github/shizunge/gantry)\n\n[*Gantry*](https://github.com/shizunge/gantry) automatically updates selected docker swarm services to newer images with the same tag. It is inspired by but [enhanced Shepherd](docs/migration.md).\n\n## Usage\n\n*Gantry* is released as a container [image](https://hub.docker.com/r/shizunge/gantry). You can create a docker service and run it on a swarm manager node.\n\n```\ndocker service create \\\n  --name gantry \\\n  --mode replicated-job \\\n  --constraint \"node.role==manager\" \\\n  --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \\\n  shizunge/gantry\n```\n\nThe [examples folder](examples/README.md) contains example docker compose files, and more methods to launch *Gantry*, like [at a specific time](examples/cronjob) and [via webhook](examples/webhook).\n\nYou can also run *Gantry* as a script directly on the host outside the container\n```\n./src/entrypoint.sh\n```\n\n*Gantry* is written to work with `busybox ash` (v1.35+) as well as `bash`.\n\n## Configurations\n\nYou can configure the most behaviors of *Gantry* via environment variables.\n\n### Common\n\n| Environment Variable  | Default |Description |\n|-----------------------|---------|------------|\n| GANTRY_LOG_FORMAT     | `text` | Log format, plain text or structured. Valid values are `text`, `json`. |\n| GANTRY_LOG_LEVEL      | `INFO` | Control how many logs generated by *Gantry*. Valid values are `NONE`, `ERROR`, `WARN`, `INFO`, `DEBUG`. |\n| GANTRY_NODE_NAME      |      | Add node name to logs. If not set, *Gantry* will use the host name of the Docker Swarm's manager, which is read from either the Docker daemon socket of current node or `DOCKER_HOST`. |\n| GANTRY_POST_RUN_CMD   |      | Command(s) to `eval` after each updating iteration. For [example](examples/prune-and-watchtower), you can use this to remove unused containers, networks and images and update standalone docker containers. |\n| GANTRY_PRE_RUN_CMD    |      | Command(s) to `eval` before each updating iteration. For [example](examples/prune-and-watchtower), you can use this to remove unused containers, networks and images and update standalone docker containers. If you changed *Gantry* configurations in the pre-run command(s), the new value would apply to the following updating. If the last pre-run command failed, *Gantry* would skip updating services. |\n| GANTRY_SLEEP_SECONDS  | 0    | Interval between two updates. Set it to 0 to run *Gantry* once and then exit. When this is a non-zero value, after an updating, *Gantry* will sleep until the next scheduled update. The actual sleep time is this value minus time spent on updating services. |\n| GANTRY_TRIGGER_PATH   |      | Path to watch for triggering the next update. Set this to an empty string to disable watching. This can be used with a [webhook](examples/webhook). In combination with `GANTRY_SLEEP_SECONDS`, updates will occur when either condition is met. |\n| TZ                    |      | Set timezone for time in logs. |\n\n*Gantry* bases on Docker command line, [environment variables](https://docs.docker.com/engine/reference/commandline/cli/#environment-variables) for Docker command line also works for *Gantry*.\n\n### To login to registries\n\n| Environment Variable  | Default | Description |\n|-----------------------|---------|-------------|\n| DOCKER_CONFIG                 | | The location of the [client configuration files](https://docs.docker.com/engine/reference/commandline/cli/#configuration-files) where authentication stores. It applys to all Docker commands, i.e. to all services. See [Authentication](docs/authentication.md). You can apply a different value to a particular service via [labels](#labels). |\n| GANTRY_REGISTRY_CONFIG        | | See [Authentication](docs/authentication.md). |\n| GANTRY_REGISTRY_CONFIG_FILE   | | See [Authentication](docs/authentication.md). |\n| GANTRY_REGISTRY_CONFIGS_FILE  | | See [Authentication](docs/authentication.md). |\n| GANTRY_REGISTRY_HOST          | | See [Authentication](docs/authentication.md). |\n| GANTRY_REGISTRY_HOST_FILE     | | See [Authentication](docs/authentication.md). |\n| GANTRY_REGISTRY_PASSWORD      | | See [Authentication](docs/authentication.md). |\n| GANTRY_REGISTRY_PASSWORD_FILE | | See [Authentication](docs/authentication.md). |\n| GANTRY_REGISTRY_USER          | | See [Authentication](docs/authentication.md). |\n| GANTRY_REGISTRY_USER_FILE     | | See [Authentication](docs/authentication.md). |\n\n### To select services\n\n| Environment Variable  | Default | Description |\n|-----------------------|---------|-------------|\n| GANTRY_SERVICES_EXCLUDED         | | A space separated list of services names that are excluded from updating. |\n| GANTRY_SERVICES_EXCLUDED_FILTERS | `label=gantry.services.excluded=true` | A space separated list of [filters](https://docs.docker.com/engine/reference/commandline/service_ls/#filter), e.g. `label=project=project-a`. Exclude services which match the given filters from updating. The default value allows you to add label `gantry.services.excluded=true` to services to exclude them from updating. Note that multiple filters will be logical **ANDED**. An empty string means no filters, as a result *Gantry* will not exclude any services. |\n| GANTRY_SERVICES_FILTERS          | | A space separated list of [filters](https://docs.docker.com/engine/reference/commandline/service_ls/#filter) that are accepted by `docker service ls --filter` to select services to update, e.g. `label=project=project-a`. Note that multiple filters will be logical **ANDED**. An empty string means no filters, as a result *Gantry* will update all services. Also see [How to filters multiple services by name](docs/faq.md#how-to-filters-multiple-services-by-name). |\n\n\u003e NOTE: *Gantry* reads labels on the services not on the containers. The labels need to go to the [deploy](https://docs.docker.com/reference/compose-file/deploy/#labels) section, if you are using docker compose files to setup your services.\n\n### To check if new images are available\n\n| Environment Variable  | Default | Description |\n|-----------------------|---------|-------------|\n| GANTRY_MANIFEST_CMD         | buildx | Valid values are `buildx`, `manifest`, and `none`.\u003cbr\u003eSet which command for manifest inspection.\u003cul\u003e\u003cli\u003e[`docker buildx imagetools inspect`](https://docs.docker.com/engine/reference/commandline/buildx_imagetools_inspect/)\u003c/li\u003e\u003cli\u003e[`docker manifest inspect`](https://docs.docker.com/engine/reference/commandline/manifest_inspect/)\u003c/li\u003e\u003c/ul\u003eSet to `none` to skip checking the manifest. As a result of skipping, `docker service update` always runs. Also see FAQ [which `GANTRY_MANIFEST_CMD` to use](docs/faq.md#which-gantry_manifest_cmd-to-use). You can apply a different value to a particular service via [labels](#labels). |\n| GANTRY_MANIFEST_NUM_WORKERS | 1      | The maximum number of `GANTRY_MANIFEST_CMD` that can run in parallel. |\n| GANTRY_MANIFEST_OPTIONS     |        | [Options](https://docs.docker.com/engine/reference/commandline/buildx_imagetools_inspect/#options) added to the `docker buildx imagetools inspect` or [options](https://docs.docker.com/engine/reference/commandline/manifest_inspect/#options) to `docker manifest inspect`, depending on `GANTRY_MANIFEST_CMD` value, for all services. You can apply a different value to a particular service via [labels](#labels). |\n\n### To add options to services update\n\n| Environment Variable  | Default | Description |\n|-----------------------|---------|-------------|\n| GANTRY_ROLLBACK_ON_FAILURE    | true  | Set to `true` to enable rollback when updating fails. Set to `false` to disable the rollback. You can apply a different value to a particular service via [labels](#labels). |\n| GANTRY_ROLLBACK_OPTIONS       |       | [Options](https://docs.docker.com/engine/reference/commandline/service_update/#options) added to the `docker service update --rollback` command for all services. You can apply a different value to a particular service via [labels](#labels). |\n| GANTRY_UPDATE_JOBS            | false | Set to `true` to update `replicated-job` or `global-job`. Set to `false` to disable updating jobs. *Gantry* adds additional options to `docker service update` when there is [no running tasks](docs/faq.md#how-to-update-services-with-no-running-tasks). You can apply a different value to a particular service via [labels](#labels). |\n| GANTRY_UPDATE_NUM_WORKERS     | 1     | The maximum number of updates that can run in parallel. |\n| GANTRY_UPDATE_OPTIONS         |       | [Options](https://docs.docker.com/engine/reference/commandline/service_update/#options) added to the `docker service update` command for all services. This also overrides any automatically added options. You can apply a different value to a particular service via [labels](#labels). |\n| GANTRY_UPDATE_TIMEOUT_SECONDS | 0     | Error out if updating of a single service takes longer than the given time. Set to `0` to disable timeout. You can apply a different value to a particular service via [labels](#labels). |\n\n### After updating\n\n| Environment Variable  | Default | Description |\n|-----------------------|---------|-------------|\n| GANTRY_CLEANUP_IMAGES           | true  | Set to `true` to clean up the updated images on all hosts. Set to `false` to disable the cleanup. Before cleaning up, *Gantry* will try to remove any *exited* and *dead* containers that are using the images. |\n| GANTRY_CLEANUP_IMAGES_OPTIONS   |       | [Options](https://docs.docker.com/engine/reference/commandline/service_create/#options) added to the `docker service create` command to create a global job for images removal. You can use this to add a label to the service or the containers. |\n| GANTRY_NOTIFICATION_APPRISE_URL |       | Enable notifications on service update with [*Apprise*](https://github.com/caronc/apprise-api). This must point to the notification endpoint (e.g. `http://apprise:8000/notify`) |\n| GANTRY_NOTIFICATION_CONDITION   | all   | Valid values are `all` and `on-change`. Specifies the conditions under which notifications are sent. Set to `all` to send notifications every run. Set to `on-change` to send notifications only when there are updates or errors. |\n| GANTRY_NOTIFICATION_TITLE       |       | Add an additional message to the notification title. |\n\n## Labels\n\nLabels can be added to services to modify the behavior of *Gantry* for particular services. When *Gantry* sees the following labels on a service, it will modify the Docker command line only for that service. The value on the label overrides the global environment variables.\n\n\u003e NOTE: *Gantry* reads labels on the services not on the containers. The labels need to go to the [deploy](https://docs.docker.com/reference/compose-file/deploy/#labels) section, if you are using docker compose files to setup your services.\n\n| Label  | Description |\n|--------|-------------|\n| `gantry.auth.config=\u003cconfiguration\u003e`     | Override [`DOCKER_CONFIG`](https://docs.docker.com/engine/reference/commandline/cli/#environment-variables). See [Authentication](docs/authentication.md). |\n| `gantry.services.excluded=true`          | Exclude the services from updating if you are using the default [`GANTRY_SERVICES_EXCLUDED_FILTERS`](#to-select-services). |\n| `gantry.manifest.cmd=\u003ccommand\u003e`          | Override [`GANTRY_MANIFEST_CMD`](#to-check-if-new-images-are-available). |\n| `gantry.manifest.options=\u003cstring\u003e `      | Override [`GANTRY_MANIFEST_OPTIONS`](#to-check-if-new-images-are-available). |\n| `gantry.rollback.on_failure=\u003cboolean\u003e`   | Override [`GANTRY_ROLLBACK_ON_FAILURE`](#to-add-options-to-services-update). |\n| `gantry.rollback.options=\u003cstring\u003e`       | Override [`GANTRY_ROLLBACK_OPTIONS`](#to-add-options-to-services-update). |\n| `gantry.update.jobs=\u003cboolean\u003e`           | Override [`GANTRY_UPDATE_JOBS`](#to-add-options-to-services-update). |\n| `gantry.update.options=\u003cstring\u003e`         | Override [`GANTRY_UPDATE_OPTIONS`](#to-add-options-to-services-update). |\n| `gantry.update.timeout_seconds=\u003cnumber\u003e` | Override [`GANTRY_UPDATE_TIMEOUT_SECONDS`](#to-add-options-to-services-update). |\n\n## FAQ\n\n[Authentication](docs/authentication.md)\n\n[FAQ](docs/faq.md)\n\n[Migrate from *Shepherd*](docs/migration.md)\n\n## Development\n\n*Gantry* is written to work with `busybox ash` (v1.35+), thus it could run easily in an alpine-based container without additional packages installed. One exception is that the notification feature requires `curl`. *Gantry* is also tested in `bash`.\n\n[shellcheck](https://github.com/koalaman/shellcheck) will run on push to enforce the best practices of writing shell scripts. Some checks are disabled thanks to `busybox ash` supports more features than POSIX `sh`. You can find the list of disabled checks in [.shellcheckrc](.shellcheckrc).\n\nTo run `shellcheck` locally:\n```\nshellcheck src/*.sh tests/*.sh\n```\n\nThe [tests](./tests/README.md) [folder](./tests) contains end-to-end tests, which cover the majority of the configuration options.\n\n## Contacts\n\nIf you have any problems or questions, please contact me through a [GitHub issue](https://github.com/shizunge/gantry/issues).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshizunge%2Fgantry","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshizunge%2Fgantry","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshizunge%2Fgantry/lists"}